Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

[ios] Add inline examples in documentation #7337

Merged
merged 9 commits into from
Dec 21, 2016

Conversation

ericrwolfe
Copy link
Contributor

Examples for:

  • Sources
    • MGLGeoJSONSource
    • MGLVectorSource
    • MGLRasterSource
  • Layers
    • 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.

@mention-bot
Copy link

@ericrwolfe, thanks for your PR! By analyzing this pull request, we identified @incanus, @1ec5 and @boundsj to be potential reviewers.

@ericrwolfe ericrwolfe added this to the ios-v3.4.0 milestone Dec 8, 2016
Copy link
Contributor

@1ec5 1ec5 left a 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)
Copy link
Contributor

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")
Copy link
Contributor

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)
Copy link
Contributor

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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#7145 renamed this initializer to MGLGeoJSONSource(identifier:shape:options:), and #7334 would go a step further, renaming the class to MGLShapeSource. It still makes sense to demonstrate creating the source with a feature rather than an unadorned shape.

```swift
let meters = 1500.0
let layer = MGLLineStyleLayer(identifier: "contour", source: contours)
layer.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [
Copy link
Contributor

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?

@1ec5 1ec5 mentioned this pull request Dec 8, 2016
@friedbunny
Copy link
Contributor

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?

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).

@boundsj
Copy link
Contributor

boundsj commented Dec 10, 2016

MGLGeoJSONSource was renamed to MGLShapeSource in #7334

@ericrwolfe
Copy link
Contributor Author

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),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missed the indentation here.

Copy link
Contributor Author

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.

@friedbunny
Copy link
Contributor

friedbunny commented Dec 13, 2016

@mayagao is reworking the jazzy theme elsewhere, but here’s how these examples look now:

screen shot 2016-12-13 at 1 54 37 pm

👍


```swift
// Define tileset
let tileset = MGLTileSet(tileURLTemplates: ["https://example.com/raster-tiles/{z}/{x}/{y}.png"])
Copy link
Contributor

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
Copy link
Contributor

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)
Copy link
Contributor

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?

Copy link
Contributor

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.

Copy link
Contributor

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(_:).

@ericrwolfe
Copy link
Contributor Author

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
Copy link
Contributor

@1ec5 1ec5 Dec 19, 2016

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.

@ericrwolfe
Copy link
Contributor Author

ericrwolfe commented Dec 21, 2016

Last set of commits after rebasing on release-3.4.0 adds a run script that will extract the examples from MGLDocumentationExampleTests and replaces the corresponding comment token in the header docs.

/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" \
Copy link
Contributor Author

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.

Copy link
Contributor

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.

Copy link
Contributor

@1ec5 1ec5 left a 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>&nbsp;×&nbsp;2)&nbsp;−&nbsp;1, inclusive. Any vector style
layer initialized with a vector source must have a non-`nil` value in its
`sourceLayerIdentifier` property.

<!--EXAMPLE: MGLVectorSource-->
```
Copy link
Contributor

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" \
Copy link
Contributor

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\"";
Copy link
Contributor

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.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants