-
Notifications
You must be signed in to change notification settings - Fork 1.3k
[ios, macos] Add NSExpression convenience constructors and helper methods. #11278
Conversation
c89a9bc
to
11e50c7
Compare
What kind of restrictions did you run into? As long as the category method returns - (instancetype)mgl_expressionByAppendingStringExpression:(NSExpression *)stringExpression {
return [NSExpression expressionForFunction:self selectorName:@selector(stringByAppendingString:) arguments:@[stringExpression]];
} The only restriction I’m aware of is that a category can’t declare or autosynthesize ivars, but there’s a common workaround using associated objects.
In general, I tend to be skeptical about vending a builder pattern as part of an Objective-C library (as opposed to doing so in a standalone application, where there’s more room for experimentation). Did you consider subclassing NSExpression as MGLMutableExpression?
“Append” is probably not the best way to think about it, since usually only strings or arrays get appended to. Rather, a step can be nested inside an interpolation or vice versa. Under the hood, that ends up looking a lot like the composite function of yore. |
11e50c7
to
3cda0e3
Compare
/** | ||
Returns a constant expression containing an `NSString`. | ||
|
||
This is equivalent to call `[NSExpression expressionForConstant:]`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It may already be confusing enough that there are two ways to create a string-typed constant expression (+[NSExpression expressionWithFormat:]
and +[NSExpression expressionForConstantValue:]
). This third way provides a little bit of type safety, but I’m not sure that it’s worth the additional complexity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Initially I thought it would be unnecessary to add this convenient method, but after making examples using each format it was more clear/natural use the constructor for each type. I'm not sure all people are familiar with which flavor of NSExpression they may need, and I don't want them to spend time trying to figure it out. I think we should keep it.
*/ | ||
+ (instancetype)mgl_expressionForValue:(nonnull NSValue *)value; | ||
|
||
+ (instancetype)mgl_expressionForTernaryFunction:(nonnull NSString *)conditionString trueExpression:(nonnull NSExpression *)trueExpression falseExpresssion:(nonnull NSExpression *)falseExpression; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let’s turn this into a polyfill for the preexisting +expressionForConditional:trueExpression:falseExpression:
. It’d be a lot friendlier and safer to require an NSPredicate than a freeform string. (What syntax would the string use, for instance?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One of the forms I tried was using an NSPredicate, it didn't work quite as I expected but I didn't use expressionForConditional:trueExpression:falseExpression
in the underlaying implementation, let me give it a second try.
|
||
+ (instancetype)mgl_expressionForStepFunction:(MGLExpressionStyleFunction)function defaultExpression:(NSExpression *)expression stops:(nonnull NS_DICTIONARY_OF(NSNumber *, id) *)stops { | ||
NSString *functionFormat = [NSString stringWithFormat:@"FUNCTION(%@, 'mgl_stepWithMinimum:stops:', %%@, %%@, %%@)", function]; | ||
return [NSExpression expressionWithFormat:functionFormat, expression, stops]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use +expressionForFunction:selectorName:arguments:
for improved type safety.
} | ||
|
||
+ (instancetype)mgl_expressionForStepFunction:(MGLExpressionStyleFunction)function defaultExpression:(NSExpression *)expression stops:(nonnull NS_DICTIONARY_OF(NSNumber *, id) *)stops { | ||
NSString *functionFormat = [NSString stringWithFormat:@"FUNCTION(%@, 'mgl_stepWithMinimum:stops:', %%@, %%@, %%@)", function]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The first argument to FUNCTION()
is an expression representing the operand of the function. It may be a $zoomLevel
variable expression or a key path expression or an entire interpolation expression. So the first parameter to this method needs to be an NSExpression.
@@ -511,8 +511,6 @@ | |||
DA8963381CC549A100684375 /* sprites in Resources */ = {isa = PBXBuildFile; fileRef = DA8963341CC549A100684375 /* sprites */; }; | |||
DA8963391CC549A100684375 /* styles in Resources */ = {isa = PBXBuildFile; fileRef = DA8963351CC549A100684375 /* styles */; }; | |||
DA89633A1CC549A100684375 /* tiles in Resources */ = {isa = PBXBuildFile; fileRef = DA8963361CC549A100684375 /* tiles */; }; | |||
DA9EA82B201C0C0C00F9874D /* NSExpression+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DA9EA82A201C0C0B00F9874D /* NSExpression+MGLAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This header is intentionally public so that clients like the React Native SDK can use familiar JSON-like objects instead of composing NSExpression format strings.
/cc @nitaliano
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was not intentional.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can a test be added that ensures this will remain the case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was looking at the project and this header is marked as public. Not sure what happened.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@1ec5 looks like this header was duplicated and somehow this commit removed the duplicated entries. Although it's still public and I didn't do anything to remove it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, as long as the header is still part of both the dynamic and static targets, we’re in good shape. Xcode sometimes grooms the project file to remove duplicate references that may sneak in due to bad merges.
@param stops The stops dictionay must be numeric literals in strictly ascending order. | ||
*/ | ||
+ (instancetype)mgl_expressionForStepFunction:(nonnull NSExpression*)operatorExpression defaultExpression:(nonnull NSExpression *)expression stops:(nonnull NSExpression*)stops; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is a little confusing to pair [NSExpression expressionForConstantValue:]
with a stops dictionary. I would not expect that to be paired with a stops dictionary, especially with exponential interpolation mode.
Also, capturing that in Swift, steps: NSExpression(format: "\(stops)")
does not seem to work. steps: NSExpression(format: "%@", stops)
does work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is expected. Unlike with String(format:)
, the string representation of an object is not necessarily the same as the literal representation that NSExpression expects in a format string. NSExpression(format: "\(array)")
would also likely fail, because the literal representation of an array would look like {1, 2, 3, 4}
, not:
(
1,
2,
3,
4
)
In this case, there is no literal representation for a dictionary.
28fe298
to
a776ee2
Compare
|
||
@param string The string constant. | ||
*/ | ||
+ (instancetype)mgl_expressionForString:(nonnull NSString *)string; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regarding #11278 (comment), I think we should remove these initializers.
The original goal in #8074 was to reuse standard NSExpression APIs as much as possible, so that developers would lean on non-Mapbox resources and experiences to craft their runtime styling code. Someone who already isn’t sure whether to use NSExpression(format:)
or NSExpression(forConstantValue:)
now has to contend with NSExpression.mgl_expression(for:)
as well. If they’re used to NSExpression in other contexts, they may wonder what NSExpression.mgl_expression(for:)
provides that the other methods do not.
#11016 is meant to ease the transition for developers accustomed to the more strongly-typed MGLStyleValue API, but not necessarily to make NSExpression more strongly typed. In fact, these initializers add a false sense of type safety. The MGLStyleLayer properties no longer make use of lightweight generic types, so the following code can still crash the application at runtime:
symbolStyleLayer.text = NSExpression.mgl_expression(for: number)
Finally, the approach here is inconsistent with MGLStyleValue. NSExpression’s built-in initializers distinguish between constant values and functions, just like MGLStyleValue’s initializers. By contrast, NSExpression.mgl_expression(for:)
emphasizes the data type instead of the constant nature of the value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with you. These are repetitive. Although these methods attempt to avoid the confusion of having to know the difference or when to use a specific method. For someone new to expressions will they choose expressionForConstantValue
or expressionWithFormat
for a string/color/value? Also does expressionForConstantValue
means I won't be able to modify it later? These sort of questions are what these methods try to make easier to reason about.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or someone new to expressions will they choose expressionForConstantValue or expressionWithFormat for a string/color/value?
I think it would be the same as anyone newly discovering NSExpression or NSPredicate in general. There have always been multiple ways of creating one, but the format string is preferable unless you have an overriding need for type safety. Among Objective-C developers, that would be pretty rare. I originally brought up the idea of providing these initializers for Swift developers who are migrating from style functions, which are strongly typed to a fault.
Also does expressionForConstantValue means I won't be able to modify it later?
“Constant” here means the evaluated value doesn’t change based on the context; no expression is mutable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree. This PR no longer has value based initializers and focuses on the generic types.
@param trueExpression The expression for conditions equal to true. | ||
@param falseExpression The expression for conditions equal to false. | ||
*/ | ||
+ (instancetype)mgl_expressionForConditional:(nonnull NSString *)conditionPredicate trueExpression:(nonnull NSExpression *)trueExpression falseExpresssion:(nonnull NSExpression *)falseExpression; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we rename this method +expressionForConditional:trueExpression:falseExpression:
and conditionalize it so that it’s only defined for iOS and macOS versions that lack this initializer?
/ref #11278 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, that won’t work, because I don’t think the method is implemented at all on older versions of iOS and macOS – it isn’t just missing from the public headers.
Maybe we can combine this initializer with the one in #11450, since it’s just a special case of a case
statement that has no else if
clauses.
@param stops The stops dictionay must be numeric literals in strictly ascending order. | ||
*/ | ||
+ (instancetype)mgl_expressionForStepFunction:(nonnull NSExpression*)operatorExpression defaultExpression:(nonnull NSExpression *)expression stops:(nonnull NSExpression*)stops; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is expected. Unlike with String(format:)
, the string representation of an object is not necessarily the same as the literal representation that NSExpression expects in a format string. NSExpression(format: "\(array)")
would also likely fail, because the literal representation of an array would look like {1, 2, 3, 4}
, not:
(
1,
2,
3,
4
)
In this case, there is no literal representation for a dictionary.
|
||
@param expression The evaluated expression must return a string. | ||
*/ | ||
- (instancetype)mgl_appendingExpression:(NSExpression *)expression; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Per Objective-C naming conventions, this method should be named -mgl_expressionByAppendingExpression:
(cf. -[NSString stringByAppendingString:]
). Specify a Swift name of mgl_appending(_:)
.
|
||
+ (instancetype)mgl_expressionForConditional:(nonnull NSString *)conditionPredicate trueExpression:(nonnull NSExpression *)trueExpression falseExpresssion:(nonnull NSExpression *)falseExpression { | ||
NSPredicate *conditional = [NSPredicate predicateWithFormat:conditionPredicate]; | ||
return [NSExpression expressionForConditional:conditional trueExpression:trueExpression falseExpression:falseExpression]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this work on iOS 8.x or macOS 10.10.x?
} | ||
|
||
+ (instancetype)mgl_expressionForInterpolateFunction:(nonnull NSExpression*)expressionOperator curveType:(nonnull MGLExpressionInterpolationMode)curveType parameters:(nullable NSExpression *)parameters steps:(nonnull NSExpression*)steps { | ||
NSExpression *sanitizeParams = parameters ? parameters : [NSExpression expressionWithFormat:@"%@", parameters]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If parameters
is nil
, then sanitizeParams
becomes a constant value expression representing nil
. Consider writing that out explicitly:
parameters = parameters ?: [NSExpression expressionForConstantValue:nil];
@param curveType The curve type could be `MGLExpressionInterpolationModeLinear`, | ||
`MGLExpressionInterpolationModeExponential` and | ||
`MGLExpressionInterpolationModeCubicBezier`. | ||
@param steps The steps dictionay must be numeric literals in strictly ascending order. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo: dictionary.
<a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-zoom"><code>zoom</code></a> | ||
expression reference in the Mapbox Style Specification. | ||
*/ | ||
extern MGL_EXPORT const MGLExpressionStyleFunction MGLExpressionStyleFunctionZoomLevel; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These aren’t functions, they’re inputs to the expression, represented as variables. Rename MGLExpressionStyleFunction
to MGLExpressionVariableName
. Better yet, replace these constants with class properties on a category of NSExpression that return variable expressions. For example, the property NSExpression.mgl_zoomLevelVariableExpression
would return an instance of NSExpression that was initialized using +[NSExpression expressionForVariable:]
in a dispatch_once
block.
51e8894
to
0a72bff
Compare
4eb3476
to
cdd2cc4
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In addition to the feedback below, the “Predicates and Expressions” guide needs to be updated to mention that some of the custom functions have corresponding category initializers on NSExpression.
|
||
@end | ||
|
||
@interface NSExpression (MGLInitializerAdditions) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need this many separate categories? So far we’ve only split out separate categories due to differences in access control or because one category is automatically generated.
@param trueExpression The expression for conditions equal to true. | ||
@param falseExpression The expression for conditions equal to false. | ||
*/ | ||
+ (instancetype)mgl_expressionForConditional:(nonnull NSPredicate *)conditionPredicate trueExpression:(nonnull NSExpression *)trueExpression falseExpresssion:(nonnull NSExpression *)falseExpression NS_SWIFT_NAME(mgl_conditional(_:trueExpression:falseExpression:)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For consistency with NSExpression(forConditional:true:false:)
, the Swift name should be init(forMGLConditional:true:false)
. Currently, a Swift developer is forced to write NSExpression.mgl_conditional(predicate, trueExpression: trueExpression, falseExpression: falseExpression)
, which violates Swift naming guidelines about redundancy.
@param from The expression which could be a constant or function expression. | ||
@param stops The stops dictionay must be numeric literals in strictly ascending order. | ||
*/ | ||
+ (instancetype)mgl_expressionForStepFunction:(nonnull NSExpression*)input from:(nonnull NSExpression *)from stops:(nonnull NSExpression*)stops NS_SWIFT_NAME(mgl_stepFunction(input:from:stops:)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
“Function” here is ambiguous as to whether it refers to an expression function or a style function, which could be problematic for developers migrating from style functions. I wonder if we arrived at this name in order to align with +expressionForFunction:arguments:
. The difference is that “function” in that method describes the parameter, which is the name of a function, whereas here it has nothing to do with the first parameter. If we wanted to align with that method, we’d have to call it +mgl_stepExpressionForInputExpression:minimumExpression:stops:
.
The aftermarket expression function is called mgl_step:from:stops:
. This initializer should be called either +mgl_expressionForSteppingExpression:minimum:stops:
or +mgl_expressionForSteppingExpression:fromExpression:stops:
. There may need to be a Swift name for this initializer as well, to avoid the problem in #11612.
@param parameters The parameters expression. | ||
@param steps The steps expression. | ||
*/ | ||
+ (instancetype)mgl_expressionForInterpolateFunction:(nonnull NSExpression*)input curveType:(nonnull MGLExpressionInterpolationMode)curveType parameters:(nullable NSExpression *)parameters steps:(nonnull NSExpression*)steps NS_SWIFT_NAME(mgl_interpolateFunction(input:curveType:parameters:steps:)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Stops, not steps:
+mgl_expressionForInterpolatingExpression:withCurveType:parameters:stops:
@param values The lookup values expression. | ||
@param defaultValue The defaultValue expression to be used in case there is no match. | ||
*/ | ||
+ (instancetype)mgl_expressionForMatchFunction:(nonnull NSExpression*)condition values:(nonnull NSExpression *)values defaultValue:(nonnull NSExpression *)defaultValue NS_SWIFT_NAME(mgl_matchFunction(condition:values:defaultValue:)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It’s unclear to me how this method signature would produce a match
expression. How would a developer specify multiple conditions? Where does the input expression go? What does the values
parameter contain?
A more intuitive design would accept a dictionary mapping inputs to outputs:
+ (instancetype)mgl_expressionForMatchingKey:(NSExpression *)inputExpression inDictionary:(NSDictionary<NSExpression *, NSExpression *> *)matchedExpressions defaultExpression:(NSExpression *)defaultExpression;
+ (instancetype)mgl_expressionForMatchFunction:(nonnull NSExpression*)condition values:(nonnull NSExpression *)values defaultValue:(nonnull NSExpression *)defaultValue NS_SWIFT_NAME(mgl_matchFunction(condition:values:defaultValue:)); | ||
|
||
/** | ||
Returns a constant expression appending the passed expression. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that both the receiver and the given expression must be strings; otherwise, an exception is raised.
<a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-zoom"><code>zoom</code></a> | ||
expression reference in the Mapbox Style Specification. | ||
*/ | ||
+ (NSExpression *)mgl_zoomLevelVariableExpression; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These should be class properties, per #6778.
c29d11c
to
889a413
Compare
# Conflicts: # platform/darwin/src/NSExpression+MGLAdditions.h # platform/darwin/src/NSExpression+MGLAdditions.mm # platform/ios/app/MBXViewController.m
# Conflicts: # platform/darwin/src/NSExpression+MGLAdditions.h # platform/darwin/src/NSExpression+MGLAdditions.mm # platform/darwin/test/MGLExpressionTests.mm
# Conflicts: # platform/darwin/src/NSExpression+MGLAdditions.h
# Conflicts: # platform/ios/app/MBXViewController.m
889a413
to
417eee0
Compare
@@ -9,6 +9,133 @@ | |||
|
|||
NS_ASSUME_NONNULL_BEGIN | |||
|
|||
typedef NSString *MGLExpressionInterpolationMode NS_TYPED_EXTENSIBLE_ENUM; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if this enumeration really should be extensible, since the interpolation modes are determined by the style specification rather than being a freeform value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, this was a auto-completion problem.
@param trueExpression The expression for conditions equal to true. | ||
@param falseExpression The expression for conditions equal to false. | ||
*/ | ||
+ (instancetype)mgl_expressionForConditional:(nonnull NSPredicate *)conditionPredicate trueExpression:(nonnull NSExpression *)trueExpression falseExpresssion:(nonnull NSExpression *)falseExpression NS_SWIFT_NAME(init(forMGLConditional:trueExpression:falseExpression:)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
init(forMGLConditional:true:false:)
, not init(forMGLConditional:trueExpression:falseExpression:
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I stand corrected: in Swift, the initializer is NSExpression(forConditional:trueExpression:falseExpression:)
, but the properties are just true
and false
. Go figure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Per our chat this is not possible in Swift 4.1
@param steppingExpression The stepping expression. | ||
@param fromExpression The expression which could be a constant or function expression. | ||
@param stops The stops must be an `NSDictionary` constant `NSExpression`. | ||
The wrapped dictionary must be numeric expression literals in strictly ascending order. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This requirement exists when forming the stops in JSON because the JSON format uses an array instead of an object. But the keys of an NSDictionary are intrinsically unordered, so we should remove this line.
@param stops The stops must be an `NSDictionary` constant `NSExpression`. | ||
The wrapped dictionary must be numeric expression literals in strictly ascending order. | ||
*/ | ||
+ (instancetype)mgl_expressionForSteppingExpression:(nonnull NSExpression*)steppingExpression fromExpression:(nonnull NSExpression *)fromExpression stops:(nonnull NSExpression*)stops NS_SWIFT_NAME(init(forMGLStepping:from:stops:)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fromExpression:(nonnull NSExpression *)minimumExpression
and stops. | ||
|
||
@param steppingExpression The stepping expression. | ||
@param fromExpression The expression which could be a constant or function expression. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The name of the parameter should be minimumExpression
, even though the selector piece is fromExpression:
, because variable names should never begin with prepositions.
@param parameters The parameters expression. | ||
@param stops The stops expression. | ||
*/ | ||
+ (instancetype)mgl_expressionForInterpolatingExpression:(nonnull NSExpression*)interpolatingExpression withCurveType:(nonnull MGLExpressionInterpolationMode)curveType parameters:(nullable NSExpression *)parameters stops:(nonnull NSExpression*)stops NS_SWIFT_NAME(init(forMGLInterpolating:curveType:parameters:stops:)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
inputExpression
, not interpolatingExpression
. The idea is that this function produces an expression for interpolating an input expression named inputExpression
.
@param matchedExpressions The matched values expression dictionary must be condition : value. | ||
@param defaultExpression The defaultValue expression to be used in case there is no match. | ||
*/ | ||
+ (instancetype)mgl_expressionForMatchingExpression:(nonnull NSExpression *)matchingExpression inDictionary:(nonnull NSDictionary<NSExpression *, NSExpression *> *)matchedExpressions defaultExpression:(nonnull NSExpression *)defaultExpression NS_SWIFT_NAME(init(forMGLMatchingKey:matched:default:)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
inputExpression
, not matchingExpression
.
|
||
@param expression The expression to append to the receiver. | ||
system’s preferred language, if supported, specify `nil`. To use the local | ||
language, specify a locale with the identifier `mul`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Languages have nothing to do with this method.
@param matchedExpressions The matched values expression dictionary must be condition : value. | ||
@param defaultExpression The defaultValue expression to be used in case there is no match. | ||
*/ | ||
+ (instancetype)mgl_expressionForMatchingExpression:(nonnull NSExpression *)matchingExpression inDictionary:(nonnull NSDictionary<NSExpression *, NSExpression *> *)matchedExpressions defaultExpression:(nonnull NSExpression *)defaultExpression NS_SWIFT_NAME(init(forMGLMatchingKey:matched:default:)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Swift name should be init(forMGLMatchingKey:in:default:)
, because “matched” doesn’t describe the relationship between the first and second arguments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great!
Fixes #11016.
This PR adds convenience initializers and properties that helps developers transition from the strongly typed
MGLStyleValue
class./cc @1ec5