diff options
20 files changed, 329 insertions, 22 deletions
diff --git a/platform/darwin/docs/guides/For Style Authors.md.ejs b/platform/darwin/docs/guides/For Style Authors.md.ejs index ead3b81ce6..cf0f79f419 100644 --- a/platform/darwin/docs/guides/For Style Authors.md.ejs +++ b/platform/darwin/docs/guides/For Style Authors.md.ejs @@ -403,6 +403,7 @@ In style specification | Method, function, or predicate type | Format string syn `zoom` | `NSExpression.zoomLevelVariableExpression` | `$zoomLevel` `heatmap-density` | `NSExpression.heatmapDensityVariableExpression` | `$heatmapDensity` `line-progress` | `NSExpression.lineProgressVariableExpression` | `$lineProgress` +`format` | `+[NSExpression mgl_expressionForAttributedExpressions:]` or `mgl_attributed:` | `mgl_attributed({x, y, z})` For operators that have no corresponding `NSExpression` symbol, use the `MGL_FUNCTION()` format string syntax. diff --git a/platform/darwin/docs/guides/Predicates and Expressions.md b/platform/darwin/docs/guides/Predicates and Expressions.md index 0bb26b3bfd..5f5d9a22a8 100644 --- a/platform/darwin/docs/guides/Predicates and Expressions.md +++ b/platform/darwin/docs/guides/Predicates and Expressions.md @@ -534,6 +534,22 @@ This function corresponds to the [`coalesce`](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-coalesce) operator in the Mapbox Style Specification. +### `mgl_attributed:` + +<dl> +<dt>Selector:</dt> +<dd><code>mgl_attributed:</code></dd> +<dt>Format string syntax:</dt> +<dd><code>mgl_attributed({x, y, z})</code></dd> +</dl> + +Concatenates and returns the array of `MGLAttributedExpression` objects, for use +with the `MGLSymbolStyleLayer.text` property. + +This function corresponds to the +[`format`](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-types-format) +operator in the Mapbox Style Specification. + ### `MGL_LET` <dl> diff --git a/platform/darwin/scripts/generate-style-code.js b/platform/darwin/scripts/generate-style-code.js index 704b0a29f4..a8bdec7865 100755 --- a/platform/darwin/scripts/generate-style-code.js +++ b/platform/darwin/scripts/generate-style-code.js @@ -107,7 +107,7 @@ global.objCTestValue = function (property, layerType, arraysAsStructs, indent) { // converts Formatted back to string. return layerType === 'string' ? `@"'${_.startCase(propertyName)}'"` : - `@"MGL_FUNCTION('format', '${_.startCase(propertyName)}', %@)", @{}`; + `@"${_.startCase(propertyName)}"`; case 'string': return `@"'${_.startCase(propertyName)}'"`; case 'enum': diff --git a/platform/darwin/src/MGLAttributedExpression.h b/platform/darwin/src/MGLAttributedExpression.h new file mode 100644 index 0000000000..aa5d51c66e --- /dev/null +++ b/platform/darwin/src/MGLAttributedExpression.h @@ -0,0 +1,44 @@ +#import "MGLFoundation.h" + +NS_ASSUME_NONNULL_BEGIN + +typedef NSString * MGLAttributedExpressionKey NS_EXTENSIBLE_STRING_ENUM; + +FOUNDATION_EXTERN MGL_EXPORT MGLAttributedExpressionKey const MGLFontNamesAttribute; +FOUNDATION_EXTERN MGL_EXPORT MGLAttributedExpressionKey const MGLFontSizeAttribute; + +/** + An `MGLAttributedExpression` object associates text formatting attibutes (such as font size or + font names) to an `NSExpression`. + */ +MGL_EXPORT +@interface MGLAttributedExpression : NSObject + +/** + The expression content of the receiver as `NSExpression`. + */ +@property (strong, nonatomic) NSExpression *expression; + +/** + The formatting attributes. + */ +@property (strong, nonatomic, readonly) NSDictionary<MGLAttributedExpressionKey, id> *attributes; + +/** + Returns an `MGLAttributedExpression` object initialized with an expression and no attribute information. + */ +- (instancetype)initWithExpression:(NSExpression *)expression; + +/** + Returns an `MGLAttributedExpression` object initialized with an expression and text format attributes. + */ +- (instancetype)initWithExpression:(NSExpression *)expression attributes:(nullable NSDictionary <MGLAttributedExpressionKey, id> *)attrs; + +/** + Creates an `MGLAttributedExpression` object initialized with an expression and the format attributes for font names and font size. + */ ++ (instancetype)attributedExpression:(NSExpression *)expression fontNames:(nullable NSArray<NSString*> *)fontNames fontSize:(nullable NSNumber *)fontSize; + +@end + +NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/MGLAttributedExpression.m b/platform/darwin/src/MGLAttributedExpression.m new file mode 100644 index 0000000000..715f74e42f --- /dev/null +++ b/platform/darwin/src/MGLAttributedExpression.m @@ -0,0 +1,59 @@ +#import "MGLAttributedExpression.h" +#import "MGLLoggingConfiguration_Private.h" + +const MGLAttributedExpressionKey MGLFontNamesAttribute = @"text-font"; +const MGLAttributedExpressionKey MGLFontSizeAttribute = @"font-scale"; + +@implementation MGLAttributedExpression + +- (instancetype)initWithExpression:(NSExpression *)expression { + self = [self initWithExpression:expression attributes:nil]; + return self; +} + ++ (instancetype)attributedExpression:(NSExpression *)expression fontNames:(nullable NSArray<NSString *> *)fontNames fontSize:(nullable NSNumber *)fontSize { + MGLAttributedExpression *attributedExpression; + + NSMutableDictionary *attrs = [NSMutableDictionary dictionary]; + + if (fontNames && fontNames.count > 0) { + attrs[MGLFontNamesAttribute] = fontNames; + } + + if (fontSize) { + attrs[MGLFontSizeAttribute] = fontSize; + } + + attributedExpression = [[self alloc] initWithExpression:expression attributes:attrs]; + return attributedExpression; +} + +- (instancetype)initWithExpression:(NSExpression *)expression attributes:(NSDictionary<MGLAttributedExpressionKey,id> *)attrs { + if (self = [super init]) + { + MGLLogInfo(@"Starting %@ initialization.", NSStringFromClass([self class])); + _expression = expression; + _attributes = attrs; + + MGLLogInfo(@"Finalizing %@ initialization.", NSStringFromClass([self class])); + } + return self; +} + +- (BOOL)isEqual:(id)object { + BOOL result = NO; + + if ([object isKindOfClass:[self class]]) { + MGLAttributedExpression *otherObject = object; + result = [self.expression isEqual:otherObject.expression] && + [_attributes isEqual:otherObject.attributes]; + } + + return result; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"MGLAttributedExpression<Expression: %@ Attributes: %@>", self.expression, self.attributes]; +} + +@end diff --git a/platform/darwin/src/NSExpression+MGLAdditions.h b/platform/darwin/src/NSExpression+MGLAdditions.h index 1e6fd6fc46..a19ec1af2e 100644 --- a/platform/darwin/src/NSExpression+MGLAdditions.h +++ b/platform/darwin/src/NSExpression+MGLAdditions.h @@ -7,6 +7,8 @@ #import "MGLTypes.h" +@class MGLAttributedExpression; + NS_ASSUME_NONNULL_BEGIN typedef NSString *MGLExpressionInterpolationMode NS_TYPED_ENUM; @@ -150,6 +152,14 @@ FOUNDATION_EXTERN MGL_EXPORT const MGLExpressionInterpolationMode MGLExpressionI */ + (instancetype)mgl_expressionForMatchingExpression:(nonnull NSExpression *)inputExpression inDictionary:(nonnull NSDictionary<NSExpression *, NSExpression *> *)matchedExpressions defaultExpression:(nonnull NSExpression *)defaultExpression NS_SWIFT_NAME(init(forMGLMatchingKey:in:default:)); +/** + Returns an attributed function expression specifying an `MGLAttributedExpression` constant + expression array. + + @param attributedExpressions The `MGLAttributedExpression` constant expression array. + */ ++ (instancetype)mgl_expressionForAttributedExpressions:(nonnull NSArray<NSExpression *> *)attributedExpressions NS_SWIFT_NAME(init(forAttributedExpressions:)); + #pragma mark Concatenating String Expressions /** diff --git a/platform/darwin/src/NSExpression+MGLAdditions.mm b/platform/darwin/src/NSExpression+MGLAdditions.mm index 6aaba4dd90..c4e2908888 100644 --- a/platform/darwin/src/NSExpression+MGLAdditions.mm +++ b/platform/darwin/src/NSExpression+MGLAdditions.mm @@ -11,6 +11,7 @@ #import "NSPredicate+MGLAdditions.h" #import "NSValue+MGLStyleAttributeAdditions.h" #import "MGLVectorTileSource_Private.h" +#import "MGLAttributedExpression.h" #import <objc/runtime.h> @@ -75,6 +76,7 @@ const MGLExpressionInterpolationMode MGLExpressionInterpolationModeCubicBezier = INSTALL_METHOD(mgl_atan:); INSTALL_METHOD(mgl_tan:); INSTALL_METHOD(mgl_log2:); + INSTALL_METHOD(mgl_attributed:); // Install functions that resemble control structures, taking arbitrary // numbers of arguments. Vararg aftermarket functions need to be declared @@ -96,6 +98,12 @@ const MGLExpressionInterpolationMode MGLExpressionInterpolationModeCubicBezier = return [components componentsJoinedByString:@""]; } +- (NSString *)mgl_attributed:(NSArray<MGLAttributedExpression *> *)attributedExpressions { + [NSException raise:NSInvalidArgumentException + format:@"Text format expressions lack underlying Objective-C implementations."]; + return nil; +} + /** Rounds the given number to the nearest integer. If the number is halfway between two integers, this method rounds it away from zero. @@ -229,7 +237,6 @@ const MGLExpressionInterpolationMode MGLExpressionInterpolationModeCubicBezier = return nil; } - /** A placeholder for a catch-all method that evaluates an arbitrary number of arguments as an expression according to the Mapbox Style Specification’s @@ -591,6 +598,10 @@ const MGLExpressionInterpolationMode MGLExpressionInterpolationModeCubicBezier = arguments:optionsArray]; } ++ (instancetype)mgl_expressionForAttributedExpressions:(nonnull NSArray<NSExpression *> *)attributedExpressions { + return [NSExpression expressionForFunction:@"mgl_attributed:" arguments:attributedExpressions]; +} + - (instancetype)mgl_expressionByAppendingExpression:(nonnull NSExpression *)expression { NSExpression *subexpression = [NSExpression expressionForAggregate:@[self, expression]]; return [NSExpression expressionForFunction:@"mgl_join:" arguments:@[subexpression]]; @@ -860,6 +871,22 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) { return [NSExpression expressionForFunction:@"MGL_MATCH" arguments:optionsArray]; + } else if ([op isEqualToString:@"format"]) { + NSMutableArray *attributedExpressions = [NSMutableArray array]; + + for (NSUInteger index = 0; index < argumentObjects.count; index+=2) { + NSExpression *expression = [NSExpression expressionWithMGLJSONObject:argumentObjects[index]]; + NSMutableDictionary *attrs = [NSMutableDictionary dictionary]; + if ((index + 1) < argumentObjects.count) { + attrs = argumentObjects[index + 1]; + } + + MGLAttributedExpression *attributedExpression = [[MGLAttributedExpression alloc] initWithExpression:expression attributes:attrs]; + + [attributedExpressions addObject:[NSExpression expressionForConstantValue:attributedExpression]]; + } + return [NSExpression expressionForFunction:@"mgl_attributed:" arguments:attributedExpressions]; + } else if ([op isEqualToString:@"coalesce"]) { NSMutableArray *expressions = [NSMutableArray array]; for (id operand in argumentObjects) { @@ -971,6 +998,23 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) { return @[@"literal", @[@(mglValue[0]), @(mglValue[1]), @(mglValue[2]), @(mglValue[3])]]; } } + if ([constantValue isKindOfClass:[MGLAttributedExpression class]]) { + MGLAttributedExpression *attributedExpression = (MGLAttributedExpression *)constantValue; + id jsonObject = attributedExpression.expression.mgl_jsonExpressionObject; + NSMutableArray *attributes = [NSMutableArray array]; + if ([jsonObject isKindOfClass:[NSArray class]]) { + [attributes addObjectsFromArray:jsonObject]; + } else { + [attributes addObject:jsonObject]; + } + if (attributedExpression.attributes) { + [attributes addObject:attributedExpression.attributes]; + } else { + [attributes addObject:@{}]; + } + + return attributes; + } return self.constantValue; } @@ -1115,6 +1159,9 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) { } [NSException raise:NSInvalidArgumentException format:@"Casting expression to %@ not yet implemented.", type]; + } else if ([function isEqualToString:@"mgl_attributed:"]) { + return [self mgl_jsonFormatExpressionObject]; + } else if ([function isEqualToString:@"MGL_FUNCTION"] || [function isEqualToString:@"MGL_FUNCTION:"]) { NSExpression *firstOp = self.arguments.firstObject; @@ -1363,6 +1410,25 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) { return expressionObject; } +- (id)mgl_jsonFormatExpressionObject { + NSArray<NSExpression *> *attributedExpressions; + NSExpression *formatArray = self.arguments.firstObject; + + if ([formatArray respondsToSelector:@selector(constantValue)] && [formatArray.constantValue isKindOfClass:[NSArray class]]) { + attributedExpressions = (NSArray *)formatArray.constantValue; + } else { + attributedExpressions = self.arguments; + } + + NSMutableArray *expressionObject = [NSMutableArray arrayWithObjects:@"format", nil]; + + for (NSUInteger index = 0; index < attributedExpressions.count; index++) { + [expressionObject addObjectsFromArray:attributedExpressions[index].mgl_jsonExpressionObject]; + } + + return expressionObject; +} + #pragma mark Localization /** diff --git a/platform/darwin/test/MGLExpressionTests.mm b/platform/darwin/test/MGLExpressionTests.mm index bcc30e49fd..8b8a79f184 100644 --- a/platform/darwin/test/MGLExpressionTests.mm +++ b/platform/darwin/test/MGLExpressionTests.mm @@ -12,6 +12,7 @@ #else #import "NSColor+MGLAdditions.h" #endif +#import "MGLAttributedExpression.h" #define MGLAssertEqualValues(actual, expected, ...) \ XCTAssertTrue(actual.is<__typeof__(expected)>()); \ @@ -990,6 +991,87 @@ using namespace std::string_literals; } } +- (void)testFormatExpressionObject { + { + MGLAttributedExpression *attribute1 = [MGLAttributedExpression attributedExpression:[NSExpression expressionForConstantValue:@"foo"] + fontNames:nil + fontSize:@(1.2)]; + MGLAttributedExpression *attribute2 = [MGLAttributedExpression attributedExpression:[NSExpression expressionForConstantValue:@"biz"] + fontNames:nil + fontSize:@(1.0)]; + MGLAttributedExpression *attribute3 = [MGLAttributedExpression attributedExpression:[NSExpression expressionForConstantValue:@"bar"] + fontNames:nil + fontSize:@(0.8)]; + MGLAttributedExpression *attribute4 = [MGLAttributedExpression attributedExpression:[NSExpression expressionForConstantValue:@"\r"] + fontNames:@[] + fontSize:nil]; + NSExpression *expression = [NSExpression expressionWithFormat:@"mgl_attributed:(%@, %@, %@, %@)", + MGLConstantExpression(attribute1), + MGLConstantExpression(attribute4), + MGLConstantExpression(attribute2), + MGLConstantExpression(attribute3)]; + NSArray *jsonExpression = @[@"format", @"foo", @{@"font-scale": @1.2}, @"\r", @{}, @"biz", @{@"font-scale": @1.0}, @"bar", @{@"font-scale": @0.8}]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression); + } + { + MGLAttributedExpression *attribute1 = [MGLAttributedExpression attributedExpression:[NSExpression expressionForConstantValue:@"foo"] + fontNames:nil + fontSize:@(1.2)]; + MGLAttributedExpression *attribute2 = [MGLAttributedExpression attributedExpression:[NSExpression expressionForConstantValue:@"biz"] + fontNames:nil + fontSize:@(1.0)]; + MGLAttributedExpression *attribute3 = [MGLAttributedExpression attributedExpression:[NSExpression expressionForConstantValue:@"bar"] + fontNames:nil + fontSize:@(0.8)]; + MGLAttributedExpression *attribute4 = [MGLAttributedExpression attributedExpression:[NSExpression expressionForConstantValue:@"\n"] + fontNames:@[] + fontSize:nil]; + NSExpression *expression = [NSExpression expressionWithFormat:@"mgl_attributed:(%@, %@, %@, %@)", + MGLConstantExpression(attribute1), + MGLConstantExpression(attribute4), + MGLConstantExpression(attribute2), + MGLConstantExpression(attribute3)]; + NSArray *jsonExpression = @[@"format", @"foo", @{@"font-scale": @1.2}, @"\n", @{}, @"biz", @{@"font-scale": @1.0}, @"bar", @{@"font-scale": @0.8}]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression); + } + { + MGLAttributedExpression *attribute1 = [MGLAttributedExpression attributedExpression:[NSExpression expressionForConstantValue:@"foo"] + fontNames:nil + fontSize:@(1.2)]; + NSExpression *expression = [NSExpression expressionWithFormat:@"mgl_attributed:(%@)", MGLConstantExpression(attribute1)]; + + NSExpression *compatibilityExpression = [NSExpression expressionForFunction:@"mgl_attributed:" arguments:@[MGLConstantExpression(attribute1)]]; + NSArray *jsonExpression = @[@"format", @"foo", @{@"font-scale": @1.2}]; + XCTAssertEqualObjects(compatibilityExpression.mgl_jsonExpressionObject, expression.mgl_jsonExpressionObject); + XCTAssertEqualObjects(compatibilityExpression, expression); + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression); + } + { + MGLAttributedExpression *attribute1 = [MGLAttributedExpression attributedExpression:[NSExpression expressionForConstantValue:@"foo"] + fontNames:nil + fontSize:@(1.2)]; + MGLAttributedExpression *attribute2 = [MGLAttributedExpression attributedExpression:[NSExpression expressionForConstantValue:@"biz"] + fontNames:nil + fontSize:@(1.0)]; + MGLAttributedExpression *attribute3 = [MGLAttributedExpression attributedExpression:[NSExpression expressionForConstantValue:@"bar"] + fontNames:nil + fontSize:@(0.8)]; + MGLAttributedExpression *attribute4 = [MGLAttributedExpression attributedExpression:[NSExpression expressionForConstantValue:@"\n"] + fontNames:@[] + fontSize:nil]; + NSExpression *expression = [NSExpression mgl_expressionForAttributedExpressions:@[MGLConstantExpression(attribute1), + MGLConstantExpression(attribute4), + MGLConstantExpression(attribute2), + MGLConstantExpression(attribute3)]]; + NSArray *jsonExpression = @[@"format", @"foo", @{@"font-scale": @1.2}, @"\n", @{}, @"biz", @{@"font-scale": @1.0}, @"bar", @{@"font-scale": @0.8}]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression); + } +} + - (void)testGenericExpressionObject { { NSExpression *expression = [NSExpression expressionWithFormat:@"MGL_FUNCTION('random', 1, 2, 3, 4, 5)"]; @@ -1002,23 +1084,6 @@ using namespace std::string_literals; XCTAssertThrowsSpecificNamed([expression expressionValueWithObject:nil context:nil], NSException, NSInvalidArgumentException); } { - NSExpression *expression = [NSExpression expressionWithFormat:@"MGL_FUNCTION('format', 'foo', %@, '\r', %@, 'biz', %@, 'bar', %@)", @{@"font-scale": @1.2}, @{}, @{@"font-scale": @1.0}, @{@"font-scale": @0.8}]; - NSArray *jsonExpression = @[@"format", @"foo", @{@"font-scale": @1.2}, @"\r", @{}, @"biz", @{@"font-scale": @1.0}, @"bar", @{@"font-scale": @0.8}]; - - XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); - - NSExpression *encodedExpression = [NSExpression expressionWithMGLJSONObject:jsonExpression]; - - // Expressions encoded from a json create a different constant abstract type - // thus even tho knowing that the constant values are the same, the base - // class is not. This compares the resulting array which is type agnostic encoding. - for (NSUInteger index = 0; index < jsonExpression.count; index++) { - NSExpression *left = encodedExpression.mgl_jsonExpressionObject[index]; - NSExpression *right = expression.mgl_jsonExpressionObject[index]; - XCTAssertEqualObjects(left.mgl_jsonExpressionObject, right.mgl_jsonExpressionObject); - } - } - { NSArray *arguments = @[ MGLConstantExpression(@"one"), MGLConstantExpression(@1), [NSExpression expressionForVariable:@"one"], diff --git a/platform/darwin/test/MGLStyleLayerTests.mm.ejs b/platform/darwin/test/MGLStyleLayerTests.mm.ejs index ba878b8bbe..4a38070007 100644 --- a/platform/darwin/test/MGLStyleLayerTests.mm.ejs +++ b/platform/darwin/test/MGLStyleLayerTests.mm.ejs @@ -8,6 +8,9 @@ #import "MGLStyleLayerTests.h" #import "../../darwin/src/NSDate+MGLAdditions.h" +<% if (type === 'symbol') { -%> +#include "../../darwin/src/MGLAttributedExpression.h" +<% } -%> #import "MGLStyleLayer_Private.h" @@ -80,7 +83,13 @@ XCTAssertEqualObjects(layer.<%- objCName(property) %>, constantExpression, @"<%- objCName(property) %> should round-trip constant value expressions."); +<% if (property.type !== 'formatted') { -%> constantExpression = [NSExpression expressionWithFormat:<%- objCTestValue(property, type, false, 3) %>]; +<% } else { -%> + MGLAttributedExpression *attributedConstantExpression = [[MGLAttributedExpression alloc] initWithExpression:[NSExpression expressionWithFormat:<%- objCTestValue(property, 'string', true, 3) %>] + attributes:@{}]; + constantExpression = [NSExpression mgl_expressionForAttributedExpressions:@[[NSExpression expressionForConstantValue:attributedConstantExpression]]]; +<% } -%> NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}]; layer.<%- objCName(property) %> = functionExpression; @@ -180,7 +189,9 @@ @"Setting <%- objCName(property) %> to a constant string with tokens should convert to an expression."); <% if (property.type === 'formatted') { -%> - NSExpression* tokenExpression = [NSExpression expressionWithFormat:@"MGL_FUNCTION('format', CAST(token, 'NSString'), %@)", @{}]; + MGLAttributedExpression *tokenAttibutedExpression = [[MGLAttributedExpression alloc] initWithExpression:[NSExpression expressionWithFormat:@"CAST(token, 'NSString')"] + attributes:@{}]; + NSExpression* tokenExpression = [NSExpression mgl_expressionForAttributedExpressions:@[[NSExpression expressionForConstantValue:tokenAttibutedExpression]]]; <% } else { -%> NSExpression* tokenExpression = [NSExpression expressionWithFormat:@"CAST(token, \"NSString\")"]; <% } -%> diff --git a/platform/darwin/test/MGLSymbolStyleLayerTests.mm b/platform/darwin/test/MGLSymbolStyleLayerTests.mm index f02c5d496e..2f4206a96b 100644 --- a/platform/darwin/test/MGLSymbolStyleLayerTests.mm +++ b/platform/darwin/test/MGLSymbolStyleLayerTests.mm @@ -3,6 +3,7 @@ #import "MGLStyleLayerTests.h" #import "../../darwin/src/NSDate+MGLAdditions.h" +#include "../../darwin/src/MGLAttributedExpression.h" #import "MGLStyleLayer_Private.h" @@ -1104,7 +1105,9 @@ XCTAssertEqualObjects(layer.text, constantExpression, @"text should round-trip constant value expressions."); - constantExpression = [NSExpression expressionWithFormat:@"MGL_FUNCTION('format', 'Text Field', %@)", @{}]; + MGLAttributedExpression *attributedConstantExpression = [[MGLAttributedExpression alloc] initWithExpression:[NSExpression expressionWithFormat:@"'Text Field'"] + attributes:@{}]; + constantExpression = [NSExpression mgl_expressionForAttributedExpressions:@[[NSExpression expressionForConstantValue:attributedConstantExpression]]]; NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}]; layer.text = functionExpression; @@ -1140,7 +1143,9 @@ XCTAssertEqual(rawLayer->getTextField(), propertyValue, @"Setting text to a constant string with tokens should convert to an expression."); - NSExpression* tokenExpression = [NSExpression expressionWithFormat:@"MGL_FUNCTION('format', CAST(token, 'NSString'), %@)", @{}]; + MGLAttributedExpression *tokenAttibutedExpression = [[MGLAttributedExpression alloc] initWithExpression:[NSExpression expressionWithFormat:@"CAST(token, 'NSString')"] + attributes:@{}]; + NSExpression* tokenExpression = [NSExpression mgl_expressionForAttributedExpressions:@[[NSExpression expressionForConstantValue:tokenAttibutedExpression]]]; XCTAssertEqualObjects(layer.text, tokenExpression, @"Setting text to a constant string with tokens should convert to an expression."); } diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 034223d085..a1ab7c949a 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -8,6 +8,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * Added an MGLMapView.prefetchesTiles property that you can disable if you don’t want to prefetch simplified tiles as a performance optimization. ([#14031](https://github.com/mapbox/mapbox-gl-native/pull/14031)) * Fixed an issue that caused `MGL_FUNCTION` to ignore multiple formatting parameters when passed a `format` function as parameter. ([#14064](https://github.com/mapbox/mapbox-gl-native/pull/14064)) * Added a Galician localization. ([#14095](https://github.com/mapbox/mapbox-gl-native/pull/14095)) +* Added `mgl_attributed:` expression operator, which concatenate `MGLAttributedExpression` objects for specifying rich text in the `MGLSymbolStyleLayer.text` property. ([#14094](https://github.com/mapbox/mapbox-gl-native/pull/14094)) ### User interaction diff --git a/platform/ios/docs/guides/For Style Authors.md b/platform/ios/docs/guides/For Style Authors.md index 72f326cc17..73394ff6ed 100644 --- a/platform/ios/docs/guides/For Style Authors.md +++ b/platform/ios/docs/guides/For Style Authors.md @@ -390,6 +390,7 @@ In style specification | Method, function, or predicate type | Format string syn `zoom` | `NSExpression.zoomLevelVariableExpression` | `$zoomLevel` `heatmap-density` | `NSExpression.heatmapDensityVariableExpression` | `$heatmapDensity` `line-progress` | `NSExpression.lineProgressVariableExpression` | `$lineProgress` +`format` | `+[NSExpression mgl_expressionForAttributedExpressions:]` or `mgl_attributed:` | `mgl_attributed({x, y, z})` For operators that have no corresponding `NSExpression` symbol, use the `MGL_FUNCTION()` format string syntax. diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj index 51e9ba19b0..b24bcbe110 100644 --- a/platform/ios/ios.xcodeproj/project.pbxproj +++ b/platform/ios/ios.xcodeproj/project.pbxproj @@ -57,6 +57,10 @@ 1FCAE2A520B872A400C577DD /* MGLLocationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1FCAE2A120B872A400C577DD /* MGLLocationManager.m */; }; 1FCAE2A820B88B3800C577DD /* MGLLocationManager_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 1FCAE2A620B88B3800C577DD /* MGLLocationManager_Private.h */; }; 1FCAE2A920B88B3800C577DD /* MGLLocationManager_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 1FCAE2A620B88B3800C577DD /* MGLLocationManager_Private.h */; }; + 1FF48587223710BE00F19727 /* MGLAttributedExpression.h in Headers */ = {isa = PBXBuildFile; fileRef = 1FF48585223710BE00F19727 /* MGLAttributedExpression.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 1FF48588223710BE00F19727 /* MGLAttributedExpression.h in Headers */ = {isa = PBXBuildFile; fileRef = 1FF48585223710BE00F19727 /* MGLAttributedExpression.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 1FF48589223710BE00F19727 /* MGLAttributedExpression.m in Sources */ = {isa = PBXBuildFile; fileRef = 1FF48586223710BE00F19727 /* MGLAttributedExpression.m */; }; + 1FF4858A223710BE00F19727 /* MGLAttributedExpression.m in Sources */ = {isa = PBXBuildFile; fileRef = 1FF48586223710BE00F19727 /* MGLAttributedExpression.m */; }; 30E578171DAA85520050F07E /* UIImage+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 30E578111DAA7D690050F07E /* UIImage+MGLAdditions.h */; }; 30E578181DAA85520050F07E /* UIImage+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 30E578111DAA7D690050F07E /* UIImage+MGLAdditions.h */; }; 30E578191DAA855E0050F07E /* UIImage+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 30E578121DAA7D690050F07E /* UIImage+MGLAdditions.mm */; }; @@ -887,6 +891,8 @@ 1FDB00C921F8F15000D21389 /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = cs; path = cs.lproj/Foundation.stringsdict; sourceTree = "<group>"; }; 1FDB00CA21F8F15100D21389 /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Localizable.strings; sourceTree = "<group>"; }; 1FDB00CB21F8F15300D21389 /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = cs; path = cs.lproj/Localizable.stringsdict; sourceTree = "<group>"; }; + 1FF48585223710BE00F19727 /* MGLAttributedExpression.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLAttributedExpression.h; sourceTree = "<group>"; }; + 1FF48586223710BE00F19727 /* MGLAttributedExpression.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLAttributedExpression.m; sourceTree = "<group>"; }; 20DABE861DF78148007AC5FF /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Foundation.strings"; sourceTree = "<group>"; }; 20DABE881DF78148007AC5FF /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = "<group>"; }; 30E578111DAA7D690050F07E /* UIImage+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIImage+MGLAdditions.h"; path = "src/UIImage+MGLAdditions.h"; sourceTree = SOURCE_ROOT; }; @@ -2086,6 +2092,8 @@ DA00FC8C1D5EEB0D009AABC8 /* MGLAttributionInfo.h */, DAF0D8171DFE6B2800B28378 /* MGLAttributionInfo_Private.h */, DA00FC8D1D5EEB0D009AABC8 /* MGLAttributionInfo.mm */, + 1FF48585223710BE00F19727 /* MGLAttributedExpression.h */, + 1FF48586223710BE00F19727 /* MGLAttributedExpression.m */, 556660C91E1BF3A900E2C41B /* MGLFoundation.h */, 558DE79E1E5615E400C7916D /* MGLFoundation_Private.h */, 558DE79F1E5615E400C7916D /* MGLFoundation.mm */, @@ -2502,6 +2510,7 @@ 30E578171DAA85520050F07E /* UIImage+MGLAdditions.h in Headers */, 96036A01200565C700510F3D /* NSOrthography+MGLAdditions.h in Headers */, 1F7454961ECD450D00021D39 /* MGLLight_Private.h in Headers */, + 1FF48587223710BE00F19727 /* MGLAttributedExpression.h in Headers */, DAD1656C1CF41981001FF4B9 /* MGLFeature.h in Headers */, 40EDA1C01CFE0E0200D9EA68 /* MGLAnnotationContainerView.h in Headers */, 9620BB381E69FE1700705A1D /* MGLSDKUpdateChecker.h in Headers */, @@ -2658,6 +2667,7 @@ 353AFA151D65AB17005A69F4 /* NSDate+MGLAdditions.h in Headers */, AC518E00201BB55A00EBC820 /* MGLTelemetryConfig.h in Headers */, 3510FFFA1D6DCC4700F413B2 /* NSCompoundPredicate+MGLAdditions.h in Headers */, + 1FF48588223710BE00F19727 /* MGLAttributedExpression.h in Headers */, DA72620C1DEEE3480043BB89 /* MGLOpenGLStyleLayer.h in Headers */, 35CE61831D4165D9004F2359 /* UIColor+MGLAdditions.h in Headers */, 96E516F32000597100A02306 /* NSDictionary+MGLAdditions.h in Headers */, @@ -3267,6 +3277,7 @@ DA8848291CBAFA6200AB86E3 /* MGLStyle.mm in Sources */, 357FE2DF1E02D2B20068B753 /* NSCoder+MGLAdditions.mm in Sources */, 40834BF81FE05E1800C1BD0D /* NSData+MMEGZIP.m in Sources */, + 1FF48589223710BE00F19727 /* MGLAttributedExpression.m in Sources */, DA88481C1CBAFA6200AB86E3 /* MGLGeometry.mm in Sources */, 558DE7A21E5615E400C7916D /* MGLFoundation.mm in Sources */, 40834BE91FE05E1800C1BD0D /* MMECommonEventData.m in Sources */, @@ -3404,6 +3415,7 @@ 3510FFF31D6D9D8C00F413B2 /* NSExpression+MGLAdditions.mm in Sources */, 40834C0C1FE05E1800C1BD0D /* NSData+MMEGZIP.m in Sources */, DAA4E4321CBB730400178DFB /* MGLMapView.mm in Sources */, + 1FF4858A223710BE00F19727 /* MGLAttributedExpression.m in Sources */, 40834BFD1FE05E1800C1BD0D /* MMECommonEventData.m in Sources */, DAA4E41E1CBB730400178DFB /* MGLMapCamera.mm in Sources */, FA68F14E1E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.mm in Sources */, diff --git a/platform/ios/sdk-files.json b/platform/ios/sdk-files.json index f5a6eccc27..7dbfee1a2d 100644 --- a/platform/ios/sdk-files.json +++ b/platform/ios/sdk-files.json @@ -89,6 +89,7 @@ "platform/darwin/src/MGLStyle.mm", "platform/darwin/src/NSCoder+MGLAdditions.mm", "platform/ios/vendor/mapbox-events-ios/MapboxMobileEvents/NSData+MMEGZIP.m", + "platform/darwin/src/MGLAttributedExpression.m", "platform/darwin/src/MGLGeometry.mm", "platform/darwin/src/MGLFoundation.mm", "platform/ios/vendor/mapbox-events-ios/MapboxMobileEvents/MMECommonEventData.m", @@ -187,6 +188,7 @@ "MGLHeatmapStyleLayer.h": "platform/darwin/src/MGLHeatmapStyleLayer.h", "MGLCircleStyleLayer.h": "platform/darwin/src/MGLCircleStyleLayer.h", "MGLMultiPoint.h": "platform/darwin/src/MGLMultiPoint.h", + "MGLAttributedExpression.h": "platform/darwin/src/MGLAttributedExpression.h", "MGLFeature.h": "platform/darwin/src/MGLFeature.h", "MGLMapCamera.h": "platform/darwin/src/MGLMapCamera.h", "MGLForegroundStyleLayer.h": "platform/darwin/src/MGLForegroundStyleLayer.h", diff --git a/platform/ios/src/Mapbox.h b/platform/ios/src/Mapbox.h index 43300b6aa5..34907d6c5a 100644 --- a/platform/ios/src/Mapbox.h +++ b/platform/ios/src/Mapbox.h @@ -72,3 +72,4 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[]; #import "MGLLocationManager.h" #import "MGLLoggingConfiguration.h" #import "MGLNetworkConfiguration.h" +#import "MGLAttributedExpression.h" diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index ba3f00a5d6..2542f57d95 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -9,6 +9,7 @@ * Added the `-[MGLShapeSource leavesOfCluster:offset:limit:]`, `-[MGLShapeSource childrenOfCluster:]`, `-[MGLShapeSource zoomLevelForExpandingCluster:]` methods for inspecting a cluster in an `MGLShapeSource`s created with the `MGLShapeSourceOptionClustered` option. Feature querying now returns clusters represented by `MGLPointFeatureCluster` objects (that conform to the `MGLCluster` protocol). ([#12952](https://github.com/mapbox/mapbox-gl-native/pull/12952) * Added `MGLNetworkConfiguration` class to customize the SDK's `NSURLSessionConfiguration` object. ([#11447](https://github.com/mapbox/mapbox-gl-native/pull/13886)) * Fixed an issue that caused `MGL_FUNCTION` to ignore multiple formatting parameters when passed a `format` function as parameter. ([#14064](https://github.com/mapbox/mapbox-gl-native/pull/14064)) +* Added `mgl_attributed:` expression operator, which concatenate `MGLAttributedExpression` objects for specifying rich text in the `MGLSymbolStyleLayer.text` property. ([#14094](https://github.com/mapbox/mapbox-gl-native/pull/14094)) ### Annotations diff --git a/platform/macos/docs/guides/For Style Authors.md b/platform/macos/docs/guides/For Style Authors.md index fdba68879c..038ddf1f93 100644 --- a/platform/macos/docs/guides/For Style Authors.md +++ b/platform/macos/docs/guides/For Style Authors.md @@ -383,6 +383,7 @@ In style specification | Method, function, or predicate type | Format string syn `zoom` | `NSExpression.zoomLevelVariableExpression` | `$zoomLevel` `heatmap-density` | `NSExpression.heatmapDensityVariableExpression` | `$heatmapDensity` `line-progress` | `NSExpression.lineProgressVariableExpression` | `$lineProgress` +`format` | `+[NSExpression mgl_expressionForAttributedExpressions:]` or `mgl_attributed:` | `mgl_attributed({x, y, z})` For operators that have no corresponding `NSExpression` symbol, use the `MGL_FUNCTION()` format string syntax. diff --git a/platform/macos/macos.xcodeproj/project.pbxproj b/platform/macos/macos.xcodeproj/project.pbxproj index 739a388e6e..5d5a94c78a 100644 --- a/platform/macos/macos.xcodeproj/project.pbxproj +++ b/platform/macos/macos.xcodeproj/project.pbxproj @@ -29,6 +29,8 @@ 1F95931B1E6DE2B600D5B294 /* MGLNSDateAdditionsTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1F95931A1E6DE2B600D5B294 /* MGLNSDateAdditionsTests.mm */; }; 1F9EF4061FBA1B0E0063FBB0 /* mapbox_helmet.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 1F9EF4051FBA1B0D0063FBB0 /* mapbox_helmet.pdf */; }; 1FC481852098F323000D09B4 /* NSPredicate+MGLPrivateAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1FC481842098F323000D09B4 /* NSPredicate+MGLPrivateAdditions.h */; }; + 1FF4858D2237235300F19727 /* MGLAttributedExpression.m in Sources */ = {isa = PBXBuildFile; fileRef = 1FF4858B2237235200F19727 /* MGLAttributedExpression.m */; }; + 1FF4858E2237235300F19727 /* MGLAttributedExpression.h in Headers */ = {isa = PBXBuildFile; fileRef = 1FF4858C2237235200F19727 /* MGLAttributedExpression.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3508EC641D749D39009B0EE4 /* NSExpression+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 3508EC621D749D39009B0EE4 /* NSExpression+MGLAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3508EC651D749D39009B0EE4 /* NSExpression+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3508EC631D749D39009B0EE4 /* NSExpression+MGLAdditions.mm */; }; 3526EABD1DF9B19800006B43 /* MGLCodingTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3526EABC1DF9B19800006B43 /* MGLCodingTests.mm */; }; @@ -343,6 +345,8 @@ 1FC481842098F323000D09B4 /* NSPredicate+MGLPrivateAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSPredicate+MGLPrivateAdditions.h"; sourceTree = "<group>"; }; 1FDB00CC21F8F1FD00D21389 /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Localizable.strings; sourceTree = "<group>"; }; 1FDB00CD21F8F1FF00D21389 /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Localizable.strings; sourceTree = "<group>"; }; + 1FF4858B2237235200F19727 /* MGLAttributedExpression.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLAttributedExpression.m; sourceTree = "<group>"; }; + 1FF4858C2237235200F19727 /* MGLAttributedExpression.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAttributedExpression.h; sourceTree = "<group>"; }; 3508EC621D749D39009B0EE4 /* NSExpression+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSExpression+MGLAdditions.h"; sourceTree = "<group>"; }; 3508EC631D749D39009B0EE4 /* NSExpression+MGLAdditions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSExpression+MGLAdditions.mm"; sourceTree = "<group>"; }; 3526EABC1DF9B19800006B43 /* MGLCodingTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLCodingTests.mm; path = ../../darwin/test/MGLCodingTests.mm; sourceTree = "<group>"; }; @@ -1204,6 +1208,8 @@ DA00FC881D5EEAC3009AABC8 /* MGLAttributionInfo.h */, DAF0D8151DFE6B1800B28378 /* MGLAttributionInfo_Private.h */, DA00FC891D5EEAC3009AABC8 /* MGLAttributionInfo.mm */, + 1FF4858C2237235200F19727 /* MGLAttributedExpression.h */, + 1FF4858B2237235200F19727 /* MGLAttributedExpression.m */, 556660C51E1BEA0100E2C41B /* MGLFoundation.h */, 558DE7A41E56161C00C7916D /* MGLFoundation_Private.h */, 558DE7A51E56161C00C7916D /* MGLFoundation.mm */, @@ -1276,6 +1282,7 @@ DAE6C38D1CC31E2A00DB3429 /* MGLOfflineRegion_Private.h in Headers */, DA7DC9831DED647F0027472F /* MGLRasterTileSource_Private.h in Headers */, 747ABE5B219B2BDB00523B67 /* MGLBackgroundStyleLayer_Private.h in Headers */, + 1FF4858E2237235300F19727 /* MGLAttributedExpression.h in Headers */, 1753ED401E53CE6100A9FD90 /* MGLConversion.h in Headers */, 747ABE59219B2BDB00523B67 /* MGLFillExtrusionStyleLayer_Private.h in Headers */, DA8F259C1D51CB000010E6B5 /* MGLStyleValue_Private.h in Headers */, @@ -1653,6 +1660,7 @@ 352742821D4C243B00A1ECE6 /* MGLSource.mm in Sources */, DAE6C3881CC31E2A00DB3429 /* MGLMapCamera.mm in Sources */, DA6408D81DA4E5DA00908C90 /* MGLVectorStyleLayer.m in Sources */, + 1FF4858D2237235300F19727 /* MGLAttributedExpression.m in Sources */, DA8F25B31D51CB270010E6B5 /* NSValue+MGLStyleAttributeAdditions.mm in Sources */, DAE6C3911CC31E2A00DB3429 /* MGLPolygon.mm in Sources */, 35C6DF851E214C0400ACA483 /* MGLDistanceFormatter.m in Sources */, diff --git a/platform/macos/sdk-files.json b/platform/macos/sdk-files.json index 3c728ad37d..25987a5a0e 100644 --- a/platform/macos/sdk-files.json +++ b/platform/macos/sdk-files.json @@ -47,6 +47,7 @@ "platform/darwin/src/MGLSource.mm", "platform/darwin/src/MGLMapCamera.mm", "platform/darwin/src/MGLVectorStyleLayer.m", + "platform/darwin/src/MGLAttributedExpression.m", "platform/darwin/src/NSValue+MGLStyleAttributeAdditions.mm", "platform/darwin/src/MGLPolygon.mm", "platform/darwin/src/MGLDistanceFormatter.m", @@ -80,6 +81,7 @@ "MGLComputedShapeSource.h": "platform/darwin/src/MGLComputedShapeSource.h", "MGLRasterStyleLayer.h": "platform/darwin/src/MGLRasterStyleLayer.h", "NSExpression+MGLAdditions.h": "platform/darwin/src/NSExpression+MGLAdditions.h", + "MGLAttributedExpression.h": "platform/darwin/src/MGLAttributedExpression.h", "MGLAnnotation.h": "platform/darwin/src/MGLAnnotation.h", "MGLHillshadeStyleLayer.h": "platform/darwin/src/MGLHillshadeStyleLayer.h", "MGLMapSnapshotter.h": "platform/darwin/src/MGLMapSnapshotter.h", diff --git a/platform/macos/src/Mapbox.h b/platform/macos/src/Mapbox.h index 4d54c753df..a11867657d 100644 --- a/platform/macos/src/Mapbox.h +++ b/platform/macos/src/Mapbox.h @@ -67,3 +67,4 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[]; #import "NSPredicate+MGLAdditions.h" #import "MGLLoggingConfiguration.h" #import "MGLNetworkConfiguration.h" +#import "MGLAttributedExpression.h" |