diff options
23 files changed, 437 insertions, 205 deletions
@@ -275,6 +275,10 @@ idocument: style-code-darwin: node platform/darwin/scripts/generate-style-code.js + node platform/darwin/scripts/update-examples.js + +darwin-update-examples: + node platform/darwin/scripts/update-examples.js endif #### Linux targets ##################################################### diff --git a/platform/darwin/scripts/update-examples.js b/platform/darwin/scripts/update-examples.js new file mode 100644 index 0000000000..2aeb80e139 --- /dev/null +++ b/platform/darwin/scripts/update-examples.js @@ -0,0 +1,152 @@ +'use strict'; + +const fs = require('fs'); +const execFileSync = require('child_process').execFileSync; +const _ = require('lodash'); + +const examplesSrc = fs.readFileSync('platform/darwin/test/MGLDocumentationExampleTests.swift', 'utf8'); + +// Regex extracts the following block +// /** Front matter to describe the example. **/ +// func testMGLClass$member() { +// ... +// //#-example-code +// let sampleCode: String? +// //#-end-example-code +// ... +// } +// +// into the following regex groups: +// 1 (test method name): "MGLClass" or "MGLClass$member" or "MGLClass$initWithArg_anotherArg_" +// 2 (indentation): " " +// 3 (sample code): "let sampleCode: String?" +const exampleRegex = /func test([\w$]+)\s*\(\)\s*\{[^]*?\n([ \t]+)\/\/#-example-code\n([^]+?)\n\2\/\/#-end-example-code\n/gm; + +/** + * Returns the given source with example code inserted into the documentation + * comment for the symbol at the given one-based line. + * + * @param {String} src Source code to insert the example code into. + * @param {Number} line One-based line number of the symbol being documented. + * @param {String} exampleCode Example code to insert. + * @returns {String} `src` with `exampleCode` inserted just above `line`. + */ +function completeSymbolInSource(src, line, exampleCode) { + // Split the file contents right before the symbol declaration (but after its documentation comment). + let srcUpToSymbol = src.split('\n', line - 1).join('\n'); + let srcFromSymbol = src.substr(srcUpToSymbol.length); + + // Match the documentation comment block that is not followed by the beginning or end of a declaration. + let commentMatch = srcUpToSymbol.match(/\/\*\*\s*(?:[^*]|\*(?!\/))+?\s*\*\/[^;{}]*?$/); + + // Replace the Swift code block with the test method’s contents. + let completedComment = commentMatch[0].replace(/^([ \t]*)```swift\n[^]*?```/m, function (m, indentation) { + // Apply the original indentation to each line. + return ('```swift\n' + exampleCode + '\n```').replace(/^/gm, indentation); + }); + + // Splice the modified comment into the overall file contents. + srcUpToSymbol = (srcUpToSymbol.substr(0, commentMatch.index) + completedComment + + srcUpToSymbol.substr(commentMatch.index + commentMatch[0].length)); + return srcUpToSymbol + srcFromSymbol; +} + +let examples = {}; +let match; +while ((match = exampleRegex.exec(examplesSrc)) !== null) { + let testMethodName = match[1], + indentation = match[2], + exampleCode = match[3]; + + // Trim leading whitespace from the example code. + exampleCode = exampleCode.replace(new RegExp('^' + indentation, 'gm'), ''); + + examples[testMethodName] = exampleCode; +} + +function completeExamples(os) { + console.log(`Installing ${os} SDK examples…`); + + let sdk = os === 'iOS' ? 'iphonesimulator' : 'macosx'; + let sysroot = execFileSync('xcrun', ['--show-sdk-path', '--sdk', sdk]).toString().trim(); + let umbrellaPath = `platform/${os.toLowerCase()}/src/Mapbox.h`; + let docArgs = ['doc', '--objc', umbrellaPath, '--', + '-x', 'objective-c', '-I', 'platform/darwin/src/', '-isysroot', sysroot]; + let docStr = execFileSync('sourcekitten', docArgs).toString().trim(); + let docJson = JSON.parse(docStr); + _.forEach(docJson, function (result) { + _.forEach(result, function (structure, path) { + let src; + let newSrc; + // Recursively search for code blocks in documentation comments and populate + // them with example code from the test methods. Find and replace the code + // blocks in reverse to keep the SourceKitten line numbers accurate. + _.forEachRight(structure['key.substructure'], function completeSubstructure(substructure, idx, substructures, symbolPath) { + if (!symbolPath) { + symbolPath = [substructure['key.name']]; + } + _.forEachRight(substructure['key.substructure'], function (substructure, idx, substructures) { + completeSubstructure(substructure, idx, substructures, _.concat(symbolPath, substructure['key.name'])); + }); + + let comment = substructure['key.doc.comment']; + if (!comment || !comment.match(/^(?:\s*)```swift\n/m)) { + return; + } + + // Lazily read in the existing file. + if (!src) { + newSrc = src = fs.readFileSync(path, 'utf8'); + } + + // Get the contents of the test method whose name matches the symbol path. + let testMethodName = symbolPath.join('$').replace(/$[+-]/, '').replace(/:/g, '_'); + let example = examples[testMethodName]; + if (!example) { + console.error(`MGLDocumentationExampleTests.${testMethodName}() not found.`); + process.exit(1); + } + + // Resolve conditional compilation blocks. + example = example.replace(/^(\s*)#if\s+os\((iOS|macOS)\)\n([^]*?)(?:^\1#else\n([^]*?))?^\1#endif\n/gm, + function (m, indentation, ifOs, ifCase, elseCase) { + return (os === ifOs ? ifCase : elseCase).replace(new RegExp('^ ', 'gm'), ''); + }); + + // Insert the test method contents into the documentation comment just + // above the substructure. + let startLine = substructure['key.parsed_scope.start']; + newSrc = completeSymbolInSource(newSrc, startLine, example); + }); + + if (!src) { + return; + } + + // Write out the modified file contents. + if (src === newSrc) { + console.log('Skipping', path); + } else { + console.log('Updating', path); + if (['0', 'false'].indexOf(process.env.DRY_RUN || '0') !== -1) { + fs.writeFileSync(path, newSrc); + } + } + }); + }); +} + +function ensureSourceKittenIsInstalled() { + try { + execFileSync('which', ['sourcekitten']); + } catch (e) { + console.log(`Installing SourceKitten via Homebrew…`); + execFileSync('brew', ['install', 'sourcekitten']); + } +} + +ensureSourceKittenIsInstalled(); + +// Where a particular comment is part of both SDKs, prefer the iOS example. +completeExamples('macOS'); +completeExamples('iOS'); diff --git a/platform/darwin/src/MGLCircleStyleLayer.h b/platform/darwin/src/MGLCircleStyleLayer.h index b57e245da2..d2e043e22a 100644 --- a/platform/darwin/src/MGLCircleStyleLayer.h +++ b/platform/darwin/src/MGLCircleStyleLayer.h @@ -59,8 +59,21 @@ typedef NS_ENUM(NSUInteger, MGLCircleTranslateAnchor) { otherwise, find it using the `MGLStyle.layers` property. You can also create a new circle style layer and add it to the style using a method such as `-[MGLStyle addLayer:]`. - - <!--EXAMPLE: MGLCircleStyleLayer--> + + ### Example + + ```swift + let layer = MGLCircleStyleLayer(identifier: "circles", source: population) + layer.sourceLayerIdentifier = "population" + layer.circleColor = MGLStyleValue(rawValue: .green) + layer.circleRadius = MGLStyleValue(interpolationBase: 1.75, stops: [ + 12: MGLStyleValue(rawValue: 2), + 22: MGLStyleValue(rawValue: 180) + ]) + layer.circleOpacity = MGLStyleValue(rawValue: 0.7) + layer.predicate = NSPredicate(format: "%K == %@", "marital-status", "married") + mapView.style.addLayer(layer) + ``` */ @interface MGLCircleStyleLayer : MGLVectorStyleLayer diff --git a/platform/darwin/src/MGLFillStyleLayer.h b/platform/darwin/src/MGLFillStyleLayer.h index 2638e85aa5..184f44ef65 100644 --- a/platform/darwin/src/MGLFillStyleLayer.h +++ b/platform/darwin/src/MGLFillStyleLayer.h @@ -37,8 +37,16 @@ typedef NS_ENUM(NSUInteger, MGLFillTranslateAnchor) { otherwise, find it using the `MGLStyle.layers` property. You can also create a new fill style layer and add it to the style using a method such as `-[MGLStyle addLayer:]`. - - <!--EXAMPLE: MGLFillStyleLayer--> + + ### Example + + ```swift + let layer = MGLFillStyleLayer(identifier: "parks", source: parks) + layer.sourceLayerIdentifier = "parks" + layer.fillColor = MGLStyleValue(rawValue: .green) + layer.predicate = NSPredicate(format: "type == %@", "national-park") + mapView.style.addLayer(layer) + ``` */ @interface MGLFillStyleLayer : MGLVectorStyleLayer diff --git a/platform/darwin/src/MGLLineStyleLayer.h b/platform/darwin/src/MGLLineStyleLayer.h index c3371aa2a7..f049ae1db1 100644 --- a/platform/darwin/src/MGLLineStyleLayer.h +++ b/platform/darwin/src/MGLLineStyleLayer.h @@ -87,8 +87,21 @@ typedef NS_ENUM(NSUInteger, MGLLineTranslateAnchor) { otherwise, find it using the `MGLStyle.layers` property. You can also create a new line style layer and add it to the style using a method such as `-[MGLStyle addLayer:]`. - - <!--EXAMPLE: MGLLineStyleLayer--> + + ### Example + + ```swift + let layer = MGLLineStyleLayer(identifier: "trails-path", source: trails) + layer.sourceLayerIdentifier = "trails" + layer.lineWidth = MGLStyleValue(interpolationBase: 1.5, stops: [ + 14: MGLStyleValue(rawValue: 2), + 18: MGLStyleValue(rawValue: 20), + ]) + layer.lineColor = MGLStyleValue(rawValue: .brown) + layer.lineCap = MGLStyleValue(rawValue: NSValue(mglLineCap: .round)) + layer.predicate = NSPredicate(format: "%K == %@", "trail-type", "mountain-biking") + mapView.style.addLayer(layer) + ``` */ @interface MGLLineStyleLayer : MGLVectorStyleLayer diff --git a/platform/darwin/src/MGLRasterSource.h b/platform/darwin/src/MGLRasterSource.h index 3efd84f383..cd95e85c5b 100644 --- a/platform/darwin/src/MGLRasterSource.h +++ b/platform/darwin/src/MGLRasterSource.h @@ -35,8 +35,19 @@ extern const MGLTileSourceOption MGLTileSourceOptionTileSize; `MGLRasterSource` object that you can use to initialize new style layers. You can also add and remove sources dynamically using methods such as `-[MGLStyle addSource:]` and `-[MGLStyle sourceWithIdentifier:]`. - - <!--EXAMPLE: MGLRasterSource--> + + ### Example + + ```swift + let source = MGLRasterSource(identifier: "clouds", tileURLTemplates: ["https://example.com/raster-tiles/{z}/{x}/{y}.png"], options: [ + .minimumZoomLevel: 9, + .maximumZoomLevel: 16, + .tileSize: 512, + .attributionInfos: [ + MGLAttributionInfo(title: NSAttributedString(string: "© Mapbox"), url: URL(string: "http://mapbox.com")) + ] + ]) + mapView.style.addSource(source) ``` */ @interface MGLRasterSource : MGLTileSource diff --git a/platform/darwin/src/MGLRasterStyleLayer.h b/platform/darwin/src/MGLRasterStyleLayer.h index 7b53eebf96..45ab25a8f1 100644 --- a/platform/darwin/src/MGLRasterStyleLayer.h +++ b/platform/darwin/src/MGLRasterStyleLayer.h @@ -23,8 +23,14 @@ NS_ASSUME_NONNULL_BEGIN otherwise, find it using the `MGLStyle.layers` property. You can also create a new raster style layer and add it to the style using a method such as `-[MGLStyle addLayer:]`. - - <!--EXAMPLE: MGLRasterStyleLayer--> + + ### Example + + ```swift + let layer = MGLRasterStyleLayer(identifier: "clouds", source: source) + layer.rasterOpacity = MGLStyleValue(rawValue: 0.5) + mapView.style.addLayer(layer) + ``` */ @interface MGLRasterStyleLayer : MGLForegroundStyleLayer diff --git a/platform/darwin/src/MGLShapeSource.h b/platform/darwin/src/MGLShapeSource.h index 0eb454437b..30e299d2b1 100644 --- a/platform/darwin/src/MGLShapeSource.h +++ b/platform/darwin/src/MGLShapeSource.h @@ -74,8 +74,18 @@ extern const MGLShapeSourceOption MGLShapeSourceOptionSimplificationTolerance; Any vector style layer initialized with a shape source should have a `nil` value in its `sourceLayerIdentifier` property. - - <!--EXAMPLE: MGLShapeSource--> + + ### Example + + ```swift + var coordinates: [CLLocationCoordinate2D] = [ + CLLocationCoordinate2D(latitude: 37.77, longitude: -122.42), + CLLocationCoordinate2D(latitude: 38.91, longitude: -77.04), + ] + let polyline = MGLPolylineFeature(coordinates: &coordinates, count: UInt(coordinates.count)) + let shape = MGLShapeCollectionFeature(shapes: [polyline]) + let source = MGLShapeSource(identifier: "lines", shape: shape, options: nil) + mapView.style.addSource(source) ``` */ @interface MGLShapeSource : MGLSource diff --git a/platform/darwin/src/MGLStyleLayer.h.ejs b/platform/darwin/src/MGLStyleLayer.h.ejs index ac95022ea7..17529b8f9d 100644 --- a/platform/darwin/src/MGLStyleLayer.h.ejs +++ b/platform/darwin/src/MGLStyleLayer.h.ejs @@ -67,8 +67,11 @@ typedef NS_ENUM(NSUInteger, MGL<%- camelize(property.name) %>) { otherwise, find it using the `MGLStyle.layers` property. You can also create a new <%- type %> style layer and add it to the style using a method such as `-[MGLStyle addLayer:]`. - - <!--EXAMPLE: MGL<%- camelize(type) %>StyleLayer--> + + ### Example + + ```swift + ``` */ <% } -%> @interface MGL<%- camelize(type) %>StyleLayer : MGL<%- diff --git a/platform/darwin/src/MGLSymbolStyleLayer.h b/platform/darwin/src/MGLSymbolStyleLayer.h index 052d67617a..ca34698159 100644 --- a/platform/darwin/src/MGLSymbolStyleLayer.h +++ b/platform/darwin/src/MGLSymbolStyleLayer.h @@ -260,8 +260,21 @@ typedef NS_ENUM(NSUInteger, MGLTextTranslateAnchor) { otherwise, find it using the `MGLStyle.layers` property. You can also create a new symbol style layer and add it to the style using a method such as `-[MGLStyle addLayer:]`. - - <!--EXAMPLE: MGLSymbolStyleLayer--> + + ### Example + + ```swift + let layer = MGLSymbolStyleLayer(identifier: "coffeeshops", source: pois) + layer.sourceLayerIdentifier = "pois" + layer.iconImageName = MGLStyleValue(rawValue: "coffee") + layer.iconScale = MGLStyleValue(rawValue: 0.5) + layer.textField = MGLStyleValue(rawValue: "{name}") + layer.textTranslate = MGLStyleValue(rawValue: NSValue(cgVector: CGVector(dx: 10, dy: 0))) + layer.textJustification = MGLStyleValue(rawValue: NSValue(mglTextJustification: .left)) + layer.textAnchor = MGLStyleValue(rawValue: NSValue(mglTextAnchor: .left)) + layer.predicate = NSPredicate(format: "%K == %@", "venue-type", "coffee") + mapView.style.addLayer(layer) + ``` */ @interface MGLSymbolStyleLayer : MGLVectorStyleLayer diff --git a/platform/darwin/src/MGLVectorSource.h b/platform/darwin/src/MGLVectorSource.h index ba8f075b5a..aa867adb6c 100644 --- a/platform/darwin/src/MGLVectorSource.h +++ b/platform/darwin/src/MGLVectorSource.h @@ -24,8 +24,19 @@ 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--> + + ### Example + + ```swift + let source = MGLVectorSource(identifier: "pois", tileURLTemplates: ["https://example.com/vector-tiles/{z}/{x}/{y}.mvt"], options: [ + .minimumZoomLevel: 9, + .maximumZoomLevel: 16, + .attributionInfos: [ + MGLAttributionInfo(title: NSAttributedString(string: "© Mapbox"), url: URL(string: "http://mapbox.com")) + ] + ]) + mapView.style.addSource(source) + ``` */ @interface MGLVectorSource : MGLTileSource diff --git a/platform/darwin/src/MGLVectorStyleLayer.h b/platform/darwin/src/MGLVectorStyleLayer.h index 50e56d8643..d33494c3e4 100644 --- a/platform/darwin/src/MGLVectorStyleLayer.h +++ b/platform/darwin/src/MGLVectorStyleLayer.h @@ -59,7 +59,10 @@ NS_ASSUME_NONNULL_BEGIN <li><code>NSContainsPredicateOperatorType</code> (<code>CONTAINS</code>)</li> </ul> - To test whether a feature has or lacks a specific attribute, compare the attribute to `NULL` or `NIL`. Predicates created using the `+[NSPredicate predicateWithValue:]` method are also supported. String operators and custom operators are not supported. + To test whether a feature has or lacks a specific attribute, compare the + attribute to `NULL` or `NIL`. Predicates created using the + `+[NSPredicate predicateWithValue:]` method are also supported. String + operators and custom operators are not supported. For details about the predicate format string syntax, consult the “Predicate Format String Syntax” chapter of the @@ -130,8 +133,19 @@ NS_ASSUME_NONNULL_BEGIN style attributes and also `hyphen-minus` and `tag:subtag`. However, you must use `%K` in the format string to represent these variables: `@"%K == 'LineString'", @"$type"`. - - <!--EXAMPLE: MGLVectorStyleLayer.predicate--> + + ### Example + + To filter the layer to include only the features whose `index` attribute is 5 + or 10 and whose `ele` attribute is at least 1,500, you could create an + `NSCompoundPredicate` along these lines: + + ```swift + let layer = MGLLineStyleLayer(identifier: "contour", source: terrain) + layer.sourceLayerIdentifier = "contours" + layer.predicate = NSPredicate(format: "(index == 5 || index == 10) && ele >= 1500.0") + mapView.style.addLayer(layer) + ``` */ @property (nonatomic, nullable) NSPredicate *predicate; diff --git a/platform/ios/test/MGLDocumentationExampleTests.swift b/platform/darwin/test/MGLDocumentationExampleTests.swift index 0d97cfeb35..9243924328 100644 --- a/platform/ios/test/MGLDocumentationExampleTests.swift +++ b/platform/darwin/test/MGLDocumentationExampleTests.swift @@ -1,42 +1,54 @@ import XCTest import Mapbox -import UIKit +#if os(iOS) + import UIKit +#else + import Cocoa +#endif /** Test cases that ensure the inline examples in the project documentation compile. - There is a run script build phase for the dynamic & static iOS targets - that invokes `/platform/ios/scrips/add-examples-to-docs.js`. This script - will pull example code out of this test file and replace the corresponding - placeholder comment in the built header files. - - Adding examples: - 1. Add a test case below + To add an example: + 1. Add a test case named in the form testMGLClass or testMGLClass$method. 2. Wrap the code you'd like to appear in the documentation within the following comment blocks: - `/*---BEGIN EXAMPLE: ExampleToken---*/` + ``` + //#-example-code ... - `/*---END EXAMPLE---*/` - 3. Insert a comment `<!--EXAMPLE: ExampleToken-->` inside the header file - where you'd like the example code to be inserted + //#-end-example-code + ``` + 3. Insert an empty Swift code block inside the header file where you'd like the + example code to be inserted. + 4. Run `make darwin-update-examples` to extract example code from the test + method below and insert it into the header. */ -class MGLDocumentationExampleTests: XCTestCase { +class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate { var mapView: MGLMapView! + var styleLoadingExpectation: XCTestExpectation! override func setUp() { super.setUp() - self.mapView = MGLMapView(frame: CGRect(x: 0, y: 0, width: 256, height: 256)) + let styleURL = Bundle(for: MGLDocumentationExampleTests.self).url(forResource: "one-liner", withExtension: "json") + mapView = MGLMapView(frame: CGRect(x: 0, y: 0, width: 256, height: 256), styleURL: styleURL) + mapView.delegate = self + styleLoadingExpectation = expectation(description: "Map view should finish loading style") + waitForExpectations(timeout: 1, handler: nil) } override func tearDown() { - self.mapView = nil + mapView = nil + styleLoadingExpectation = nil super.tearDown() } + + func mapView(_ mapView: MGLMapView, didFinishLoading style: MGLStyle) { + styleLoadingExpectation.fulfill() + } - // MGLShapeSource - func testMGLShapeSourceExample() { - /*---BEGIN EXAMPLE: MGLShapeSource---*/ + func testMGLShapeSource() { + //#-example-code var coordinates: [CLLocationCoordinate2D] = [ CLLocationCoordinate2D(latitude: 37.77, longitude: -122.42), CLLocationCoordinate2D(latitude: 38.91, longitude: -77.04), @@ -45,14 +57,13 @@ class MGLDocumentationExampleTests: XCTestCase { let shape = MGLShapeCollectionFeature(shapes: [polyline]) let source = MGLShapeSource(identifier: "lines", shape: shape, options: nil) mapView.style.addSource(source) - /*---END EXAMPLE---*/ + //#-end-example-code XCTAssertNotNil(mapView.style.source(withIdentifier: "lines")) } - // MGLRasterSource - func testMGLRasterSourceExample() { - /*---BEGIN EXAMPLE: MGLRasterSource---*/ + func testMGLRasterSource() { + //#-example-code let source = MGLRasterSource(identifier: "clouds", tileURLTemplates: ["https://example.com/raster-tiles/{z}/{x}/{y}.png"], options: [ .minimumZoomLevel: 9, .maximumZoomLevel: 16, @@ -62,14 +73,13 @@ class MGLDocumentationExampleTests: XCTestCase { ] ]) mapView.style.addSource(source) - /*---END EXAMPLE---*/ + //#-end-example-code XCTAssertNotNil(mapView.style.source(withIdentifier: "clouds")) } - // MGLVectorSource func testMGLVectorSource() { - /*---BEGIN EXAMPLE: MGLVectorSource---*/ + //#-example-code let source = MGLVectorSource(identifier: "pois", tileURLTemplates: ["https://example.com/vector-tiles/{z}/{x}/{y}.mvt"], options: [ .minimumZoomLevel: 9, .maximumZoomLevel: 16, @@ -78,20 +88,19 @@ class MGLDocumentationExampleTests: XCTestCase { ] ]) mapView.style.addSource(source) - /*---END EXAMPLE---*/ + //#-end-example-code XCTAssertNotNil(mapView.style.source(withIdentifier: "pois")) } - // MGLCircleStyleLayer - func testMGLCircleStyleLayerExample() { + func testMGLCircleStyleLayer() { let population = MGLVectorSource(identifier: "population", configurationURL: URL(string: "https://example.com/style.json")!) mapView.style.addSource(population) - /*---BEGIN EXAMPLE: MGLCircleStyleLayer---*/ + //#-example-code let layer = MGLCircleStyleLayer(identifier: "circles", source: population) layer.sourceLayerIdentifier = "population" - layer.circleColor = MGLStyleValue(rawValue: UIColor.green) + layer.circleColor = MGLStyleValue(rawValue: .green) layer.circleRadius = MGLStyleValue(interpolationBase: 1.75, stops: [ 12: MGLStyleValue(rawValue: 2), 22: MGLStyleValue(rawValue: 180) @@ -99,105 +108,101 @@ class MGLDocumentationExampleTests: XCTestCase { layer.circleOpacity = MGLStyleValue(rawValue: 0.7) layer.predicate = NSPredicate(format: "%K == %@", "marital-status", "married") mapView.style.addLayer(layer) - /*---END EXAMPLE---*/ + //#-end-example-code XCTAssertNotNil(mapView.style.layer(withIdentifier: "circles")) } - // MGLLineStyleLayer - func testMGLLineStyleLayerExample() { + func testMGLLineStyleLayer() { let trails = MGLVectorSource(identifier: "trails", configurationURL: URL(string: "https://example.com/style.json")!) mapView.style.addSource(trails) - /*---BEGIN EXAMPLE: MGLLineStyleLayer---*/ + //#-example-code let layer = MGLLineStyleLayer(identifier: "trails-path", source: trails) layer.sourceLayerIdentifier = "trails" layer.lineWidth = MGLStyleValue(interpolationBase: 1.5, stops: [ 14: MGLStyleValue(rawValue: 2), 18: MGLStyleValue(rawValue: 20), ]) - layer.lineColor = MGLStyleValue(rawValue: UIColor.brown) + layer.lineColor = MGLStyleValue(rawValue: .brown) layer.lineCap = MGLStyleValue(rawValue: NSValue(mglLineCap: .round)) layer.predicate = NSPredicate(format: "%K == %@", "trail-type", "mountain-biking") mapView.style.addLayer(layer) - /*---END EXAMPLE---*/ + //#-end-example-code XCTAssertNotNil(mapView.style.layer(withIdentifier: "trails-path")) } - // MGLFillStyleLayer - func testMGLFillStyleLayerExample() { + func testMGLFillStyleLayer() { let parks = MGLVectorSource(identifier: "parks", configurationURL: URL(string: "https://example.com/style.json")!) mapView.style.addSource(parks) - /*---BEGIN EXAMPLE: MGLFillStyleLayer---*/ + //#-example-code let layer = MGLFillStyleLayer(identifier: "parks", source: parks) layer.sourceLayerIdentifier = "parks" - layer.fillColor = MGLStyleValue(rawValue: UIColor.green) + layer.fillColor = MGLStyleValue(rawValue: .green) layer.predicate = NSPredicate(format: "type == %@", "national-park") mapView.style.addLayer(layer) - /*---END EXAMPLE---*/ + //#-end-example-code XCTAssertNotNil(mapView.style.layer(withIdentifier: "parks")) } - // MGLSymbolStyleLayer - func testMGLSymbolStyleLayerExample() { + func testMGLSymbolStyleLayer() { let pois = MGLVectorSource(identifier: "pois", configurationURL: URL(string: "https://example.com/style.json")!) mapView.style.addSource(pois) - /*---BEGIN EXAMPLE: MGLSymbolStyleLayer---*/ + //#-example-code let layer = MGLSymbolStyleLayer(identifier: "coffeeshops", source: pois) layer.sourceLayerIdentifier = "pois" layer.iconImageName = MGLStyleValue(rawValue: "coffee") layer.iconScale = MGLStyleValue(rawValue: 0.5) layer.textField = MGLStyleValue(rawValue: "{name}") - layer.textTranslate = MGLStyleValue(rawValue: NSValue(cgVector: CGVector(dx: 10, dy: 0))) + #if os(macOS) + var vector = CGVector(dx: 10, dy: 0) + layer.textTranslate = MGLStyleValue(rawValue: NSValue(bytes: &vector, objCType: "{CGVector=dd}")) + #else + layer.textTranslate = MGLStyleValue(rawValue: NSValue(cgVector: CGVector(dx: 10, dy: 0))) + #endif layer.textJustification = MGLStyleValue(rawValue: NSValue(mglTextJustification: .left)) layer.textAnchor = MGLStyleValue(rawValue: NSValue(mglTextAnchor: .left)) layer.predicate = NSPredicate(format: "%K == %@", "venue-type", "coffee") mapView.style.addLayer(layer) - /*---END EXAMPLE---*/ + //#-end-example-code XCTAssertNotNil(mapView.style.layer(withIdentifier: "coffeeshops")) } - // MGLRasterStyleLayer func testMGLRasterStyleLayer() { let source = MGLRasterSource(identifier: "clouds", tileURLTemplates: ["https://example.com/raster-tiles/{z}/{x}/{y}.png"], options: [ .minimumZoomLevel: 9, .maximumZoomLevel: 16, .tileSize: 512, .attributionInfos: [ - MGLAttributionInfo(title: NSAttributedString(string: "© Mapbox"), url: URL(string: "http://mapbox.com")) + MGLAttributionInfo(title: NSAttributedString(string: "© Mapbox"), url: URL(string: "http://mapbox.com")) ] ]) mapView.style.addSource(source) - /*---BEGIN EXAMPLE: MGLRasterStyleLayer---*/ + //#-example-code let layer = MGLRasterStyleLayer(identifier: "clouds", source: source) layer.rasterOpacity = MGLStyleValue(rawValue: 0.5) mapView.style.addLayer(layer) - /*---END EXAMPLE---*/ + //#-end-example-code XCTAssertNotNil(mapView.style.layer(withIdentifier: "clouds")) } - // MGLVectorStyleLayer.predicate - func testMGLVectorStyleLayer() { + func testMGLVectorStyleLayer$predicate() { let terrain = MGLVectorSource(identifier: "terrain", configurationURL: URL(string: "https://example.com/style.json")!) mapView.style.addSource(terrain) - /*---BEGIN EXAMPLE: MGLVectorStyleLayer.predicate---*/ - /** - To create a filter with the logic `(index == 10 || index == 5) && ele >= 200`, - you could create a predicate using `NSCompoundPredicate` along these lines: - */ + //#-example-code let layer = MGLLineStyleLayer(identifier: "contour", source: terrain) layer.sourceLayerIdentifier = "contours" - layer.predicate = NSPredicate(format: "(index == 10 || index == 5) && ele >= 1500.0") + layer.predicate = NSPredicate(format: "(index == 5 || index == 10) && ele >= 1500.0") mapView.style.addLayer(layer) - /*---END EXAMPLE---*/ + //#-end-example-code XCTAssertNotNil(mapView.style.layer(withIdentifier: "contour")) } diff --git a/platform/darwin/test/MGLStyleLayerTests.m b/platform/darwin/test/MGLStyleLayerTests.m index 590d6eda7f..66bdc0df0b 100644 --- a/platform/darwin/test/MGLStyleLayerTests.m +++ b/platform/darwin/test/MGLStyleLayerTests.m @@ -10,6 +10,7 @@ - (void)setUp { [super setUp]; + [MGLAccountManager setAccessToken:@"pk.feedcafedeadbeefbadebede"]; #if TARGET_OS_IPHONE UIApplication *app = [UIApplication sharedApplication]; UIViewController *vc = [[UIViewController alloc] init]; @@ -19,12 +20,17 @@ [vc.view addSubview:_mapView]; _mapView.delegate = self; #else - [MGLAccountManager setAccessToken:@"pk.feedcafedeadbeefbadebede"]; NSWindowController *windowController = [[NSWindowController alloc] initWithWindowNibName:@"MGLStyleLayerTests" owner:self]; [windowController showWindow:nil]; #endif } +- (void)tearDown { + _mapView = nil; + + [super tearDown]; +} + - (void)testPropertyName:(NSString *)name isBoolean:(BOOL)isBoolean { NS_MUTABLE_ARRAY_OF(NSString *) *components = [name componentsSeparatedByString:@"-"].mutableCopy; if (isBoolean) { diff --git a/platform/darwin/test/MGLStyleTests.mm b/platform/darwin/test/MGLStyleTests.mm index 55a5df08ca..bcad7ab508 100644 --- a/platform/darwin/test/MGLStyleTests.mm +++ b/platform/darwin/test/MGLStyleTests.mm @@ -1,3 +1,4 @@ +#import "MGLAccountManager.h" #import "MGLMapView.h" #import "MGLStyle_Private.h" #import "MGLOpenGLStyleLayer.h" @@ -36,9 +37,19 @@ - (void)setUp { [super setUp]; - + + [MGLAccountManager setAccessToken:@"pk.feedcafedeadbeefbadebede"]; self.mapView = [[MGLMapView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; - self.style = [[MGLStyle alloc] initWithMapView:self.mapView]; +} + +- (void)tearDown { + self.mapView = nil; + + [super tearDown]; +} + +- (MGLStyle *)style { + return self.mapView.style; } - (void)testUnversionedStyleURLs { @@ -174,18 +185,18 @@ - (void)testAddingLayersWithDuplicateIdentifiers { //Just some source MGLVectorSource *source = [[MGLVectorSource alloc] initWithIdentifier:@"my-source" configurationURL:[NSURL URLWithString:@"mapbox://mapbox.mapbox-terrain-v2"]]; - [self.mapView.style addSource: source]; + [self.style addSource: source]; //Add initial layer MGLFillStyleLayer *initial = [[MGLFillStyleLayer alloc] initWithIdentifier:@"my-layer" source:source]; - [self.mapView.style addLayer:initial]; + [self.style addLayer:initial]; //Try to add the duplicate - XCTAssertThrowsSpecificNamed([self.mapView.style addLayer:[[MGLFillStyleLayer alloc] initWithIdentifier:@"my-layer" source:source]], NSException, @"MGLRedundantLayerIdentifierException"); - XCTAssertThrowsSpecificNamed([self.mapView.style insertLayer:[[MGLFillStyleLayer alloc] initWithIdentifier:@"my-layer" source:source] belowLayer:initial],NSException, @"MGLRedundantLayerIdentifierException"); - XCTAssertThrowsSpecificNamed([self.mapView.style insertLayer:[[MGLFillStyleLayer alloc] initWithIdentifier:@"my-layer" source:source] aboveLayer:initial], NSException, @"MGLRedundantLayerIdentifierException"); - XCTAssertThrowsSpecificNamed([self.mapView.style insertLayer:[[MGLFillStyleLayer alloc] initWithIdentifier:@"my-layer" source:source] atIndex:0], NSException, @"MGLRedundantLayerIdentifierException"); - XCTAssertThrowsSpecificNamed([self.mapView.style insertLayer:[[MGLOpenGLStyleLayer alloc] initWithIdentifier:@"my-layer"] atIndex:0], NSException, @"MGLRedundantLayerIdentifierException"); + XCTAssertThrowsSpecificNamed([self.style addLayer:[[MGLFillStyleLayer alloc] initWithIdentifier:@"my-layer" source:source]], NSException, @"MGLRedundantLayerIdentifierException"); + XCTAssertThrowsSpecificNamed([self.style insertLayer:[[MGLFillStyleLayer alloc] initWithIdentifier:@"my-layer" source:source] belowLayer:initial],NSException, @"MGLRedundantLayerIdentifierException"); + XCTAssertThrowsSpecificNamed([self.style insertLayer:[[MGLFillStyleLayer alloc] initWithIdentifier:@"my-layer" source:source] aboveLayer:initial], NSException, @"MGLRedundantLayerIdentifierException"); + XCTAssertThrowsSpecificNamed([self.style insertLayer:[[MGLFillStyleLayer alloc] initWithIdentifier:@"my-layer" source:source] atIndex:0], NSException, @"MGLRedundantLayerIdentifierException"); + XCTAssertThrowsSpecificNamed([self.style insertLayer:[[MGLOpenGLStyleLayer alloc] initWithIdentifier:@"my-layer"] atIndex:0], NSException, @"MGLRedundantLayerIdentifierException"); } - (NSString *)stringWithContentsOfStyleHeader { @@ -209,8 +220,8 @@ #endif XCTAssertNotNil(image); - [self.mapView.style setImage:image forName:imageName]; - MGLImage *styleImage = [self.mapView.style imageForName:imageName]; + [self.style setImage:image forName:imageName]; + MGLImage *styleImage = [self.style imageForName:imageName]; XCTAssertNotNil(styleImage); XCTAssertEqual(image.size.width, styleImage.size.width); @@ -218,29 +229,27 @@ } - (void)testLayersOrder { - [self.mapView setStyleURL:[MGLStyle streetsStyleURLWithVersion:8]]; - NSString *filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"amsterdam" ofType:@"geojson"]; NSURL *url = [NSURL fileURLWithPath:filePath]; MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"sourceID" URL:url options:nil]; - [self.mapView.style addSource:source]; + [self.style addSource:source]; MGLCircleStyleLayer *layer1 = [[MGLCircleStyleLayer alloc] initWithIdentifier:@"layer1" source:source]; - [self.mapView.style addLayer:layer1]; + [self.style addLayer:layer1]; MGLCircleStyleLayer *layer3 = [[MGLCircleStyleLayer alloc] initWithIdentifier:@"layer3" source:source]; - [self.mapView.style addLayer:layer3]; + [self.style addLayer:layer3]; MGLCircleStyleLayer *layer2 = [[MGLCircleStyleLayer alloc] initWithIdentifier:@"layer2" source:source]; - [self.mapView.style insertLayer:layer2 aboveLayer:layer1]; + [self.style insertLayer:layer2 aboveLayer:layer1]; MGLCircleStyleLayer *layer4 = [[MGLCircleStyleLayer alloc] initWithIdentifier:@"layer4" source:source]; - [self.mapView.style insertLayer:layer4 aboveLayer:layer3]; + [self.style insertLayer:layer4 aboveLayer:layer3]; MGLCircleStyleLayer *layer0 = [[MGLCircleStyleLayer alloc] initWithIdentifier:@"layer0" source:source]; - [self.mapView.style insertLayer:layer0 belowLayer:layer1]; + [self.style insertLayer:layer0 belowLayer:layer1]; - NSArray<MGLStyleLayer *> *layers = [self.mapView.style layers]; + NSArray<MGLStyleLayer *> *layers = [self.style layers]; XCTAssert([[layers[0] identifier] isEqualToString:layer0.identifier]); XCTAssert([[layers[1] identifier] isEqualToString:layer1.identifier]); diff --git a/platform/darwin/test/one-liner.json b/platform/darwin/test/one-liner.json new file mode 100644 index 0000000000..23c546181f --- /dev/null +++ b/platform/darwin/test/one-liner.json @@ -0,0 +1 @@ +{"version":8,"sources":{},"layers":[]}
\ No newline at end of file diff --git a/platform/ios/DEVELOPING.md b/platform/ios/DEVELOPING.md index 94ff49ee1b..f7bc0cc415 100644 --- a/platform/ios/DEVELOPING.md +++ b/platform/ios/DEVELOPING.md @@ -118,6 +118,22 @@ To add a localization to the iOS SDK: 1. In the Project navigator, expand each .strings and .stringsdict file in the project. An additional version for your localization should be listed; translate it. Translate everything on the right side of the equals sign. Leave the left side and any comments unmodified. See Apple’s documentation on the [.strings](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/LoadingResources/Strings/Strings.html) and [.stringsdict](https://developer.apple.com/library/content/documentation/MacOSX/Conceptual/BPInternational/StringsdictFileFormat/StringsdictFileFormat.html) formats. 1. You’re already most of the way towards localizing the macOS SDK too – consider [completing that localization](../macos/DEVELOPING.md#adding-a-localization). +### Adding a code example + +To add an example code listing to the documentation for a class or class member: + +1. Add a test method named in the form `testMGLClass` or `testMGLClass$method` + to [MGLDocumentationExampleTests](test/MGLDocumentationExampleTests.swift). + Wrap the code you’d like to appear in the documentation within + `//#-example-code` and `//#-end-example-code` comments. +1. Insert the code listings into the headers: + +```bash +make darwin-update-examples +``` + +[SourceKitten](https://github.com/jpsim/SourceKitten/) is required and will be installed automatically using Homebrew. + ## Testing `make ios-test` builds and runs unit tests of cross-platform code as well as the SDK. diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj index ebcf3a55cb..8d96bcf497 100644 --- a/platform/ios/ios.xcodeproj/project.pbxproj +++ b/platform/ios/ios.xcodeproj/project.pbxproj @@ -228,6 +228,7 @@ DA35A2CA1CCAAAD200E826B2 /* NSValue+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DA35A2C71CCAAAD200E826B2 /* NSValue+MGLAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; DA35A2CB1CCAAAD200E826B2 /* NSValue+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DA35A2C81CCAAAD200E826B2 /* NSValue+MGLAdditions.m */; }; DA35A2CC1CCAAAD200E826B2 /* NSValue+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DA35A2C81CCAAAD200E826B2 /* NSValue+MGLAdditions.m */; }; + DA35D0881E1A6309007DED41 /* one-liner.json in Resources */ = {isa = PBXBuildFile; fileRef = DA35D0871E1A6309007DED41 /* one-liner.json */; }; DA6408DB1DA4E7D300908C90 /* MGLVectorStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = DA6408D91DA4E7D300908C90 /* MGLVectorStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; }; DA6408DC1DA4E7D300908C90 /* MGLVectorStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = DA6408D91DA4E7D300908C90 /* MGLVectorStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; }; DA6408DD1DA4E7D300908C90 /* MGLVectorStyleLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = DA6408DA1DA4E7D300908C90 /* MGLVectorStyleLayer.m */; }; @@ -617,7 +618,7 @@ 554180411D2E97DE00012372 /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = System/Library/Frameworks/OpenGLES.framework; sourceTree = SDKROOT; }; 55D8C9941D0F133500F42F10 /* config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = config.xcconfig; path = ../../build/ios/config.xcconfig; sourceTree = "<group>"; }; 55D8C9951D0F18CE00F42F10 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; }; - 6407D66F1E0085FD00F6A9C3 /* MGLDocumentationExampleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MGLDocumentationExampleTests.swift; sourceTree = "<group>"; }; + 6407D66F1E0085FD00F6A9C3 /* MGLDocumentationExampleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MGLDocumentationExampleTests.swift; path = ../../darwin/test/MGLDocumentationExampleTests.swift; sourceTree = "<group>"; }; 7E016D7C1D9E86BE00A29A21 /* MGLPolyline+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MGLPolyline+MGLAdditions.h"; sourceTree = "<group>"; }; 7E016D7D1D9E86BE00A29A21 /* MGLPolyline+MGLAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MGLPolyline+MGLAdditions.m"; sourceTree = "<group>"; }; 7E016D821D9E890300A29A21 /* MGLPolygon+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MGLPolygon+MGLAdditions.h"; sourceTree = "<group>"; }; @@ -669,6 +670,7 @@ DA35A2C71CCAAAD200E826B2 /* NSValue+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSValue+MGLAdditions.h"; sourceTree = "<group>"; }; DA35A2C81CCAAAD200E826B2 /* NSValue+MGLAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSValue+MGLAdditions.m"; sourceTree = "<group>"; }; DA35A2D11CCAB25200E826B2 /* jazzy.yml */ = {isa = PBXFileReference; lastKnownFileType = text; path = jazzy.yml; sourceTree = "<group>"; }; + DA35D0871E1A6309007DED41 /* one-liner.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = "one-liner.json"; path = "../../darwin/test/one-liner.json"; sourceTree = "<group>"; }; DA4A26961CB6E795000B7809 /* Mapbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Mapbox.framework; sourceTree = BUILT_PRODUCTS_DIR; }; DA6408D91DA4E7D300908C90 /* MGLVectorStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLVectorStyleLayer.h; sourceTree = "<group>"; }; DA6408DA1DA4E7D300908C90 /* MGLVectorStyleLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLVectorStyleLayer.m; sourceTree = "<group>"; }; @@ -1110,6 +1112,7 @@ DA2E88601CC0382C00F24E7B /* MGLStyleTests.mm */, DA2E88551CC036F400F24E7B /* Info.plist */, DA2784FB1DF02FF4001D5B8D /* Media.xcassets */, + DA35D0871E1A6309007DED41 /* one-liner.json */, ); name = "SDK Tests"; path = test; @@ -1706,7 +1709,6 @@ DA8847CE1CBAF91600AB86E3 /* Frameworks */, DA8847CF1CBAF91600AB86E3 /* Headers */, DA8847D01CBAF91600AB86E3 /* Resources */, - 64E5BF321E09D729005223F7 /* Add Examples to Documentation */, ); buildRules = ( ); @@ -1741,7 +1743,6 @@ DAA4E4101CBB71D400178DFB /* Frameworks */, DAA4E4111CBB71D400178DFB /* CopyFiles */, DABFB85C1CBE99DE00D62B32 /* Headers */, - 6421B07A1E09EA4B00AF169B /* Add Examples to Documentation */, ); buildRules = ( ); @@ -1869,6 +1870,7 @@ files = ( DA2784FC1DF02FF4001D5B8D /* Media.xcassets in Resources */, 353BAEF71D646370009A8DA9 /* amsterdam.geojson in Resources */, + DA35D0881E1A6309007DED41 /* one-liner.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1934,37 +1936,6 @@ }; /* End PBXResourcesBuildPhase section */ -/* Begin PBXShellScriptBuildPhase section */ - 6421B07A1E09EA4B00AF169B /* Add Examples to Documentation */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Add Examples to Documentation"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "node \"${SRCROOT}/scripts/add-examples-to-docs.js\""; - }; - 64E5BF321E09D729005223F7 /* Add Examples to Documentation */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Add Examples to Documentation"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "node \"${SRCROOT}/scripts/add-examples-to-docs.js\""; - }; -/* End PBXShellScriptBuildPhase section */ - /* Begin PBXSourcesBuildPhase section */ DA1DC9461CB6C1C2006E619F /* Sources */ = { isa = PBXSourcesBuildPhase; diff --git a/platform/ios/jazzy.yml b/platform/ios/jazzy.yml index 1f1e48fce5..0a14cc3792 100644 --- a/platform/ios/jazzy.yml +++ b/platform/ios/jazzy.yml @@ -11,6 +11,8 @@ head: | objc: Yes skip_undocumented: Yes hide_documentation_coverage: Yes +umbrella_header: src/Mapbox.h +framework_root: ../darwin/src custom_categories: - name: Guides diff --git a/platform/ios/scripts/add-examples-to-docs.js b/platform/ios/scripts/add-examples-to-docs.js deleted file mode 100644 index 7be755e2c6..0000000000 --- a/platform/ios/scripts/add-examples-to-docs.js +++ /dev/null @@ -1,65 +0,0 @@ -'use strict'; - -const fs = require('fs'); - -const examples = fs.readFileSync(`${__dirname}/../test/MGLDocumentationExampleTests.swift`, 'utf8'); - -// Regex extracts the following block -// /*---BEGIN EXAMPLE: MGLStyleSource---*/ -// /* Frontmatter to describe the example */ -// let sampleCode: String? -// /*---END EXAMPLE---*/ -// -// into the following regex groups: -// 1 (token): " MGLStyleSource" -// 2 (frontmatter): "/* Frontmatter to describe the example */" -// 3 (sample code): "let sampleCode: String?" -const exampleRegex = /\/\*---BEGIN EXAMPLE:(.*)---\*\/\s*(\/\*+[\s\S]*?\*+\/)?([\s\S]*?)\/\*---END EXAMPLE---\*\//gm; - -var path = `${process.env.TARGET_BUILD_DIR}/${process.env.PUBLIC_HEADERS_FOLDER_PATH}`; - -console.log("Installing examples..."); - -var match; -while ((match = exampleRegex.exec(examples)) !== null) { - const token = match[1].trim(); - const className = token.split('.')[0]; - - const frontmatter = (match[2] || '') - .replace(/\/\*+/g, '') // Remove block comment /** - .replace(/\*+\//g, '') // Remove block comment end */ - .trim() - .replace(/\n {8,9}/g, '\n'); // Remove leading whitespace (8-9 spaces incl block comment) - - const exampleCode = match[3] - .trim() - .replace(/\n {8}/g, '\n'); // Remove leading whitespace (8 spaces) - - // Generate example text - var exampleText = "### Example\n\n"; - if (frontmatter.length > 0) { - exampleText += `${frontmatter}\n\n`; - } - exampleText += "```swift\n" + exampleCode + "\n```"; - exampleText = exampleText.replace(/\n/g, '\n '); - - const placeholderRegex = new RegExp(`<!--EXAMPLE: ${token}-->`); - - // check if file exists at path - const filename = `${path}/${className}.h`; - - if (fs.existsSync(filename)) { - const file = fs.readFileSync(filename, 'utf8'); - // Check for example placeholder in file & update file if found - if (placeholderRegex.test(file)) { - console.log("Updating example:", filename); - fs.writeFileSync(filename, file.replace(placeholderRegex, exampleText)); - } else if (file.indexOf(exampleText) === -1) { - console.log(`Placeholder "${token}" missing:`, filename); - } else { - console.log(`Example "${token}" already replaced.`); - } - } else if (token !== "ExampleToken") { - console.log("Error file doesn't exist:", filename); - } -} diff --git a/platform/ios/scripts/document.sh b/platform/ios/scripts/document.sh index 634f4de5dc..ec349d592d 100755 --- a/platform/ios/scripts/document.sh +++ b/platform/ios/scripts/document.sh @@ -35,16 +35,11 @@ cp -r platform/ios/docs/img "${OUTPUT}/img" DEFAULT_THEME="platform/darwin/docs/theme" THEME=${JAZZY_THEME:-$DEFAULT_THEME} -DEFAULT_FRAMEWORK_PATH="build/ios/pkg/dynamic/Mapbox.framework" -FRAMEWORK_PATH=${FRAMEWORK_PATH:-$DEFAULT_FRAMEWORK_PATH} - 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" \ --readme ${README} \ --documentation="platform/ios/docs/guides/*.md" \ --root-url https://www.mapbox.com/ios-sdk/api/${RELEASE_VERSION}/ \ diff --git a/platform/macos/DEVELOPING.md b/platform/macos/DEVELOPING.md index 71eb7995c8..823ad4656b 100644 --- a/platform/macos/DEVELOPING.md +++ b/platform/macos/DEVELOPING.md @@ -95,6 +95,32 @@ To add a localization to the macOS SDK: 1. In the Project navigator, expand each .strings and .stringsdict file in the project. An additional version for your localization should be listed; translate it. Translate everything on the right side of the equals sign. Leave the left side and any comments unmodified. See Apple’s documentation on the [.strings](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/LoadingResources/Strings/Strings.html) and [.stringsdict](https://developer.apple.com/library/content/documentation/MacOSX/Conceptual/BPInternational/StringsdictFileFormat/StringsdictFileFormat.html) formats. 1. You’re already most of the way towards localizing the iOS SDK too – consider [completing that localization](../ios/DEVELOPING.md#adding-a-localization). +### Adding a code example + +To add an example code listing to the documentation for a class or class member: + +1. Add a test method named in the form `testMGLClass` or `testMGLClass$method` + to [MGLDocumentationExampleTests](test/MGLDocumentationExampleTests.swift). + Wrap the code you’d like to appear in the documentation within + `//#-example-code` and `//#-end-example-code` comments. +1. Insert the code listings into the headers: + +```bash +make darwin-update-examples +``` + +[SourceKitten](https://github.com/jpsim/SourceKitten/) is required and will be installed automatically using Homebrew. + +## Testing + +`make macos-test` builds and runs unit tests of cross-platform code as well as the SDK. + +To instead run the cross-platform tests in Xcode instead of on the command line: + +1. Run `make xproj` to set up the workspace. +1. Change the scheme to “mbgl-test” and press Command-R to run core unit tests. +1. Change the scheme to “CI” and press Command-U to run SDK integration tests. + ## Access tokens The demo applications use Mapbox vector tiles, which require a Mapbox account and API access token. Obtain an access token on the [Mapbox account page](https://www.mapbox.com/studio/account/tokens/). You will be prompted for this access token the first time you launch the demo application. diff --git a/platform/macos/macos.xcodeproj/project.pbxproj b/platform/macos/macos.xcodeproj/project.pbxproj index 56872a1b19..e4657596cf 100644 --- a/platform/macos/macos.xcodeproj/project.pbxproj +++ b/platform/macos/macos.xcodeproj/project.pbxproj @@ -68,6 +68,7 @@ DA0CD58E1CF56F5800A5F5A5 /* MGLFeatureTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA0CD58D1CF56F5800A5F5A5 /* MGLFeatureTests.mm */; }; DA2207BC1DC076940002F84D /* MGLStyleValueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA2207BB1DC076940002F84D /* MGLStyleValueTests.swift */; }; DA2784FE1DF03060001D5B8D /* Media.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DA2784FD1DF03060001D5B8D /* Media.xcassets */; }; + DA29875A1E1A4290002299F5 /* MGLDocumentationExampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA2987591E1A4290002299F5 /* MGLDocumentationExampleTests.swift */; }; DA2DBBCB1D51E30A00D38FF9 /* MGLStyleLayerTests.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA2DBBCA1D51E30A00D38FF9 /* MGLStyleLayerTests.xib */; }; DA35A2A41CC9EB1A00E826B2 /* MGLCoordinateFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = DA35A2A31CC9EB1A00E826B2 /* MGLCoordinateFormatter.h */; settings = {ATTRIBUTES = (Public, ); }; }; DA35A2A61CC9EB2700E826B2 /* MGLCoordinateFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = DA35A2A51CC9EB2700E826B2 /* MGLCoordinateFormatter.m */; }; @@ -80,6 +81,7 @@ DA35A2C21CCA9F4A00E826B2 /* MGLClockDirectionFormatterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DA35A2C11CCA9F4A00E826B2 /* MGLClockDirectionFormatterTests.m */; }; DA35A2CF1CCAAED300E826B2 /* NSValue+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DA35A2CD1CCAAED300E826B2 /* NSValue+MGLAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; DA35A2D01CCAAED300E826B2 /* NSValue+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DA35A2CE1CCAAED300E826B2 /* NSValue+MGLAdditions.m */; }; + DA35D08A1E1A631B007DED41 /* one-liner.json in Resources */ = {isa = PBXBuildFile; fileRef = DA35D0891E1A631B007DED41 /* one-liner.json */; }; DA551B821DB496AC0009AFAF /* MGLTileSource.h in Headers */ = {isa = PBXBuildFile; fileRef = DA551B7F1DB496AC0009AFAF /* MGLTileSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; DA551B831DB496AC0009AFAF /* MGLTileSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DA551B801DB496AC0009AFAF /* MGLTileSource_Private.h */; }; DA551B841DB496AC0009AFAF /* MGLTileSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA551B811DB496AC0009AFAF /* MGLTileSource.mm */; }; @@ -321,6 +323,7 @@ DA2207BA1DC076930002F84D /* test-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "test-Bridging-Header.h"; sourceTree = "<group>"; }; DA2207BB1DC076940002F84D /* MGLStyleValueTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MGLStyleValueTests.swift; sourceTree = "<group>"; }; DA2784FD1DF03060001D5B8D /* Media.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Media.xcassets; path = ../../darwin/test/Media.xcassets; sourceTree = "<group>"; }; + DA2987591E1A4290002299F5 /* MGLDocumentationExampleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MGLDocumentationExampleTests.swift; path = ../../darwin/test/MGLDocumentationExampleTests.swift; sourceTree = "<group>"; }; DA2DBBCA1D51E30A00D38FF9 /* MGLStyleLayerTests.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MGLStyleLayerTests.xib; sourceTree = "<group>"; }; DA35A2A31CC9EB1A00E826B2 /* MGLCoordinateFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLCoordinateFormatter.h; sourceTree = "<group>"; }; DA35A2A51CC9EB2700E826B2 /* MGLCoordinateFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLCoordinateFormatter.m; sourceTree = "<group>"; }; @@ -333,6 +336,7 @@ DA35A2C11CCA9F4A00E826B2 /* MGLClockDirectionFormatterTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLClockDirectionFormatterTests.m; path = ../../darwin/test/MGLClockDirectionFormatterTests.m; sourceTree = "<group>"; }; DA35A2CD1CCAAED300E826B2 /* NSValue+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSValue+MGLAdditions.h"; sourceTree = "<group>"; }; DA35A2CE1CCAAED300E826B2 /* NSValue+MGLAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSValue+MGLAdditions.m"; sourceTree = "<group>"; }; + DA35D0891E1A631B007DED41 /* one-liner.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = "one-liner.json"; path = "../../darwin/test/one-liner.json"; sourceTree = "<group>"; }; DA551B7F1DB496AC0009AFAF /* MGLTileSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLTileSource.h; sourceTree = "<group>"; }; DA551B801DB496AC0009AFAF /* MGLTileSource_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLTileSource_Private.h; sourceTree = "<group>"; }; DA551B811DB496AC0009AFAF /* MGLTileSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLTileSource.mm; sourceTree = "<group>"; }; @@ -886,6 +890,7 @@ 3526EABC1DF9B19800006B43 /* MGLCodingTests.m */, DA35A2B51CCA14D700E826B2 /* MGLCompassDirectionFormatterTests.m */, DA35A2A71CC9F41600E826B2 /* MGLCoordinateFormatterTests.m */, + DA2987591E1A4290002299F5 /* MGLDocumentationExampleTests.swift */, DD58A4C71D822C6200E1F038 /* MGLExpressionTests.mm */, DA0CD58D1CF56F5800A5F5A5 /* MGLFeatureTests.mm */, DAE6C3C81CC34BD800DB3429 /* MGLGeometryTests.mm */, @@ -896,6 +901,7 @@ DAE6C3CC1CC34BD800DB3429 /* MGLStyleTests.mm */, DAE6C33A1CC30DB200DB3429 /* Info.plist */, DA2784FD1DF03060001D5B8D /* Media.xcassets */, + DA35D0891E1A631B007DED41 /* one-liner.json */, ); name = "SDK Tests"; path = test; @@ -1216,6 +1222,7 @@ 35724FC41D630502002A4AB4 /* amsterdam.geojson in Resources */, DA2784FE1DF03060001D5B8D /* Media.xcassets in Resources */, DA2DBBCB1D51E30A00D38FF9 /* MGLStyleLayerTests.xib in Resources */, + DA35D08A1E1A631B007DED41 /* one-liner.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1326,6 +1333,7 @@ DA87A99E1DC9DC2100810D09 /* MGLPredicateTests.mm in Sources */, DD58A4C91D822C6700E1F038 /* MGLExpressionTests.mm in Sources */, DA87A9A71DCACC5000810D09 /* MGLBackgroundStyleLayerTests.m in Sources */, + DA29875A1E1A4290002299F5 /* MGLDocumentationExampleTests.swift in Sources */, DAE6C3D31CC34C9900DB3429 /* MGLOfflinePackTests.m in Sources */, DA87A9A51DCACC5000810D09 /* MGLLineStyleLayerTests.m in Sources */, DA87A9A31DCACC5000810D09 /* MGLRasterStyleLayerTests.m in Sources */, |