summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJesse Bounds <jesse@rebounds.net>2016-09-22 17:30:47 -0700
committerGitHub <noreply@github.com>2016-09-22 17:30:47 -0700
commit3ddb8dfc86753120d2ea2c3befdf2430c1c56562 (patch)
treebc241d8c66ea7f78ae4728f9644673e8994e1295
parent08c08f6705d168f0bc7cde251cdacb2c4a53bb7a (diff)
downloadqtlocation-mapboxgl-3ddb8dfc86753120d2ea2c3befdf2430c1c56562.tar.gz
[ios, macos] Add tile template option to raster and vector sources
A new MGLTileSet class that wraps all available core Tileset options can be created and passed into the sources. The sources pass that tileset object along to core.
-rw-r--r--platform/darwin/src/MGLRasterSource.h12
-rw-r--r--platform/darwin/src/MGLRasterSource.mm37
-rw-r--r--platform/darwin/src/MGLTileSet.h78
-rw-r--r--platform/darwin/src/MGLTileSet.mm87
-rw-r--r--platform/darwin/src/MGLTileSet_Private.h9
-rw-r--r--platform/darwin/src/MGLVectorSource.h10
-rw-r--r--platform/darwin/src/MGLVectorSource.mm31
-rw-r--r--platform/ios/ios.xcodeproj/project.pbxproj22
-rw-r--r--platform/ios/src/Mapbox.h1
-rw-r--r--platform/ios/test/MGLTileSetTests.mm82
10 files changed, 360 insertions, 9 deletions
diff --git a/platform/darwin/src/MGLRasterSource.h b/platform/darwin/src/MGLRasterSource.h
index e563244bce..3b22257cb6 100644
--- a/platform/darwin/src/MGLRasterSource.h
+++ b/platform/darwin/src/MGLRasterSource.h
@@ -1,10 +1,20 @@
#import "MGLSource.h"
+#import "MGLTypes.h"
+
+@class MGLTileSet;
+
+NS_ASSUME_NONNULL_BEGIN
@interface MGLRasterSource : MGLSource
@property (nonatomic, readonly, copy) NSURL *URL;
-@property (nonatomic, readonly, assign) CGFloat tileSize;
+@property (nonatomic, readonly, assign) NSUInteger tileSize;
+@property (nonatomic, readonly, nullable) MGLTileSet *tileSet;
- (instancetype)initWithSourceIdentifier:(NSString *)sourceIdentifier URL:(NSURL *)url tileSize:(CGFloat)tileSize;
+- (instancetype)initWithSourceIdentifier:(NSString *)sourceIdentifier tileSize:(CGFloat)tileSize tileSet:(MGLTileSet *)tileSet;
+
@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLRasterSource.mm b/platform/darwin/src/MGLRasterSource.mm
index 15ca2fc558..06d5a9689c 100644
--- a/platform/darwin/src/MGLRasterSource.mm
+++ b/platform/darwin/src/MGLRasterSource.mm
@@ -1,12 +1,15 @@
#import "MGLRasterSource.h"
#import "MGLSource_Private.h"
+#import "MGLTileSet_Private.h"
+#import "NSURL+MGLAdditions.h"
#include <mbgl/style/sources/raster_source.hpp>
@implementation MGLRasterSource
-- (instancetype)initWithSourceIdentifier:(NSString *)sourceIdentifier URL:(NSURL *)url tileSize:(CGFloat)tileSize {
+- (instancetype)initWithSourceIdentifier:(NSString *)sourceIdentifier URL:(NSURL *)url tileSize:(CGFloat)tileSize
+{
if (self = [super initWithSourceIdentifier:sourceIdentifier]) {
_URL = url;
_tileSize = tileSize;
@@ -14,10 +17,34 @@
return self;
}
-- (std::unique_ptr<mbgl::style::Source>)mbglSource {
- auto source = std::make_unique<mbgl::style::RasterSource>(self.sourceIdentifier.UTF8String,
- self.URL.absoluteString.UTF8String,
- uint16_t(self.tileSize));
+- (instancetype)initWithSourceIdentifier:(NSString *)sourceIdentifier tileSize:(CGFloat)tileSize tileSet:(MGLTileSet *)tileSet;
+{
+ if (self = [super initWithSourceIdentifier:sourceIdentifier])
+ {
+ _tileSize = tileSize;
+ _tileSet = tileSet;
+ }
+ return self;
+}
+
+- (std::unique_ptr<mbgl::style::Source>)mbglSource
+{
+ std::unique_ptr<mbgl::style::RasterSource> source;
+
+ if (self.URL)
+ {
+ source = std::make_unique<mbgl::style::RasterSource>(self.sourceIdentifier.UTF8String,
+ self.URL.mgl_URLByStandardizingScheme.absoluteString.UTF8String,
+ uint16_t(self.tileSize));
+ }
+ else
+ {
+ source = std::make_unique<mbgl::style::RasterSource>(self.sourceIdentifier.UTF8String,
+ self.tileSet.mbglTileset,
+ uint16_t(self.tileSize));
+
+ }
+
return std::move(source);
}
diff --git a/platform/darwin/src/MGLTileSet.h b/platform/darwin/src/MGLTileSet.h
new file mode 100644
index 0000000000..5ae899d833
--- /dev/null
+++ b/platform/darwin/src/MGLTileSet.h
@@ -0,0 +1,78 @@
+#import <Foundation/Foundation.h>
+#import "MGLTypes.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/** These constants represent the scheme that the tile URL templates will use. */
+typedef NS_ENUM(NSUInteger, MGLTileSetScheme) {
+ MGLTileSetSchemeXYZ = 0,
+ MGLTileSetSchemeTMS
+};
+
+/**
+ The `MGLTileSet` class holds the tile URL template strings and associated
+ configuration for those strings. It can be passed to an `MGLVectorSource` or
+ `MGLRasterSource` instead of an `NSURL` representing a TileJSON URL to create a
+ source.
+ */
+@interface MGLTileSet : NSObject
+
+/**
+ An `NSArray` of `NSString` objects that represent the tile templates.
+ */
+@property (nonatomic, copy) NS_ARRAY_OF(NSString *) *tileURLTemplates;
+
+/**
+ An `NSNumber` object containing an integer; specifies the minimum zoom level at
+ which the source will display tiles. The value should be in the range of 0 to
+ 22. The default value is 0 and the source will use the default value
+ if `minimumZoomLevel` is nil.
+ */
+@property (nonatomic, nullable) NSNumber *minimumZoomLevel;
+
+/**
+ An `NSNumber` object containing an integer; specifies the maximum zoom level at
+ which to display tiles. The value should be in the range of 0 to 22 and greater
+ than `minimumZoomLevel`. The default value is 22 and the source will use the
+ default value if `maximumZoomLevel` is nil.
+ */
+@property (nonatomic, nullable) NSNumber *maximumZoomLevel;
+
+/**
+ An `NSString` object that contains an attribution to be displayed when the map is
+ shown to a user. The default value is nil.
+ */
+@property (nonatomic, copy, nullable) NSString *attribution;
+
+/**
+ An `MGLTileSetScheme` value that contains an enumeration (either
+ `MGLTileSetSchemeXYZ` or `MGLTileSetSchemeTMS`) that influences the y direction
+ of the tile coordinates. The default is `MGLTileSetSchemeXYZ`.
+ */
+@property (nonatomic) MGLTileSetScheme scheme;
+
+/**
+ Initializes and returns a new tile set object.
+
+ @param tileURLTemplates An `NSArray` of `NSString` objects that represent the
+ tile templates.
+ @return The initialized tile set object.
+ */
+- (instancetype)initWithTileURLTemplates:(NS_ARRAY_OF(NSString *) *)tileURLTemplates;
+
+/**
+ Initializes and returns a new tile set object.
+
+ @param tileURLTemplates An `NSArray` of `NSString` objects that represent the
+ tile templates.
+ @param minimumZoomLevel An `NSUInteger`; specifies the minimum zoom level at
+ which to display tiles.
+ @param maximumZoomLevel An `NSUInteger`; specifies the maximum zoom level at
+ which to display tiles.
+ @return The initialized tile set object.
+ */
+- (instancetype)initWithTileURLTemplates:(NS_ARRAY_OF(NSString *) *)tileURLTemplates minimumZoomLevel:(NSUInteger)minimumZoomLevel maximumZoomLevel:(NSUInteger)maximumZoomLevel;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLTileSet.mm b/platform/darwin/src/MGLTileSet.mm
new file mode 100644
index 0000000000..b2359bb92d
--- /dev/null
+++ b/platform/darwin/src/MGLTileSet.mm
@@ -0,0 +1,87 @@
+#import "MGLTileSet.h"
+
+#include <mbgl/util/tileset.hpp>
+
+@implementation MGLTileSet
+
+- (instancetype)initWithTileURLTemplates:(NS_ARRAY_OF(NSString *) *)tileURLTemplates
+{
+ if (self = [super init])
+ {
+ _tileURLTemplates = tileURLTemplates;
+ }
+ return self;
+}
+
+- (instancetype)initWithTileURLTemplates:(NS_ARRAY_OF(NSString *) *)tileURLTemplates minimumZoomLevel:(NSUInteger)minimumZoomLevel maximumZoomLevel:(NSUInteger)maximumZoomLevel
+{
+ if (minimumZoomLevel > maximumZoomLevel)
+ {
+ [[NSException exceptionWithName:@"Invalid minimumZoomLevel"
+ reason:@"minimumZoomLevel must be less than maximumZoomLevel"
+ userInfo:nil] raise];
+ return nil;
+ }
+
+ if (self = [super init])
+ {
+ _tileURLTemplates = tileURLTemplates;
+ _minimumZoomLevel = @(minimumZoomLevel);
+ _maximumZoomLevel = @(maximumZoomLevel);
+ }
+ return self;
+}
+
+- (void)setMinimumZoomLevel:(NSNumber *)minimumZoomLevel
+{
+ if (self.maximumZoomLevel && [minimumZoomLevel integerValue] > [self.maximumZoomLevel integerValue])
+ {
+ [[NSException exceptionWithName:@"Invalid minimumZoomLevel"
+ reason:@"minimumZoomLevel must be less than maximumZoomLevel"
+ userInfo:nil] raise];
+ return;
+ }
+
+ _minimumZoomLevel = minimumZoomLevel;
+}
+
+- (void)setMaximumZoomLevel:(NSNumber *)maximumZoomLevel
+{
+ if (self.maximumZoomLevel && [maximumZoomLevel integerValue] < [self.maximumZoomLevel integerValue])
+ {
+ [[NSException exceptionWithName:@"Invalid minimumZoomLevel"
+ reason:@"minimumZoomLevel must be less than maximumZoomLevel"
+ userInfo:nil] raise];
+ }
+
+ _maximumZoomLevel = maximumZoomLevel;
+}
+
+- (mbgl::Tileset)mbglTileset
+{
+ mbgl::Tileset tileset;
+
+ for (NSString *tileURLTemplate in self.tileURLTemplates)
+ {
+ tileset.tiles.push_back(tileURLTemplate.UTF8String);
+ }
+
+ // set the minimum / maximum zoom range to the values specified by this class if they
+ // were set. otherwise, use the core objects default values
+ uint8_t minimumZoomLevel = self.minimumZoomLevel ? [self.minimumZoomLevel unsignedIntegerValue] : tileset.zoomRange.min;
+ uint8_t maximumZoomLevel = self.minimumZoomLevel ? [self.maximumZoomLevel unsignedIntegerValue] : tileset.zoomRange.max;
+ tileset.zoomRange = mbgl::Range<uint8_t>(minimumZoomLevel, maximumZoomLevel);
+
+ if (self.attribution)
+ {
+ tileset.attribution = self.attribution.UTF8String;
+ }
+
+ if (self.scheme == MGLTileSetSchemeTMS) {
+ tileset.scheme = mbgl::Tileset::Scheme::TMS;
+ }
+
+ return tileset;
+}
+
+@end
diff --git a/platform/darwin/src/MGLTileSet_Private.h b/platform/darwin/src/MGLTileSet_Private.h
new file mode 100644
index 0000000000..6a14d428db
--- /dev/null
+++ b/platform/darwin/src/MGLTileSet_Private.h
@@ -0,0 +1,9 @@
+#import "MGLTileSet.h"
+
+#include <mbgl/util/tileset.hpp>
+
+@interface MGLTileSet (Private)
+
+- (mbgl::Tileset)mbglTileset;
+
+@end \ No newline at end of file
diff --git a/platform/darwin/src/MGLVectorSource.h b/platform/darwin/src/MGLVectorSource.h
index 05e041511e..2402208499 100644
--- a/platform/darwin/src/MGLVectorSource.h
+++ b/platform/darwin/src/MGLVectorSource.h
@@ -1,4 +1,9 @@
#import "MGLSource.h"
+#import "MGLTypes.h"
+
+@class MGLTileSet;
+
+NS_ASSUME_NONNULL_BEGIN
/**
A vector tile source. Tiles must be in Mapbox Vector Tile format.
@@ -9,6 +14,7 @@
@interface MGLVectorSource : MGLSource
@property (nonatomic, readonly, copy) NSURL *URL;
+@property (nonatomic, readonly, nullable) MGLTileSet *tileSet;
/**
Initializes and returns a vector source from a remote url.
@@ -20,4 +26,8 @@
*/
- (instancetype)initWithSourceIdentifier:(NSString *)sourceIdentifier URL:(NSURL *)url;
+- (instancetype)initWithSourceIdentifier:(NSString *)sourceIdentifier tileSet:(MGLTileSet *)tileSet;
+
@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLVectorSource.mm b/platform/darwin/src/MGLVectorSource.mm
index d0c9b0126a..2ab9627ef5 100644
--- a/platform/darwin/src/MGLVectorSource.mm
+++ b/platform/darwin/src/MGLVectorSource.mm
@@ -1,6 +1,8 @@
#import "MGLVectorSource.h"
#import "MGLSource_Private.h"
+#import "MGLTileSet_Private.h"
+#import "NSURL+MGLAdditions.h"
#include <mbgl/style/sources/vector_source.hpp>
@@ -8,16 +10,39 @@
static NSString *MGLVectorSourceType = @"vector";
-- (instancetype)initWithSourceIdentifier:(NSString *)sourceIdentifier URL:(NSURL *)url {
- if (self = [super initWithSourceIdentifier:sourceIdentifier]) {
+- (instancetype)initWithSourceIdentifier:(NSString *)sourceIdentifier URL:(NSURL *)url
+{
+ if (self = [super initWithSourceIdentifier:sourceIdentifier])
+ {
_URL = url;
}
return self;
}
+- (instancetype)initWithSourceIdentifier:(NSString *)sourceIdentifier tileSet:(MGLTileSet *)tileSet
+{
+ if (self = [super initWithSourceIdentifier:sourceIdentifier])
+ {
+ _tileSet = tileSet;
+ }
+ return self;
+}
+
- (std::unique_ptr<mbgl::style::Source>)mbglSource
{
- auto source = std::make_unique<mbgl::style::VectorSource>(self.sourceIdentifier.UTF8String, self.URL.absoluteString.UTF8String);
+ std::unique_ptr<mbgl::style::VectorSource> source;
+
+ if (self.URL)
+ {
+ source = std::make_unique<mbgl::style::VectorSource>(self.sourceIdentifier.UTF8String,
+ self.URL.mgl_URLByStandardizingScheme.absoluteString.UTF8String);
+ }
+ else
+ {
+ source = std::make_unique<mbgl::style::VectorSource>(self.sourceIdentifier.UTF8String,
+ self.tileSet.mbglTileset);
+ }
+
return std::move(source);
}
diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj
index 35a1075301..de77398582 100644
--- a/platform/ios/ios.xcodeproj/project.pbxproj
+++ b/platform/ios/ios.xcodeproj/project.pbxproj
@@ -159,6 +159,13 @@
4018B1CA1CDC288E00F666AF /* MGLAnnotationView.h in Headers */ = {isa = PBXBuildFile; fileRef = 4018B1C51CDC277F00F666AF /* MGLAnnotationView.h */; settings = {ATTRIBUTES = (Public, ); }; };
4018B1CB1CDC288E00F666AF /* MGLAnnotationView.h in Headers */ = {isa = PBXBuildFile; fileRef = 4018B1C51CDC277F00F666AF /* MGLAnnotationView.h */; settings = {ATTRIBUTES = (Public, ); }; };
404326891D5B9B27007111BD /* MGLAnnotationContainerView_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 404326881D5B9B1A007111BD /* MGLAnnotationContainerView_Private.h */; };
+ 404C26E21D89B877000AA13D /* MGLTileSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 404C26E01D89B877000AA13D /* MGLTileSet.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 404C26E31D89B877000AA13D /* MGLTileSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 404C26E01D89B877000AA13D /* MGLTileSet.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 404C26E41D89B877000AA13D /* MGLTileSet.mm in Sources */ = {isa = PBXBuildFile; fileRef = 404C26E11D89B877000AA13D /* MGLTileSet.mm */; };
+ 404C26E51D89B877000AA13D /* MGLTileSet.mm in Sources */ = {isa = PBXBuildFile; fileRef = 404C26E11D89B877000AA13D /* MGLTileSet.mm */; };
+ 404C26E71D89C55D000AA13D /* MGLTileSet_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 404C26E61D89C515000AA13D /* MGLTileSet_Private.h */; };
+ 404C26E81D89C55D000AA13D /* MGLTileSet_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 404C26E61D89C515000AA13D /* MGLTileSet_Private.h */; };
+ 4085AF091D933DEA00F11B22 /* MGLTileSetTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4085AF081D933DEA00F11B22 /* MGLTileSetTests.mm */; };
40CFA6511D7875BB008103BD /* MGLGeoJSONSourceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 40CFA6501D787579008103BD /* MGLGeoJSONSourceTests.mm */; };
40EDA1C01CFE0E0200D9EA68 /* MGLAnnotationContainerView.h in Headers */ = {isa = PBXBuildFile; fileRef = 40EDA1BD1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.h */; };
40EDA1C11CFE0E0500D9EA68 /* MGLAnnotationContainerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 40EDA1BE1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.m */; };
@@ -568,6 +575,10 @@
4018B1C51CDC277F00F666AF /* MGLAnnotationView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAnnotationView.h; sourceTree = "<group>"; };
402E9DE01CD2C76200FD4519 /* Mapbox.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = Mapbox.playground; sourceTree = "<group>"; };
404326881D5B9B1A007111BD /* MGLAnnotationContainerView_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLAnnotationContainerView_Private.h; sourceTree = "<group>"; };
+ 404C26E01D89B877000AA13D /* MGLTileSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLTileSet.h; sourceTree = "<group>"; };
+ 404C26E11D89B877000AA13D /* MGLTileSet.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLTileSet.mm; sourceTree = "<group>"; };
+ 404C26E61D89C515000AA13D /* MGLTileSet_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLTileSet_Private.h; sourceTree = "<group>"; };
+ 4085AF081D933DEA00F11B22 /* MGLTileSetTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLTileSetTests.mm; sourceTree = "<group>"; };
40CFA6501D787579008103BD /* MGLGeoJSONSourceTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLGeoJSONSourceTests.mm; sourceTree = "<group>"; };
40EDA1BD1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAnnotationContainerView.h; sourceTree = "<group>"; };
40EDA1BE1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLAnnotationContainerView.m; sourceTree = "<group>"; };
@@ -812,6 +823,9 @@
3566C7651D4A77BA008152BC /* MGLGeoJSONSource.mm */,
3566C76A1D4A8DFA008152BC /* MGLRasterSource.h */,
3566C76B1D4A8DFA008152BC /* MGLRasterSource.mm */,
+ 404C26E01D89B877000AA13D /* MGLTileSet.h */,
+ 404C26E61D89C515000AA13D /* MGLTileSet_Private.h */,
+ 404C26E11D89B877000AA13D /* MGLTileSet.mm */,
);
name = Sources;
sourceTree = "<group>";
@@ -927,6 +941,7 @@
isa = PBXGroup;
children = (
40CFA6501D787579008103BD /* MGLGeoJSONSourceTests.mm */,
+ 4085AF081D933DEA00F11B22 /* MGLTileSetTests.mm */,
);
name = Sources;
sourceTree = "<group>";
@@ -1453,11 +1468,13 @@
350098CA1D482D9C004B2AF0 /* NSArray+MGLStyleAttributeAdditions.h in Headers */,
350098AF1D47E6F4004B2AF0 /* UIColor+MGLStyleAttributeAdditions.h in Headers */,
3510FFF91D6DCC4700F413B2 /* NSCompoundPredicate+MGLAdditions.h in Headers */,
+ 404C26E71D89C55D000AA13D /* MGLTileSet_Private.h in Headers */,
DA88485C1CBAFB9800AB86E3 /* MGLFaux3DUserLocationAnnotationView.h in Headers */,
DA8848871CBB033F00AB86E3 /* Fabric.h in Headers */,
350098D81D4830D5004B2AF0 /* NSString+MGLStyleAttributeAdditions_Private.h in Headers */,
35305D4A1D22AA6A0007D005 /* NSData+MGLAdditions.h in Headers */,
359F57461D2FDDA6005217F1 /* MGLUserLocationAnnotationView_Private.h in Headers */,
+ 404C26E21D89B877000AA13D /* MGLTileSet.h in Headers */,
DA8848841CBB033F00AB86E3 /* FABAttributes.h in Headers */,
DA8847FD1CBAFA5100AB86E3 /* MGLTilePyramidOfflineRegion.h in Headers */,
DA88482F1CBAFA6200AB86E3 /* NSProcessInfo+MGLAdditions.h in Headers */,
@@ -1489,6 +1506,7 @@
3566C7671D4A77BA008152BC /* MGLGeoJSONSource.h in Headers */,
DA35A29F1CC9E94C00E826B2 /* MGLCoordinateFormatter.h in Headers */,
DABFB8711CBE9A0F00D62B32 /* MGLMapView+MGLCustomStyleLayerAdditions.h in Headers */,
+ 404C26E31D89B877000AA13D /* MGLTileSet.h in Headers */,
DABFB8611CBE99E500D62B32 /* MGLMultiPoint.h in Headers */,
3510FFF11D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h in Headers */,
35E0CFE71D3E501500188327 /* MGLStyle_Private.h in Headers */,
@@ -1515,6 +1533,7 @@
350098B01D47E6F4004B2AF0 /* UIColor+MGLStyleAttributeAdditions.h in Headers */,
DABFB8671CBE99E500D62B32 /* MGLPolygon.h in Headers */,
350098C71D48288B004B2AF0 /* NSNumber+MGLStyleAttributeAdditions_Private.h in Headers */,
+ 404C26E81D89C55D000AA13D /* MGLTileSet_Private.h in Headers */,
3593E5221D529C29006D9365 /* MGLStyleAttribute.h in Headers */,
DABFB8651CBE99E500D62B32 /* MGLOverlay.h in Headers */,
35E79F211D41266300957B9E /* MGLStyleLayer_Private.h in Headers */,
@@ -1857,6 +1876,7 @@
DA2DBBCE1D51E80400D38FF9 /* MGLStyleLayerTests.m in Sources */,
DA35A2C61CCA9F8300E826B2 /* MGLCompassDirectionFormatterTests.m in Sources */,
3575798E1D502EC7000B822E /* MGLRuntimeStylingHelper.m in Sources */,
+ 4085AF091D933DEA00F11B22 /* MGLTileSetTests.mm in Sources */,
357579851D502AF5000B822E /* MGLSymbolStyleLayerTests.m in Sources */,
357579871D502AFE000B822E /* MGLLineStyleLayerTests.m in Sources */,
357579891D502B06000B822E /* MGLCircleStyleLayerTests.m in Sources */,
@@ -1923,6 +1943,7 @@
DA88481F1CBAFA6200AB86E3 /* MGLMultiPoint.mm in Sources */,
DA88482B1CBAFA6200AB86E3 /* MGLTypes.m in Sources */,
4018B1C71CDC287F00F666AF /* MGLAnnotationView.mm in Sources */,
+ 404C26E41D89B877000AA13D /* MGLTileSet.mm in Sources */,
DA88481D1CBAFA6200AB86E3 /* MGLMapCamera.mm in Sources */,
DA8848261CBAFA6200AB86E3 /* MGLPolygon.mm in Sources */,
35B82BFA1D6C5F8400B1B721 /* NSPredicate+MGLAdditions.mm in Sources */,
@@ -1991,6 +2012,7 @@
DAA4E4301CBB730400178DFB /* MGLLocationManager.m in Sources */,
DAA4E4321CBB730400178DFB /* MGLMapView.mm in Sources */,
DAA4E41E1CBB730400178DFB /* MGLMapCamera.mm in Sources */,
+ 404C26E51D89B877000AA13D /* MGLTileSet.mm in Sources */,
4018B1C81CDC287F00F666AF /* MGLAnnotationView.mm in Sources */,
DAA4E4341CBB730400178DFB /* MGLFaux3DUserLocationAnnotationView.m in Sources */,
35B82BFB1D6C5F8400B1B721 /* NSPredicate+MGLAdditions.mm in Sources */,
diff --git a/platform/ios/src/Mapbox.h b/platform/ios/src/Mapbox.h
index 24925d169c..535e00e01e 100644
--- a/platform/ios/src/Mapbox.h
+++ b/platform/ios/src/Mapbox.h
@@ -56,3 +56,4 @@ FOUNDATION_EXPORT const unsigned char MapboxVersionString[];
#import "NSValue+MGLStyleAttributeAdditions.h"
#import "NSString+MGLStyleAttributeAdditions.h"
#import "NSArray+MGLStyleAttributeAdditions.h"
+#import "MGLTileSet.h"
diff --git a/platform/ios/test/MGLTileSetTests.mm b/platform/ios/test/MGLTileSetTests.mm
new file mode 100644
index 0000000000..d77046928c
--- /dev/null
+++ b/platform/ios/test/MGLTileSetTests.mm
@@ -0,0 +1,82 @@
+#import <XCTest/XCTest.h>
+
+#import <Mapbox/Mapbox.h>
+#import "MGLTileSet_Private.h"
+
+#include <mbgl/util/tileset.hpp>
+
+@interface MGLTileSetTests : XCTestCase
+
+@end
+
+@implementation MGLTileSetTests
+
+- (void)testTileSet {
+ // a tile set that provides an mbgl tile set
+ MGLTileSet *tileSet = [[MGLTileSet alloc] initWithTileURLTemplates:@[@"tile.1",
+ @"tile.2",
+ @"tile.3"]];
+ mbgl::Tileset mbglTileset = [tileSet mbglTileset];
+
+ // has the correct URL templates
+ XCTAssertEqual(mbglTileset.tiles.size(), 3);
+ XCTAssertEqual(mbglTileset.tiles[0], "tile.1");
+ XCTAssertEqual(mbglTileset.tiles[1], "tile.2");
+ XCTAssertEqual(mbglTileset.tiles[2], "tile.3");
+
+ // has the default scheme
+ XCTAssertEqual(mbglTileset.scheme, mbgl::Tileset::Scheme::XYZ);
+
+ // when the tile set has no min or max zoom level set
+ tileSet.minimumZoomLevel = nil;
+ tileSet.maximumZoomLevel = nil;
+
+ // the mbgl object has default values for min and max zoom level
+ XCTAssertEqual([tileSet mbglTileset].zoomRange.min, 0);
+ XCTAssertEqual([tileSet mbglTileset].zoomRange.max, 22);
+
+ // when the tile set has min and/or max zoom level set
+ tileSet.minimumZoomLevel = @(1);
+ tileSet.maximumZoomLevel = @(2);
+
+ // the mbgl object reflects the set values for min and max zoom level
+ XCTAssertEqual([tileSet mbglTileset].zoomRange.min, 1);
+ XCTAssertEqual([tileSet mbglTileset].zoomRange.max, 2);
+
+ // when the tile set has an attribution
+ tileSet.attribution = @"my tileset © ©️🎈";
+
+ // the attribution is reflected by the mbgl tileset
+ XCTAssertEqual([tileSet mbglTileset].attribution, tileSet.attribution.UTF8String);
+
+ // when the scheme is changed
+ tileSet.scheme = MGLTileSetSchemeTMS;
+
+ // the scheme is reflected by the mbgl tileset
+ XCTAssertEqual([tileSet mbglTileset].scheme , mbgl::Tileset::Scheme::TMS);
+
+ // a tile set that provides an mbgl tile set and minimum and maximum zoom levels
+ tileSet = [[MGLTileSet alloc] initWithTileURLTemplates:@[@"tile.1"] minimumZoomLevel:15 maximumZoomLevel:20];
+
+ // the zoom levels are reflected by the mbgl tileset
+ XCTAssertEqual([tileSet mbglTileset].zoomRange.min, 15);
+ XCTAssertEqual([tileSet mbglTileset].zoomRange.max, 20);
+}
+
+- (void)testInvalidTileSet {
+ // a tile set that provides an mbgl tile set and invalid (crossed) minimum and maximum zoom levels throws an exception
+ XCTAssertThrowsSpecificNamed([[MGLTileSet alloc] initWithTileURLTemplates:@[@"tile.1"] minimumZoomLevel:10 maximumZoomLevel:9], NSException, @"Invalid minimumZoomLevel");
+
+ // a tile set that provides an mbgl tile set
+ MGLTileSet *tileSet = [[MGLTileSet alloc] initWithTileURLTemplates:@[@"tile.1"]];
+ tileSet.maximumZoomLevel = @(10);
+
+ // when the minimum zoom level is set higher than the maximum zoom level
+ XCTAssertThrowsSpecificNamed(tileSet.minimumZoomLevel = @(11), NSException, @"Invalid minimumZoomLevel");
+
+ // when the maximum zoom level is set lower than the minimum zoom level
+ tileSet.minimumZoomLevel = @(5);
+ XCTAssertThrowsSpecificNamed(tileSet.maximumZoomLevel = @(4), NSException, @"Invalid minimumZoomLevel");
+}
+
+@end