[ios, macos] Insert example code in original headers (#7569)
* [ios, macos] Removed extraneous code fences * [ios] Streamlined example delimiters Use test method names as names of example blocks and test method documentation comments as front matter for examples. Set off example blocks using a syntax similar to playground markup syntax. Avoid hard-coding indentation levels. Trigger Xcode build error when an error occurs in the script. * [ios] Removed platform-specific code from examples * [ios] Fixed logic error The comment said 200 while the code said 1,500. * [ios] Formalize build phase input * [ios] Insert examples into original source documentation Rewrote the example code insertion script to work on the original source files and overwrite any existing code examples on the same symbols. The script uses SourceKitten to find the documentation comment for the symbol named by the test method. Replaced the Run Script build phase that runs the example code insertion script with a make rule that runs the same script. Inlined skeleton examples minus the contents of the code blocks. * [ios] Fixed syntax error * [ios] Ran ios-add-examples-to-docs * [ios] Avoid touching unchanged headers * [ios] Refactored example insertion script * [ios] Point jazzy at original sources * [ios] Invoke SourceKitten only once * [ios] Look for methods as well as properties * [ios] Thoroughly search for code blocks in doc comments Refactored the example code insertion script to index test methods by their names, then recursively search the SourceKitten output for documentation comments that contain Swift code blocks, replacing each code block with the associated test method body. * [ios, macos] Enabled example insertion for macOS The example code insertion script is now platform-agnostic. * [ios, macos] Fixed documentation example tests Set the map view’s style to a minimal local JSON file. Wait for the style to finish loading before running each test. Corrected CGVector type. * [ios, macos] Dry run mode The output of this mode isn’t a good indicator of whether any files would’ve needed to be changed, because the presence of a conditional compilation block in one of the test methods means this script would always change and revert the corresponding comment. * [ios] Fixed test failure The iOS implementation of MGLMapView tries to show the Streets style by default even if no access token has been set. Avoid a race condition and frequent test failure by specifying the minimal style on initialization. * [ios, macos] Ensure SourceKitten is installed before inserting example code * [ios, macos] Tear down map view after each test Keep map views from previous tests from hanging around, potentially obscuring the result of a subsequent test. Set the access token to a bogus token upfront for all style layer tests. Unified MGLStyle usage within MGLStyleTests. * [ios, macos] Reinsert examples after generating runtime styling headers
This commit is contained in:
4
Makefile
4
Makefile
@@ -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 #####################################################
|
||||
|
||||
152
platform/darwin/scripts/update-examples.js
Normal file
152
platform/darwin/scripts/update-examples.js
Normal file
@@ -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');
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<%-
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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"))
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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]);
|
||||
|
||||
1
platform/darwin/test/one-liner.json
Normal file
1
platform/darwin/test/one-liner.json
Normal file
@@ -0,0 +1 @@
|
||||
{"version":8,"sources":{},"layers":[]}
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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}/ \
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 */,
|
||||
|
||||
Reference in New Issue
Block a user