-
Notifications
You must be signed in to change notification settings - Fork 1.3k
[ios] Add inline examples in documentation #7337
Conversation
@ericrwolfe, thanks for your PR! By analyzing this pull request, we identified @incanus, @1ec5 and @boundsj to be potential reviewers. |
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.
Thanks for this PR. Examples in our documentation are long overdue.
Most of the comments below relate to changes that have taken place since beta 4 that break these examples. Have you considered other approaches to ensuring maintainability, like keeping these examples in a Swift playground and pulling them into the header in a build step?
]) | ||
layer.circleOpacity = MGLStyleValue(rawValue: 0.7) | ||
layer.predicate = NSPredicate(format: "%K == %@", "ethnicity", "ewok") | ||
mapView.style().add(layer) |
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.
#6097 replaced MGLMapView.style()
with an MGLMapView.style
property.
22: MGLStyleValue(rawValue: 180) | ||
]) | ||
layer.circleOpacity = MGLStyleValue(rawValue: 0.7) | ||
layer.predicate = NSPredicate(format: "%K == %@", "ethnicity", "ewok") |
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.
Nit: ethnicity == 'ewok'
would have clearer intent. And lol on the choice of key-value pair.
var coordinates: [CLLocationCoordinate2D] = [] | ||
let polyline = MGLPolylineFeature(coordinates: &coordinates, count: UInt(coordinates.count)) | ||
let source = MGLGeoJSONSource(identifier: "lines", features: [polyline], options: nil) | ||
self.mapView.style().add(source) |
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.
Nit: other examples omit self.
, presumably because mapView
could be a local variable.
```swift | ||
var coordinates: [CLLocationCoordinate2D] = [] | ||
let polyline = MGLPolylineFeature(coordinates: &coordinates, count: UInt(coordinates.count)) | ||
let source = MGLGeoJSONSource(identifier: "lines", features: [polyline], options: nil) |
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.
```swift | ||
let meters = 1500.0 | ||
let layer = MGLLineStyleLayer(identifier: "contour", source: contours) | ||
layer.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [ |
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 a lot of typing. Is there a point you want to make about the individual classes within the NSPredicate class cluster, or would a single predicate format string such as (index == 10 || index == 5) && ele >= 200
be sufficient?
Maintainability and testability are important considerations that have kept us from expanding our examples repertoire in the past, so I’m agreed that we should tackle those problems in tandem with the actual example-writing process. Using a Swift playground is an interesting idea that opens the possibility of users being able to fiddle with live examples themselves, which would be an obvious win. I still think that we give Obj-C examples the same prominence, though, which means finding a solution that works for both (or a second Obj-C solution). |
|
Added unit tests for each of the inline examples. Should help catch any breaking changes to the examples. |
let layer = MGLLineStyleLayer(identifier: "trails-path", source: trails) | ||
layer.sourceLayerIdentifier = "trails" | ||
layer.lineWidth = MGLStyleValue(base: 1.5, stops: [ | ||
14: MGLStyleValue(rawValue: 2), |
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.
Missed the indentation here.
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.
Bah, copy and paste fail.
@mayagao is reworking the jazzy theme elsewhere, but here’s how these examples look now: 👍 |
|
||
```swift | ||
// Define tileset | ||
let tileset = MGLTileSet(tileURLTemplates: ["https://example.com/raster-tiles/{z}/{x}/{y}.png"]) |
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.
#7377 removed MGLTileSet in favor of direct source initializers with option dictionaries. Sorry to keep shifting the goalposts on you. There’s still an attribution option that takes an HTML string, but now there’s also an option that takes structured MGLAttributionInfo objects.
mapView.style.addSource(source) | ||
``` | ||
|
||
@see <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson">The |
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 links become redundant once you merge in (or rebase atop) #7377.
layer.predicate = NSPredicate(format: "%K == %@", "ethnicity", "ewok") | ||
mapView.style().add(layer) | ||
layer.predicate = NSPredicate(format: "%K == %@", "marital-status", "married") | ||
mapView.style.addLayer(layer) |
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 it's interesting that making the style a property instead of returning it in a method had this effect. Is this a case for NS_SWIFT_NAME
?
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.
You’re referring to the change from add(_:)
to addLayer(_:)
? I was curious about that too. It’s unlikely to be due to the change from MGLMapView.style()
to MGLMapView.style
. The more likely cause is that #6097 added layers
and sources
properties, or that we started using Xcode 8.2 instead 8.1, but I’m not really sure exactly what introduced the change.
As for whether we should use NS_SWIFT_NAME
, I’m ambivalent. If we do, we should make sure to refine all the layer and source methods on this class.
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.
Using a reduced project, I found that indeed the addition of a sources
property changed add(_:)
to addSource(_:)
, and likewise the addition of a layers
property changed add(_:)
to addLayer(_:)
. This appears to be consistent with Cocoa APIs such as -[UIView addConstraint:]
, which, due to the presence of UIView.constraints
, is bridged as addConstraint(_:)
.
6f5a8bc
to
920b0b8
Compare
Last commit should cover all of the outstanding changes needed. Lmk if you see anything else that could use an inline example. /cc @1ec5 @friedbunny |
|
||
### Example ### | ||
|
||
```swift |
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 style layer headers are generated by platform/darwin/scripts/generate-style-code.js, so the next time someone runs make style-code-darwin
, these changes will get blown away. The approach I took in #7475 was to override a property on each layer’s entry in the style specification JSON to provide documentation. Unfortunately, it’d be quite inconvenient to embed a multiline code example inside a JSON string.
Let’s have the overridden style specification JSON name the relevant function within MGLDocumentationExampleTests. generate-style-code.js can then extract that function using a regular expression and insert it into the generated header.
fd2fa3b
to
2b7863d
Compare
Last set of commits after rebasing on release-3.4.0 adds a run script that will extract the examples from /cc @1ec5 @friedbunny |
jazzy \ | ||
--config platform/ios/jazzy.yml \ | ||
--sdk iphonesimulator \ | ||
--github-file-prefix https://github.com/mapbox/mapbox-gl-native/tree/${BRANCH} \ | ||
--module-version ${SHORT_VERSION} \ | ||
--framework-root ${FRAMEWORK_PATH} \ | ||
--umbrella-header "${FRAMEWORK_PATH}/Headers/Mapbox.h" \ |
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.
By default, Jazzy generates its docs from the project source, which excludes any changes by the run script, so I've added an optional FRAMEWORK_PATH
env variable that by default searches for the dynamic framework output by make iframework
.
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 someone runs make idocument
after building only the static framework, this will probably error out. It’s a workflow I don’t personally use, but I suppose someone might end up doing that after reading DEVELOPING.md. I wonder if we could check for the existence of the dynamic framework and, if it doesn’t exist, fall back to the static framework’s Headers folder. But that’s all fodder for other PRs.
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.
Beautiful!
@@ -24,6 +24,9 @@ NS_ASSUME_NONNULL_BEGIN | |||
(<var>extent</var> × 2) − 1, inclusive. Any vector style | |||
layer initialized with a vector source must have a non-`nil` value in its | |||
`sourceLayerIdentifier` property. | |||
|
|||
<!--EXAMPLE: MGLVectorSource--> | |||
``` |
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.
Stray backticks.
jazzy \ | ||
--config platform/ios/jazzy.yml \ | ||
--sdk iphonesimulator \ | ||
--github-file-prefix https://github.com/mapbox/mapbox-gl-native/tree/${BRANCH} \ | ||
--module-version ${SHORT_VERSION} \ | ||
--framework-root ${FRAMEWORK_PATH} \ | ||
--umbrella-header "${FRAMEWORK_PATH}/Headers/Mapbox.h" \ |
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 someone runs make idocument
after building only the static framework, this will probably error out. It’s a workflow I don’t personally use, but I suppose someone might end up doing that after reading DEVELOPING.md. I wonder if we could check for the existence of the dynamic framework and, if it doesn’t exist, fall back to the static framework’s Headers folder. But that’s all fodder for other PRs.
); | ||
runOnlyForDeploymentPostprocessing = 0; | ||
shellPath = /bin/sh; | ||
shellScript = "node \"${SRCROOT}/scripts/add-examples-to-docs.js\""; |
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 you declare add-examples-to-docs.js and MGLDocumentationExampleTests.swift as input files to this build phase and the modified headers as output files, Xcode will avoid running this script when none of those files have changed. But since that’d be one more place that would have to get updated every time we add an example, it’s fine to leave the input and output files out.
Examples for:
MGLGeoJSONSource
MGLVectorSource
MGLRasterSource
MGLCircleStyleLayer
MGLFillStyleLayer
MGLLineStyleLayer
MGLSymbolStyleLayer
MGLVectorStyleLayer.predicate
Swift 3 for now. We can add Objective-C inline or a link to the respective examples on http://mapbox.com/ios-sdk/examples later.