├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.md ├── MBXMapKit.podspec ├── MBXMapKit ├── MBXConstantsAndTypes.h ├── MBXMapKit.h ├── MBXMapKit.m ├── MBXOfflineMapDatabase.h ├── MBXOfflineMapDatabase.m ├── MBXOfflineMapDownloader.h ├── MBXOfflineMapDownloader.m ├── MBXPointAnnotation.h ├── MBXPointAnnotation.m ├── MBXRasterTileOverlay.h ├── MBXRasterTileOverlay.m ├── MBXRasterTileRenderer.h └── MBXRasterTileRenderer.m ├── README-old.md ├── README.md ├── Sample Project ├── MBXMapKit iOS │ ├── Base.lproj │ │ ├── Main_iPad.storyboard │ │ └── Main_iPhone.storyboard │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-120.png │ │ │ ├── Icon-29-1.png │ │ │ ├── Icon-29.png │ │ │ ├── Icon-29@2x-1.png │ │ │ ├── Icon-29@2x.png │ │ │ ├── Icon-29@3x.png │ │ │ ├── Icon-40.png │ │ │ ├── Icon-40@2x-1.png │ │ │ ├── Icon-40@2x.png │ │ │ ├── Icon-40@3x.png │ │ │ ├── Icon-50.png │ │ │ ├── Icon-50@2x.png │ │ │ ├── Icon-57.png │ │ │ ├── Icon-57@2x.png │ │ │ ├── Icon-60@2x.png │ │ │ ├── Icon-60@3x.png │ │ │ ├── Icon-72.png │ │ │ ├── Icon-72@2x.png │ │ │ ├── Icon-76.png │ │ │ └── Icon-76@2x.png │ │ └── LaunchImage.launchimage │ │ │ ├── Contents.json │ │ │ └── Launch.png │ ├── LaunchScreen.xib │ ├── MBXAppDelegate.h │ ├── MBXAppDelegate.m │ ├── MBXMapKit iOS-Info.plist │ ├── MBXMapKit iOS-Prefix.pch │ ├── MBXViewController.h │ ├── MBXViewController.m │ ├── en.lproj │ │ └── InfoPlist.strings │ └── main.m └── MBXMapKit.xcodeproj │ └── project.pbxproj ├── install_docs.sh └── remove_docs.sh /.gitignore: -------------------------------------------------------------------------------- 1 | Sample Project/MBXMapKit.xcodeproj/project.xcworkspace/ 2 | Sample Project/MBXMapKit.xcodeproj/xcuserdata/ 3 | _site/ 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Changelog 2 | --------- 3 | 4 | ### 0.8.0 5 | #### August 21, 2015 6 | 7 | - Minor docs updates & deprecation in favor of the GL-based iOS SDK 2.0.0+ ([#172](https://github.com/mapbox/mbxmapkit/issues/172)). 8 | 9 | 10 | ### 0.7.0 11 | #### February 4, 2015 12 | 13 | - Removed support for the Mapbox `v3` server API, requiring use of [access tokens](https://www.mapbox.com/developers/api/#access-tokens). 14 | 15 | ### 0.6.0 16 | #### February 3, 2015 17 | 18 | - Re-added support for Mapbox `v4` API and [access tokens](https://www.mapbox.com/developers/api/#access-tokens) in tile and metadata requests. See `MBXMapKit.setAccessToken()`. 19 | - Added a new `MBXRasterTileRenderer` replacement for `MKTileOverlayRenderer` to support `512px` raster tiles and work around some iOS bugs. 20 | - Added a category method on `MKMapView` for obtaining the current zoom level. See `MKMapView.mbx_zoomLevel`. 21 | - Added a delegate callback for tile overlay rendering completion, similar to `MKMapViewDelegate.mapViewDidFinishRenderingMap(_, fullyRendered)`. See `MBXRasterTileOverlayDelegate.tileOverlayDidFinishRendering(_, fullyRendered)`. 22 | - Fixed a bug with setting the zoom level in a category method. 23 | - Removed official support for OS X from CocoaPods. 24 | - Updated the way that deprecated methods and properties are marked. 25 | - Documentation improvements. 26 | 27 | ### 0.5.0 28 | #### September 4, 2014 29 | 30 | - Temporarily removed support for Mapbox `v4` API and [access tokens](https://www.mapbox.com/developers/api/#access-tokens) in tile and metadata requests. 31 | 32 | ### 0.4.0 33 | #### August 14, 2014 34 | 35 | - Added support for Mapbox `v4` API and [access tokens](https://www.mapbox.com/developers/api/#access-tokens) in tile and metadata requests. 36 | - Added `uniqueID` property to offline map databases for use when multiple regions with the same `mapID` are saved. 37 | - Added `creationDate` reference property to offline map databases. 38 | - Now ensures that all Mapbox API requests are over HTTPS. 39 | - Moved to `@import` modules for UIKit/AppKit linking requirements. 40 | - Fixed a bug with marker icon request URL formatting. 41 | - Some light refactoring. 42 | 43 | ### 0.3.0 44 | #### June 12, 2014 45 | 46 | - Major refactor of styling integration. 47 | - Instead of providing an `MBXMapView` subclass of Apple's `MKMapView`, there is now a `MBXRasterTileOverlay` class which can be added directly to a stock `MKMapView` just like Apple's `MKTileOverlay`. 48 | - `MBXRasterTileOverlay` has support for Mapbox map IDs and optionally setting the map center and zoom from Mapbox metadata as well as optionally auto-adding server-specified (Mapbox simplestyle) markers. 49 | - Includes a new `MBXRasterTileOverlayDelegate` protocol for callbacks pertaining to asynchronous marker and metadata loading, including errors received. 50 | - First-class offline map database creation and subsequent use with `MBXOfflineMapDownloader` and its `MBXOfflineMapDatabase` document objects. 51 | - Includes optional support for saving offline JSON metadata and marker imagery as well. 52 | - Includes a new `MBXOfflineMapDownloaderDelegate` protocol for receiving updates to downloader progress and state, including errors received. 53 | - Support for `NSURLCache` shared performance cache for network requests, which is now separate and distinct from offline map functionality. 54 | - Added class `MBXPointAnnotation` for easier custom imagery. Used by `MBXRasterTileOverlay` when auto-adding Mapbox markers. 55 | - Global, configurable user agent for Mapbox API requests with `+[MBXMapKit setUserAgent:]`. 56 | - Prefixed category methods on Apple classes with `mbx_` for namespace safety. 57 | - Bug fixes and performance improvements. 58 | 59 | ### 0.2.1 60 | #### March 6, 2014 61 | 62 | - Fixed a bug where a user-set map view delegate wasn't consulted for annotation views. 63 | 64 | ### 0.2.0 65 | #### March 5, 2014 66 | 67 | - Support for [simplestyle GeoJSON](https://www.mapbox.com/developers/api/maps/#geojson) markers bundled with Mapbox online maps. 68 | - New `MBXPointAnnotation` class supporting the [Mapbox markers API](https://www.mapbox.com/developers/api/static/#markers). 69 | - Added support for the [Mapbox image quality API](https://www.mapbox.com/developers/api/static/#format). 70 | - Added handling for the `-initWithFrame:` default initializer. 71 | - Updated example map to one that includes server-side markers. 72 | - Improved handling of airplane mode and other offline scenarios. 73 | - Improved documentation. 74 | - Fixed a bug related to asynchronous layer loading overriding previously set starting center coordinate. 75 | - Fixed a bug where a Mapbox map server-set center was not used in the library. 76 | - Fixed a bug where non-HTTP 200 responses could get written to cache and added code to clean up previous instances of the bug. 77 | - Fixed a bug where a custom caching interval was not respected during cache sweeps. 78 | 79 | ### 0.1.0 80 | #### September 18, 2013 81 | 82 | - Initial public release. 83 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | CONTRIBUTING 2 | ------------ 3 | 4 | **Please note that MBXMapKit is deprecated in favor of the Mapbox GL-based [iOS SDK 2.0.0+](https://www.mapbox.com/ios-sdk/). As such, this project is not under active development.** 5 | 6 | If you have a usage question, please email help-at-mapbox.com. 7 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MBXMapKit copyright (c) 2013-2014, Mapbox. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without modification, 4 | are permitted provided that the following conditions are met: 5 | 6 | - Redistributions of source code must retain the above copyright notice, this 7 | list of conditions and the following disclaimer. 8 | - Redistributions in binary form must reproduce the above copyright notice, this 9 | list of conditions and the following disclaimer in the documentation and/or 10 | other materials provided with the distribution. 11 | - Neither the name "MapBox" nor the names of its contributors may be 12 | used to endorse or promote products derived from this software without 13 | specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 19 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 22 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /MBXMapKit.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |m| 2 | 3 | m.name = 'MBXMapKit' 4 | m.version = '0.8.0' 5 | 6 | m.deprecated_in_favor_of = 'Mapbox-iOS-SDK' 7 | 8 | m.summary = 'Lightweight Mapbox integration with MapKit on iOS.' 9 | m.description = 'Lightweight Mapbox integration with MapKit on iOS for custom map styles and complete offline control.' 10 | m.homepage = 'https://www.mapbox.com/mbxmapkit/' 11 | m.license = 'BSD' 12 | m.author = { 'Mapbox' => 'mobile@mapbox.com' } 13 | m.screenshot = 'https://raw.githubusercontent.com/mapbox/mbxmapkit/packaging/screenshot.png' 14 | m.social_media_url = 'https://twitter.com/Mapbox' 15 | 16 | m.source = { :git => 'https://github.com/mapbox/mbxmapkit.git', :tag => m.version.to_s } 17 | 18 | m.ios.deployment_target = '7.0' 19 | 20 | m.source_files = 'MBXMapKit/*.{h,m}' 21 | 22 | m.requires_arc = true 23 | 24 | m.documentation_url = 'https://www.mapbox.com/mbxmapkit/' 25 | 26 | m.library = 'sqlite3' 27 | 28 | end 29 | -------------------------------------------------------------------------------- /MBXMapKit/MBXConstantsAndTypes.h: -------------------------------------------------------------------------------- 1 | // 2 | // MBXConstantsAndTypes.h 3 | // MBXMapKit 4 | // 5 | // Copyright (c) 2014 Mapbox. All rights reserved. 6 | // 7 | 8 | @import Foundation; 9 | 10 | #ifndef MBXMapKit_MBXConstantsAndTypes_h 11 | #define MBXMapKit_MBXConstantsAndTypes_h 12 | 13 | #pragma mark - Library version 14 | 15 | extern NSString *const MBXMapKitVersion; 16 | 17 | #pragma mark - Constants for the MBXMapKit error domain 18 | 19 | /** The MBXMapKit error domain. */ 20 | extern NSString *const MBXMapKitErrorDomain; 21 | /** An HTTP status other than 200 was received. */ 22 | extern NSInteger const MBXMapKitErrorCodeHTTPStatus; 23 | /** A required key is missing from the metadata or markers JSON dictionary. */ 24 | extern NSInteger const MBXMapKitErrorCodeDictionaryMissingKeys; 25 | /** An offline map download was cancelled before completion. */ 26 | extern NSInteger const MBXMapKitErrorCodeDownloadingCanceled; 27 | /** An offline map database does not contain a requested resource. */ 28 | extern NSInteger const MBXMapKitErrorCodeOfflineMapHasNoDataForURL; 29 | /** There was a SQLite error while accessing an offline map database. */ 30 | extern NSInteger const MBXMapKitErrorCodeOfflineMapSqlite; 31 | /** There is a network connectivity problem such as airplane mode. */ 32 | extern NSInteger const MBXMapKitErrorCodeURLSessionConnectivity; 33 | 34 | #pragma mark - Image quality constants 35 | 36 | /** Map tile image quality options. */ 37 | typedef NS_ENUM(NSUInteger, MBXRasterImageQuality) { 38 | /** Full image quality. */ 39 | MBXRasterImageQualityFull = 0, 40 | /** 32 color indexed PNG. */ 41 | MBXRasterImageQualityPNG32 = 1, 42 | /** 64 color indexed PNG. */ 43 | MBXRasterImageQualityPNG64 = 2, 44 | /** 128 color indexed PNG. */ 45 | MBXRasterImageQualityPNG128 = 3, 46 | /** 256 color indexed PNG. */ 47 | MBXRasterImageQualityPNG256 = 4, 48 | /** 70% quality JPEG. */ 49 | MBXRasterImageQualityJPEG70 = 5, 50 | /** 80% quality JPEG. */ 51 | MBXRasterImageQualityJPEG80 = 6, 52 | /** 90% quality JPEG. */ 53 | MBXRasterImageQualityJPEG90 = 7 54 | }; 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /MBXMapKit/MBXMapKit.h: -------------------------------------------------------------------------------- 1 | // 2 | // MBXMapKit.h 3 | // MBXMapKit 4 | // 5 | // Copyright (c) 2014 Mapbox. All rights reserved. 6 | // 7 | 8 | @import Foundation; 9 | @import MapKit; 10 | 11 | #import "MBXConstantsAndTypes.h" 12 | #import "MBXOfflineMapDatabase.h" 13 | #import "MBXOfflineMapDownloader.h" 14 | #import "MBXPointAnnotation.h" 15 | #import "MBXRasterTileOverlay.h" 16 | #import "MBXRasterTileRenderer.h" 17 | 18 | #pragma mark - MKMapView category 19 | 20 | /** This category adds methods to the MapKit framework’s `MKMapView` class. */ 21 | @interface MKMapView (MBXMapKit) 22 | 23 | /** @name Manipulating the Visible Portion of the Map */ 24 | 25 | /** Changes the center coordinate and zoom level of the map and optionally animates the change. 26 | * @param centerCoordinate The new center coordinate for the map. 27 | * @param zoomLevel The new zoom level for the map. Acceptable values range from a minimium of `0` (full world, if able to be shown in the current `frame`) to a maximum of `20` (the highest detail that MapKit supports). 28 | * @param animated Specify `YES` if you want the map view to scroll to the new location or `NO` if you want the map to display the new location immediately. */ 29 | - (void)mbx_setCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate zoomLevel:(NSUInteger)zoomLevel animated:(BOOL)animated; 30 | 31 | /** Returns the current zoom level of the map. */ 32 | - (CGFloat)mbx_zoomLevel; 33 | 34 | @end 35 | 36 | #pragma mark - MBXMapKit global settings 37 | 38 | /** Global convenience methods for the framework. */ 39 | @interface MBXMapKit : NSObject 40 | 41 | /** @name Authorizing Access */ 42 | 43 | /** Sets the global access token for Mapbox API requests. Obtain an access token on your [Mapbox account page](https://www.mapbox.com/account/apps/). 44 | * @param accessToken A Mapbox API access token. */ 45 | + (void)setAccessToken:(NSString *)accessToken; 46 | 47 | /** Returns the global access token for Mapbox API HTTP requests. */ 48 | + (NSString *)accessToken; 49 | 50 | /** @name Using a Custom User Agent */ 51 | 52 | /** Sets the global user agent for Mapbox API HTTP requests. 53 | * 54 | * If unset, defaults to `MBXMapKit` followed by the library version, generic hardware model, and software version information. 55 | * 56 | * Example: `MyMapApp/1.2` 57 | * @param userAgent The desired user agent string. */ 58 | + (void)setUserAgent:(NSString *)userAgent; 59 | 60 | /** Returns the global user agent for Mapbox API HTTP requests. */ 61 | + (NSString *)userAgent; 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /MBXMapKit/MBXMapKit.m: -------------------------------------------------------------------------------- 1 | // 2 | // MBXMapKit.m 3 | // MBXMapKit 4 | // 5 | // Copyright (c) 2014 Mapbox. All rights reserved. 6 | // 7 | 8 | #import "TargetConditionals.h" 9 | 10 | #if TARGET_OS_IPHONE 11 | @import UIKit; 12 | #else 13 | @import AppKit; 14 | #endif 15 | 16 | #import "MBXMapKit.h" 17 | 18 | NSString *const MBXMapKitVersion = @"0.7.0"; 19 | 20 | #pragma mark - Add support to MKMapView for using Mapbox-style center/zoom to configure the visible region 21 | 22 | @implementation MKMapView (MBXMapView) 23 | 24 | - (void)mbx_setCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate zoomLevel:(NSUInteger)zoomLevel animated:(BOOL)animated 25 | { 26 | zoomLevel = zoomLevel > 20 ? 20 : zoomLevel; 27 | 28 | CGFloat longitudeDelta = pow(2.0f, -(CGFloat)zoomLevel) * (self.frame.size.width / 256) * 360; 29 | MKCoordinateRegion region = MKCoordinateRegionMake(centerCoordinate, MKCoordinateSpanMake(0, longitudeDelta)); 30 | [self setRegion:region animated:animated]; 31 | } 32 | 33 | - (CGFloat)mbx_zoomLevel 34 | { 35 | CGFloat zoomLevel = self.region.span.longitudeDelta; 36 | zoomLevel /= 360; 37 | zoomLevel /= (self.frame.size.width / 256); 38 | zoomLevel = log2f(zoomLevel); 39 | zoomLevel = fabs(zoomLevel); 40 | 41 | return zoomLevel; 42 | } 43 | 44 | @end 45 | 46 | #pragma mark - Constants for the MBXMapKit error domain 47 | 48 | NSString *const MBXMapKitErrorDomain = @"MBXMapKitErrorDomain"; 49 | NSInteger const MBXMapKitErrorCodeHTTPStatus = -1; 50 | NSInteger const MBXMapKitErrorCodeDictionaryMissingKeys = -2; 51 | NSInteger const MBXMapKitErrorCodeDownloadingCanceled = -3; 52 | NSInteger const MBXMapKitErrorCodeOfflineMapHasNoDataForURL = -4; 53 | NSInteger const MBXMapKitErrorCodeOfflineMapSqlite = -5; 54 | NSInteger const MBXMapKitErrorCodeURLSessionConnectivity = -6; 55 | 56 | #pragma mark - Global configuration 57 | 58 | @interface MBXMapKit () 59 | 60 | @property (nonatomic) NSString *accessToken; 61 | @property (nonatomic) NSString *userAgent; 62 | 63 | @end 64 | 65 | #pragma mark - 66 | 67 | @implementation MBXMapKit 68 | 69 | + (instancetype)sharedInstance 70 | { 71 | static id _sharedInstance = nil; 72 | static dispatch_once_t onceToken; 73 | 74 | dispatch_once(&onceToken, ^(void) 75 | { 76 | _sharedInstance = [[self alloc] init]; 77 | }); 78 | 79 | return _sharedInstance; 80 | } 81 | 82 | + (void)setAccessToken:(NSString *)accessToken 83 | { 84 | [[MBXMapKit sharedInstance] setAccessToken:accessToken]; 85 | } 86 | 87 | + (NSString *)accessToken 88 | { 89 | NSAssert([[MBXMapKit sharedInstance] accessToken], @"An access token is required in order to use the Mapbox API. Obtain a token on your Mapbox account page at https://www.mapbox.com/account/apps/."); 90 | 91 | return [[MBXMapKit sharedInstance] accessToken]; 92 | } 93 | 94 | + (void)setUserAgent:(NSString *)userAgent 95 | { 96 | [[MBXMapKit sharedInstance] setUserAgent:userAgent]; 97 | } 98 | 99 | + (NSString *)userAgent 100 | { 101 | NSString *userAgent = [[MBXMapKit sharedInstance] userAgent]; 102 | 103 | if ( ! userAgent) 104 | { 105 | #if TARGET_OS_IPHONE 106 | userAgent = [NSString stringWithFormat:@"MBXMapKit %@ (%@/%@)", MBXMapKitVersion, [[UIDevice currentDevice] model], [[UIDevice currentDevice] systemVersion]]; 107 | #else 108 | userAgent = [NSString stringWithFormat:@"MBXMapKit %@ (OS X/%@)", MBXMapKitVersion, [[NSProcessInfo processInfo] operatingSystemVersionString]]; 109 | #endif 110 | 111 | [[MBXMapKit sharedInstance] setUserAgent:userAgent]; 112 | } 113 | 114 | return userAgent; 115 | } 116 | 117 | @end 118 | 119 | #pragma mark - Helpers for creating verbose errors 120 | 121 | @implementation NSError (MBXError) 122 | 123 | + (NSError *)mbx_errorWithCode:(NSInteger)code reason:(NSString *)reason description:(NSString *)description 124 | { 125 | // Return an error in the MBXMapKit error domain with the specified reason and description 126 | // 127 | NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : NSLocalizedString(description, nil), 128 | NSLocalizedFailureReasonErrorKey : NSLocalizedString(reason, nil) }; 129 | 130 | return [NSError errorWithDomain:MBXMapKitErrorDomain code:code userInfo:userInfo]; 131 | } 132 | 133 | 134 | + (NSError *)mbx_errorCannotOpenOfflineMapDatabase:(NSString *)path sqliteError:(const char *)sqliteError 135 | { 136 | return [NSError mbx_errorWithCode:MBXMapKitErrorCodeOfflineMapSqlite reason:[NSString stringWithFormat:@"Unable to open database %@: %@", path, [NSString stringWithUTF8String:sqliteError]] description:@"Failed to open the sqlite offline map database file"]; 137 | } 138 | 139 | + (NSError *)mbx_errorQueryFailedForOfflineMapDatabase:(NSString *)path sqliteError:(const char *)sqliteError 140 | { 141 | return [NSError mbx_errorWithCode:MBXMapKitErrorCodeOfflineMapSqlite reason:[NSString stringWithFormat:@"There was an sqlite error while executing a query on database %@: %@", path, [NSString stringWithUTF8String:sqliteError]] description:@"Failed to execute query"]; 142 | } 143 | 144 | @end 145 | -------------------------------------------------------------------------------- /MBXMapKit/MBXOfflineMapDatabase.h: -------------------------------------------------------------------------------- 1 | // 2 | // MBXOfflineMapDatabase.h 3 | // MBXMapKit 4 | // 5 | // Copyright (c) 2014 Mapbox. All rights reserved. 6 | // 7 | 8 | @import Foundation; 9 | @import MapKit; 10 | 11 | #import "MBXConstantsAndTypes.h" 12 | 13 | #pragma mark - 14 | 15 | /** An instance of the `MBXOfflineMapDatabase` class represents a store of offline map data, including map tiles, JSON metadata, and marker images. 16 | * 17 | * @warning The `MBXOfflineMapDatabase` class is not meant to be instantiated directly. Instead, instances are created and managed by the shared `MBXOfflineMapDownloader` instance. */ 18 | @interface MBXOfflineMapDatabase : NSObject 19 | 20 | 21 | #pragma mark - Properties and methods for accessing stored map data 22 | 23 | /** @name Getting and Setting Properties */ 24 | 25 | /** A unique identifier for the offline map database. */ 26 | @property (readonly, nonatomic) NSString *uniqueID; 27 | 28 | /** The Mapbox map ID from which the map resources in this offline map were downloaded. */ 29 | @property (readonly, nonatomic) NSString *mapID; 30 | 31 | /** Whether this offline map database includes the map's metadata JSON. */ 32 | @property (readonly, nonatomic) BOOL includesMetadata; 33 | 34 | /** Whether this offline map database includes the map's markers JSON and marker icons. */ 35 | @property (readonly, nonatomic) BOOL includesMarkers; 36 | 37 | /** The image quality used to download the raster tile images stored in this offline map database. */ 38 | @property (readonly, nonatomic) MBXRasterImageQuality imageQuality; 39 | 40 | /** The map region which was used to initiate the downloading of the tiles in this offline map database. */ 41 | @property (readonly, nonatomic) MKCoordinateRegion mapRegion; 42 | 43 | /** The minimum zoom limit which was used to initiate the downloading of the tiles in this offline map database. */ 44 | @property (readonly, nonatomic) NSInteger minimumZ; 45 | 46 | /** The maximum zoom limit which was used to initiate the downloading of the tiles in this offline map database. */ 47 | @property (readonly, nonatomic) NSInteger maximumZ; 48 | 49 | /** Whether this offline map database has been invalidated. This is to help prevent the completion handlers in `MBXRasterTileOverlay` from causing problems after overlay layers are removed from an `MKMapView`. */ 50 | @property (readonly, nonatomic, getter=isInvalid) BOOL invalid; 51 | 52 | /** Initial creation date of the offline map database. */ 53 | @property (readonly, nonatomic) NSDate *creationDate; 54 | 55 | - (instancetype)init UNAVAILABLE_ATTRIBUTE; 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /MBXMapKit/MBXOfflineMapDatabase.m: -------------------------------------------------------------------------------- 1 | // 2 | // MBXOfflineMapDatabase.m 3 | // MBXMapKit 4 | // 5 | // Copyright (c) 2014 Mapbox. All rights reserved. 6 | // 7 | 8 | #import "MBXMapKit.h" 9 | 10 | #import 11 | 12 | #pragma mark - Private API for creating verbose errors 13 | 14 | @interface NSError (MBXError) 15 | 16 | + (NSError *)mbx_errorWithCode:(NSInteger)code reason:(NSString *)reason description:(NSString *)description; 17 | 18 | + (NSError *)mbx_errorCannotOpenOfflineMapDatabase:(NSString *)path sqliteError:(const char *)sqliteError; 19 | 20 | + (NSError *)mbx_errorQueryFailedForOfflineMapDatabase:(NSString *)path sqliteError:(const char *)sqliteError; 21 | 22 | @end 23 | 24 | 25 | #pragma mark - 26 | 27 | @interface MBXOfflineMapDatabase () 28 | 29 | @property (readwrite, nonatomic) NSString *uniqueID; 30 | @property (readwrite, nonatomic) NSString *mapID; 31 | @property (readwrite, nonatomic) BOOL includesMetadata; 32 | @property (readwrite, nonatomic) BOOL includesMarkers; 33 | @property (readwrite, nonatomic) MBXRasterImageQuality imageQuality; 34 | @property (readwrite, nonatomic) MKCoordinateRegion mapRegion; 35 | @property (readwrite, nonatomic) NSInteger minimumZ; 36 | @property (readwrite, nonatomic) NSInteger maximumZ; 37 | @property (readwrite, nonatomic) NSString *path; 38 | @property (readwrite, nonatomic) BOOL invalid; 39 | 40 | @property (nonatomic) BOOL initializedProperly; 41 | 42 | @end 43 | 44 | 45 | #pragma mark - 46 | 47 | @implementation MBXOfflineMapDatabase 48 | 49 | 50 | - (instancetype)initWithContentsOfFile:(NSString *)path 51 | { 52 | self = [super init]; 53 | 54 | if(self) 55 | { 56 | _path = path; 57 | 58 | NSString *uniqueID = [self sqliteMetadataForName:@"uniqueID"]; 59 | NSString *mapID = [self sqliteMetadataForName:@"mapID"]; 60 | NSString *includesMetadata = [self sqliteMetadataForName:@"includesMetadata"]; 61 | NSString *includesMarkers = [self sqliteMetadataForName:@"includesMarkers"]; 62 | NSString *imageQuality = [self sqliteMetadataForName:@"imageQuality"]; 63 | NSString *region_latitude = [self sqliteMetadataForName:@"region_latitude"]; 64 | NSString *region_longitude = [self sqliteMetadataForName:@"region_longitude"]; 65 | NSString *region_latitude_delta = [self sqliteMetadataForName:@"region_latitude_delta"]; 66 | NSString *region_longitude_delta = [self sqliteMetadataForName:@"region_longitude_delta"]; 67 | NSString *minimumZ = [self sqliteMetadataForName:@"minimumZ"]; 68 | NSString *maximumZ = [self sqliteMetadataForName:@"maximumZ"]; 69 | 70 | if ( ! uniqueID) 71 | { 72 | uniqueID = [NSString stringWithFormat:@"%@-%@-%@-%@-%@-%@-%@-%f", 73 | mapID, 74 | region_latitude, 75 | region_longitude, 76 | region_latitude_delta, 77 | region_longitude_delta, 78 | minimumZ, 79 | maximumZ, 80 | [[self creationDate] timeIntervalSince1970]]; 81 | } 82 | 83 | if (mapID && includesMetadata && includesMarkers && imageQuality 84 | && region_latitude && region_longitude && region_latitude_delta && region_longitude_delta 85 | && minimumZ && maximumZ 86 | ) 87 | { 88 | // Reaching this point means that the specified database file at path pointed to an sqlite file which had 89 | // all the required values in its metadata table. That means the file passed the test for being a valid 90 | // offline map database. 91 | // 92 | _uniqueID = uniqueID; 93 | _mapID = mapID; 94 | _includesMetadata = [includesMetadata boolValue]; 95 | _includesMarkers = [includesMarkers boolValue]; 96 | 97 | _imageQuality = (MBXRasterImageQuality)[imageQuality integerValue]; 98 | 99 | _mapRegion.center.latitude = [region_latitude doubleValue]; 100 | _mapRegion.center.longitude = [region_longitude doubleValue]; 101 | _mapRegion.span.latitudeDelta = [region_latitude_delta doubleValue]; 102 | _mapRegion.span.longitudeDelta = [region_longitude_delta doubleValue]; 103 | 104 | _minimumZ = [minimumZ integerValue]; 105 | _maximumZ = [maximumZ integerValue]; 106 | 107 | _initializedProperly = YES; 108 | } 109 | else 110 | { 111 | // Reaching this point means the file at path isn't a valid offline map database, so we can't use it. 112 | // 113 | self = nil; 114 | } 115 | } 116 | 117 | return self; 118 | } 119 | 120 | 121 | - (NSData *)dataForURL:(NSURL *)url withError:(NSError **)error 122 | { 123 | // If this assert fails, you may have tried to do something like [[MBXOfflineMapDatabase alloc] init]. Please don't do that! 124 | // The correct approach is to enumerate the [[MBXOfflineMapDownloader sharedOfflineMapDownloader].offlineMapDatabases array property 125 | // or to use the database provided by MBXOfflineMapDownloaderDelegate's -offlineMapDownloader:didCompleteOfflineMapDatabase:withError:. 126 | // Also, the offlineMaps array will only have map databases in it once you have completed downloading at least one offline map region. 127 | // 128 | assert(_initializedProperly); 129 | 130 | NSData *data = [self sqliteDataForURL:url]; 131 | if (!data && error) 132 | { 133 | NSString *reason = [NSString stringWithFormat:@"The offline database has no data for %@",[url absoluteString]]; 134 | *error = [NSError mbx_errorWithCode:MBXMapKitErrorCodeOfflineMapHasNoDataForURL reason:reason description:@"No offline data for key error"]; 135 | } 136 | return data; 137 | } 138 | 139 | 140 | - (void)invalidate 141 | { 142 | // This is to let MBXOfflineMapDownloader mark an MBXOfflineMapDatabase object as invalid when it has been asked to delete 143 | // the backing database on disk. This is important because there's a possibility that an MBXRasterTileOverlay layer could still 144 | // be holding a reference to the MBXOfflineMapDatabase at the time that the backing file is deleted. If that happens, it would 145 | // be a logic error, but it seems like a pretty easy error to make, so this helps to catch it (see assert in MBXRasterTileOverlay). 146 | // 147 | self.invalid = YES; 148 | } 149 | 150 | - (NSDate *)creationDate 151 | { 152 | NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:_path error:nil]; 153 | 154 | if (attributes) return (NSDate *)[attributes objectForKey: NSFileCreationDate]; 155 | 156 | return nil; 157 | } 158 | 159 | #pragma mark - sqlite stuff 160 | 161 | - (NSString *)sqliteMetadataForName:(NSString *)name 162 | { 163 | NSString *query = [NSString stringWithFormat:@"SELECT value FROM metadata WHERE name='%@';",name]; 164 | NSData *data = [self sqliteDataForSingleColumnQuery:query]; 165 | return data ? [[NSString alloc] initWithBytes:data.bytes length:data.length encoding:NSUTF8StringEncoding] : nil; 166 | } 167 | 168 | 169 | - (NSData *)sqliteDataForURL:(NSURL *)url 170 | { 171 | NSString *query = [NSString stringWithFormat:@"SELECT value FROM data WHERE id = (SELECT id from resources WHERE url='%@');", [url absoluteString]]; 172 | NSData *data = [self sqliteDataForSingleColumnQuery:query]; 173 | return data; 174 | } 175 | 176 | 177 | - (NSData *)sqliteDataForSingleColumnQuery:(NSString *)query 178 | { 179 | // MBXMapKit expects libsqlite to have been compiled with SQLITE_THREADSAFE=2 (multi-thread mode), which means 180 | // that it can handle its own thread safety as long as you don't attempt to re-use database connections. 181 | // Since the queries here are all SELECT's, locking for writes shouldn't be an issue. 182 | // Some relevant sqlite documentation: 183 | // - http://sqlite.org/faq.html#q5 184 | // - http://www.sqlite.org/threadsafe.html 185 | // - http://www.sqlite.org/c3ref/threadsafe.html 186 | // - http://www.sqlite.org/c3ref/c_config_covering_index_scan.html#sqliteconfigmultithread 187 | // 188 | assert(sqlite3_threadsafe()==2); 189 | 190 | // Open the database read-only and multi-threaded. The slightly obscure c-style variable names here and below are 191 | // used to stay consistent with the sqlite documentaion. See http://sqlite.org/c3ref/open.html 192 | sqlite3 *db; 193 | int rc; 194 | const char *filename = [_path cStringUsingEncoding:NSUTF8StringEncoding]; 195 | rc = sqlite3_open_v2(filename, &db, SQLITE_OPEN_READONLY | SQLITE_OPEN_NOMUTEX, NULL); 196 | if (rc) 197 | { 198 | NSLog(@"Can't open database %@: %s", _path, sqlite3_errmsg(db)); 199 | sqlite3_close(db); 200 | return nil; 201 | } 202 | 203 | // Prepare the query, see http://sqlite.org/c3ref/prepare.html 204 | const char *zSql = [query cStringUsingEncoding:NSUTF8StringEncoding]; 205 | int nByte = (int)[query lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; 206 | sqlite3_stmt *ppStmt; 207 | const char *pzTail; 208 | rc = sqlite3_prepare_v2(db, zSql, nByte, &ppStmt, &pzTail); 209 | if (rc) 210 | { 211 | NSLog(@"Problem preparing sql statement: %s", sqlite3_errmsg(db)); 212 | sqlite3_finalize(ppStmt); 213 | sqlite3_close(db); 214 | return nil; 215 | } 216 | 217 | // Evaluate the query, see http://sqlite.org/c3ref/step.html and http://sqlite.org/c3ref/column_blob.html 218 | NSData *data = nil; 219 | rc = sqlite3_step(ppStmt); 220 | if (rc == SQLITE_ROW) 221 | { 222 | // The query is supposed to be for exactly one column 223 | assert(sqlite3_column_count(ppStmt)==1); 224 | 225 | // Success! 226 | data = [NSData dataWithBytes:sqlite3_column_blob(ppStmt, 0) length:sqlite3_column_bytes(ppStmt, 0)]; 227 | 228 | // Check if any more rows match 229 | if(sqlite3_step(ppStmt) != SQLITE_DONE) 230 | { 231 | // Oops, the query apparently matched more than one row (could also be an error)... not fatal, but not good. 232 | NSLog(@"Warning, query may match more than one row: %@",query); 233 | } 234 | } 235 | else if (rc == SQLITE_DONE) 236 | { 237 | // The query returned no results. 238 | } 239 | else if (rc == SQLITE_BUSY) 240 | { 241 | // This is bad, but theoretically it should never happen 242 | NSLog(@"sqlite3_step() returned SQLITE_BUSY. You probably have a concurrency problem."); 243 | } 244 | else 245 | { 246 | NSLog(@"sqlite3_step() produced an error: %s", sqlite3_errmsg(db)); 247 | } 248 | 249 | // Clean up 250 | sqlite3_finalize(ppStmt); 251 | sqlite3_close(db); 252 | return data; 253 | } 254 | 255 | @end 256 | -------------------------------------------------------------------------------- /MBXMapKit/MBXOfflineMapDownloader.h: -------------------------------------------------------------------------------- 1 | // 2 | // MBXOfflineMapDownloader.h 3 | // MBXMapKit 4 | // 5 | // Copyright (c) 2014 Mapbox. All rights reserved. 6 | // 7 | 8 | @import Foundation; 9 | @import MapKit; 10 | 11 | #pragma mark - Task states 12 | 13 | /** The possible states of the offline map downloader. */ 14 | typedef NS_ENUM(NSUInteger, MBXOfflineMapDownloaderState) { 15 | /** An offline map download job is in progress. */ 16 | MBXOfflineMapDownloaderStateRunning, 17 | /** An offline map download job is suspended and can be either resumed or canceled. */ 18 | MBXOfflineMapDownloaderStateSuspended, 19 | /** An offline map download job is being canceled. */ 20 | MBXOfflineMapDownloaderStateCanceling, 21 | /** The offline map downloader is ready to begin a new offline map download job. */ 22 | MBXOfflineMapDownloaderStateAvailable 23 | }; 24 | 25 | 26 | #pragma mark - Delegate protocol for progress updates 27 | 28 | @class MBXOfflineMapDownloader; 29 | @class MBXOfflineMapDatabase; 30 | 31 | /** The `MBXOfflineMapDownloaderDelegate` protocol provides notifications of download progress and state machine transitions for the shared offline map downloader. */ 32 | @protocol MBXOfflineMapDownloaderDelegate 33 | 34 | @optional 35 | 36 | /** @name Observing Changes to the Downloader's State */ 37 | 38 | /** Notifies the delegate that the offline map downloader's state has changed. This is designed to facilitate user interface updates such as enabling and disabling buttons and network activity indicators. 39 | * @param offlineMapDownloader The offline map downloader whose state has changed. 40 | * @param state The new state of the downloader. */ 41 | - (void)offlineMapDownloader:(MBXOfflineMapDownloader *)offlineMapDownloader stateChangedTo:(MBXOfflineMapDownloaderState)state; 42 | 43 | /** @name Obtaining Information About Download Jobs */ 44 | 45 | /** Notifies the delegate of the number of resources which will be requested as part of the current offline map download job. This is designed to facilitate a user interface update to show a progress indicator, as well as to enable sanity checks for whether the number of tiles being requested is reasonable. 46 | * @param offlineMapDownloader The offline map downloader whose state has changed. 47 | * @param totalFilesExpectedToWrite An estimated count of the number of resources that will be downloaded. This is primarily determined from the map region and zoom limits which were used to begin a download job, but it also potentially includes JSON and marker icon resources. 48 | * 49 | * @warning Since the offline map downloader does not impose any arbitrary upper limit on the number of resources which may be requested from the Mapbox APIs, it is possible to request a very large amount of data. You might want to provide your own checks or accounting mechanisms to manage the number of resources that your apps request from the API. */ 50 | - (void)offlineMapDownloader:(MBXOfflineMapDownloader *)offlineMapDownloader totalFilesExpectedToWrite:(NSUInteger)totalFilesExpectedToWrite; 51 | 52 | /** Notifies the delegate of changes to the percentage completion of an offline map download job. This is designed to facilitate updating a progress indicator. 53 | * @param offlineMapDownloader The offline map downloader. 54 | * @param totalFilesWritten The number of files which have been downloaded and saved to the database on disk. 55 | * @param totalFilesExpectedToWrite An estimated count of the number of resources that will be downloaded. This is primarily determined from the map region and zoom limits which were used to begin a download job, but it also potentially includes JSON and marker icon resources. */ 56 | - (void)offlineMapDownloader:(MBXOfflineMapDownloader *)offlineMapDownloader totalFilesWritten:(NSUInteger)totalFilesWritten totalFilesExpectedToWrite:(NSUInteger)totalFilesExpectedToWrite; 57 | 58 | /** @name Ending Download Jobs */ 59 | 60 | /** Notifies the delegate that something unexpected, but not necessarily bad, has happened. This is designed to provide an opportunity to recognize potential configuration problems with your map. For example, you might receive an HTTP 404 response for a map tile if you request a map region which extends outside of your map data's coverage area. 61 | * @param offlineMapDownloader The offline map downloader. 62 | * @param error The error encountered. */ 63 | - (void)offlineMapDownloader:(MBXOfflineMapDownloader *)offlineMapDownloader didEncounterRecoverableError:(NSError *)error; 64 | 65 | /** Notifies the delegate that an offline map download job has finished. 66 | * 67 | * If the error parameter is `nil`, the job completed successfully. Otherwise, a non-recoverable error was encountered. 68 | * @param offlineMapDownloader The offline map downloader which finished a job. 69 | * @param offlineMapDatabase An offline map database which you can use to create an `MBXRasterTileOverlay`. This paramtere may be `nil` if there was an error. 70 | * @param error The error which stopped the offline map download job. For successful completion, this parameter will be `nil`. */ 71 | - (void)offlineMapDownloader:(MBXOfflineMapDownloader *)offlineMapDownloader didCompleteOfflineMapDatabase:(MBXOfflineMapDatabase *)offlineMapDatabase withError:(NSError *)error; 72 | 73 | @end 74 | 75 | 76 | #pragma mark - 77 | 78 | /** `MBXOfflineMapDownloader` is a class for managing the downloading of offline maps. 79 | * 80 | * A single, shared instance of `MBXOfflineMapDownloader` exists and should be accessed with the `sharedOfflineMapDownloader` class method. */ 81 | @interface MBXOfflineMapDownloader : NSObject 82 | 83 | 84 | #pragma mark - 85 | 86 | /** @name Accessing the Shared Downloader */ 87 | 88 | /** Returns the shared offline map downloader. */ 89 | + (MBXOfflineMapDownloader *)sharedOfflineMapDownloader; 90 | 91 | 92 | #pragma mark - 93 | 94 | /** @name Getting and Setting Attributes */ 95 | 96 | /** The offline map downloader's current state. */ 97 | @property (readonly, nonatomic) MBXOfflineMapDownloaderState state; 98 | 99 | /** If a download job is running or suspended, the map ID which was used to begin that job. */ 100 | @property (readonly, nonatomic) NSString *mapID; 101 | 102 | /** If a download job is running or suspended, whether the job was specified to include metadata. */ 103 | @property (readonly, nonatomic) BOOL includesMetadata; 104 | 105 | /** If a download job is running or suspended, whether the job was specified to include markers. */ 106 | @property (readonly, nonatomic) BOOL includesMarkers; 107 | 108 | /** If a download job is running or suspended, the image quality which was specified for downloading the job's map tiles. */ 109 | @property (readonly, nonatomic) MBXRasterImageQuality imageQuality; 110 | 111 | /** If a download job is running or suspended, the map region which was specified to begin the job. */ 112 | @property (readonly, nonatomic) MKCoordinateRegion mapRegion; 113 | 114 | /** If a download job is running or suspended, the minimum zoom which was specified to begin the job. */ 115 | @property (readonly, nonatomic) NSInteger minimumZ; 116 | 117 | /** If a download job is running or suspended, the maximum zoom which was specified to begin the job. */ 118 | @property (readonly, nonatomic) NSInteger maximumZ; 119 | 120 | /** If a download job is running or suspended, the number of files which have been written so far. */ 121 | @property (readonly,nonatomic) NSUInteger totalFilesWritten; 122 | 123 | /** If a download job is running or suspended, the number of files which still need to be written to finish the job. */ 124 | @property (readonly,nonatomic) NSUInteger totalFilesExpectedToWrite; 125 | 126 | /** An array of `MBXOfflineMapDatabase` objects representing all completed offline map databases on disk. This is designed, in combination with the properties provided by `MBXOfflineMapDatabase`, to allow enumeration and management of the maps which are available on disk. */ 127 | @property (readonly, nonatomic) NSArray *offlineMapDatabases; 128 | 129 | /** Whether offline map databases should be excluded from iCloud and iTunes backups. This defaults to `YES`. If you want to make a change, the value will persist across app launches since it changes the offline map folder's resource value on disk. */ 130 | @property (nonatomic) BOOL offlineMapsAreExcludedFromBackup; 131 | 132 | /** @name Managing the Delegate */ 133 | 134 | /** The delegate which should receive notifications as the offline map downloader's state and progress change. */ 135 | @property (nonatomic) id delegate; 136 | 137 | #pragma mark - 138 | 139 | /** @name Managing Active Download Jobs */ 140 | 141 | /** Begins an offline map download job including metadata and markers using the default (full) image quality. 142 | * @param mapID The map ID from which to download offline map data. 143 | * @param mapRegion The region of the map for which to download tiles. 144 | * @param minimumZ The minimum zoom level for which to download tiles. 145 | * @param maximumZ The maximum zoom level for which to download tiles. 146 | * 147 | * @warning It is recommended to check the return value of the offlineMapDownloader:totalFilesExpectedToWrite: delegate method to ensure that an unexpectedly large number of resources aren't going to be loaded. Map tile counts increase exponentially with increasing zoom level. */ 148 | - (void)beginDownloadingMapID:(NSString *)mapID mapRegion:(MKCoordinateRegion)mapRegion minimumZ:(NSInteger)minimumZ maximumZ:(NSInteger)maximumZ; 149 | 150 | /** Begins an offline map download job using the default (full) image quality. 151 | * @param mapID The map ID from which to download offline map data. 152 | * @param mapRegion The region of the map for which to download tiles. 153 | * @param minimumZ The minimum zoom level for which to download tiles. 154 | * @param maximumZ The maximum zoom level for which to download tiles. 155 | * @param includeMetadata Whether to include the map's metadata (for values such as the initial center point and zoom) in the offline map. 156 | * @param includeMarkers Whether to include the map's marker image resources in the offline map. 157 | * 158 | * @warning It is recommended to check the return value of the offlineMapDownloader:totalFilesExpectedToWrite: delegate method to ensure that an unexpectedly large number of resources aren't going to be loaded. Map tile counts increase exponentially with increasing zoom level. */ 159 | - (void)beginDownloadingMapID:(NSString *)mapID mapRegion:(MKCoordinateRegion)mapRegion minimumZ:(NSInteger)minimumZ maximumZ:(NSInteger)maximumZ includeMetadata:(BOOL)includeMetadata includeMarkers:(BOOL)includeMarkers; 160 | 161 | /** Begins an offline map download job. 162 | * @param mapID The map ID from which to download offline map data. 163 | * @param mapRegion The region of the map for which to download tiles. 164 | * @param minimumZ The minimum zoom level for which to download tiles. 165 | * @param maximumZ The maximum zoom level for which to download tiles. 166 | * @param includeMetadata Whether to include the map's metadata (for values such as the initial center point and zoom) in the offline map. 167 | * @param includeMarkers Whether to include the map's marker image resources in the offline map. 168 | * @param imageQuality The image quality to when requesting tiles. */ 169 | - (void)beginDownloadingMapID:(NSString *)mapID mapRegion:(MKCoordinateRegion)mapRegion minimumZ:(NSInteger)minimumZ maximumZ:(NSInteger)maximumZ includeMetadata:(BOOL)includeMetadata includeMarkers:(BOOL)includeMarkers imageQuality:(MBXRasterImageQuality)imageQuality; 170 | 171 | /** Cancels the current offline map download job and discards any associated resources. */ 172 | - (void)cancel; 173 | 174 | /** Resumes a previously suspended offline map download job. */ 175 | - (void)resume; 176 | 177 | /** Suspends a currently running offline map download job. */ 178 | - (void)suspend; 179 | 180 | /** @name Removing Offline Maps */ 181 | 182 | /** Invalidates a given offline map and removes its associated backing database on disk. This is designed for managing the disk storage consumed by offline maps. 183 | * @param offlineMapDatabase The offline map database to invalidate. */ 184 | - (void)removeOfflineMapDatabase:(MBXOfflineMapDatabase *)offlineMapDatabase; 185 | 186 | /** Invalidates the offline map with the given unique identifier and removes its associated backing database on disk. This is designed for managing the disk storage consumed by offline maps. 187 | * @param uniqueID The unique ID of the map database to invalidate. */ 188 | - (void)removeOfflineMapDatabaseWithID:(NSString *)uniqueID; 189 | 190 | @end 191 | -------------------------------------------------------------------------------- /MBXMapKit/MBXPointAnnotation.h: -------------------------------------------------------------------------------- 1 | // 2 | // MBXPointAnnotation.h 3 | // MBXMapKit 4 | // 5 | // Copyright (c) 2014 Mapbox. All rights reserved. 6 | // 7 | 8 | #import "TargetConditionals.h" 9 | 10 | #if TARGET_OS_IPHONE 11 | @import UIKit; 12 | #else 13 | @import AppKit; 14 | #endif 15 | 16 | @import MapKit; 17 | 18 | /** The `MBXPointAnnotation` class defines a concrete annotation object located at a specified point and with a custom image. You can use this class, rather than define your own, in situations where all you want to do is associate a point on the map with a title. */ 19 | @interface MBXPointAnnotation : MKShape 20 | 21 | /** @name Accessing the Annotation’s Location */ 22 | 23 | /** The coordinate point of the annotation, specified as a latitude and longitude. */ 24 | @property (nonatomic, assign) CLLocationCoordinate2D coordinate; 25 | 26 | /** @name Getting and Setting Attributes */ 27 | 28 | /** The image to show upon display of the corresponding auto-created `MKAnnotationView`. */ 29 | #if TARGET_OS_IPHONE 30 | @property (nonatomic) UIImage *image; 31 | #else 32 | @property (nonatomic) NSImage *image; 33 | #endif 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /MBXMapKit/MBXPointAnnotation.m: -------------------------------------------------------------------------------- 1 | // 2 | // MBXPointAnnotation.m 3 | // MBXMapKit 4 | // 5 | // Copyright (c) 2014 Mapbox. All rights reserved. 6 | // 7 | 8 | #import "MBXPointAnnotation.h" 9 | 10 | @implementation MBXPointAnnotation 11 | 12 | @synthesize coordinate = _coordinate; 13 | 14 | - (CLLocationCoordinate2D)coordinate 15 | { 16 | return _coordinate; 17 | } 18 | 19 | - (void)setCoordinate:(CLLocationCoordinate2D)coordinate 20 | { 21 | [self willChangeValueForKey:@"coordinate"]; 22 | _coordinate = coordinate; 23 | [self didChangeValueForKey:@"coordinate"]; 24 | } 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /MBXMapKit/MBXRasterTileOverlay.h: -------------------------------------------------------------------------------- 1 | // 2 | // MBXRasterTileOverlay.h 3 | // MBXMapKit 4 | // 5 | // Copyright (c) 2014 Mapbox. All rights reserved. 6 | // 7 | 8 | @import Foundation; 9 | @import MapKit; 10 | 11 | #import "MBXConstantsAndTypes.h" 12 | 13 | @class MBXRasterTileOverlay; 14 | @class MBXOfflineMapDatabase; 15 | 16 | #pragma mark - Constants for the MBXMapKit error domain 17 | 18 | extern NSString *const MBXMapKitErrorDomain; 19 | extern NSInteger const MBXMapKitErrorCodeHTTPStatus; 20 | extern NSInteger const MBXMapKitErrorCodeDictionaryMissingKeys; 21 | 22 | #pragma mark - Delegate callbacks for asynchronous loading of map metadata and markers 23 | 24 | /** The `MBXRasterTileOverlayDelegate` protocol provides notifications about asynchronous loading of map metadata and markers for raster image-based maps when using the `MBXRasterTileOverlay` class. */ 25 | @protocol MBXRasterTileOverlayDelegate 26 | 27 | @optional 28 | 29 | /** @name Observing Download Completion */ 30 | 31 | /** Notifies the delegate that asynchronous loading of the maps's metadata JSON is complete. This is designed to facilitate setting an `MKMapView`'s center point, initial zoom, and zoom limits. 32 | * @param overlay The raster tile overlay which has loaded its metadata JSON. 33 | * @param metadata The metadata JSON dictionary. This value may be `nil` if there was an error. 34 | * @param error The error encountered. This is `nil` unless there was an error. */ 35 | - (void)tileOverlay:(MBXRasterTileOverlay *)overlay didLoadMetadata:(NSDictionary *)metadata withError:(NSError *)error; 36 | 37 | /** Notifies the delegate that asynchronous loading of the maps's marker JSON and associated marker icons are complete. This is designed to facilitate adding the array of markers to an `MKMapView`. 38 | * @param overlay The raster tile overlay which has loaded its markers. 39 | * @param markers An array of `MBXPointAnnotation` objects created by parsing the map's marker JSON and loading any referenced marker icons from the Mapbox API. This can be `nil` if the map has no markers or if there was an error. 40 | * @param error The error encountered. This is `nil` unless there was an error. */ 41 | - (void)tileOverlay:(MBXRasterTileOverlay *)overlay didLoadMarkers:(NSArray *)markers withError:(NSError *)error; 42 | 43 | /** Notifies the delegate that there is no more asynchronous work to be done in order to load the map's metadata and markers. This is designed to facilitate hiding a network activity indicator. 44 | * @param overlay The raster tile overlay which is finished loading metadata and markers. */ 45 | - (void)tileOverlayDidFinishLoadingMetadataAndMarkers:(MBXRasterTileOverlay *)overlay; 46 | 47 | /** Notifies the delegate that the map has finished rendering all visible tiles. 48 | * @param overlay The raster tile overlay that was rendering its tiles. 49 | * @param fullyRendered This parameter is set to `YES` if the overlay was able to render all tiles completely or `NO` if errors prevented all tiles from being rendered. */ 50 | - (void)tileOverlayDidFinishRendering:(MBXRasterTileOverlay *)overlay fullyRendered:(BOOL)fullyRendered; 51 | 52 | @end 53 | 54 | 55 | #pragma mark - 56 | 57 | /** The `MBXRasterTileOverlay` class provides an `MKTileOverlay` subclass instance which loads Mapbox-hosted custom-styled map tiles, either live from Mapbox.com, or in offline mode using an `MBXOfflineMapDatabase` instance. 58 | * 59 | * You can use an `MBXRasterTileOverlay` instance with an `MKMapView` map as you would any other `MKTileOverlay`. In particular, the use of multiple overlays on an `MKMapView` is supported as long as the proper values of `canReplaceMapContent` are set for each. Also, it is not possible to change the map ID of an overlay once it has been initialized, but you can easily add and remove overlays using the methods provided by `MKMapView`. 60 | * 61 | * @warning Please note that you are responsible for getting permission to use the map data, and for ensuring your use adheres to the relevant terms of use. 62 | * 63 | * @warning To avoid crashes in `MKMapView` due to asynchronous completion handlers referencing objects that no longer exist, it is very important to call the `invalidateAndCancel` method before removing an `MBXRasterTileOverlay` from your `MKMapView`. */ 64 | @interface MBXRasterTileOverlay : MKTileOverlay 65 | 66 | 67 | #pragma mark - Map tile overlay layer initialization and configuration 68 | 69 | /** @name Initializing a Tile Overlay */ 70 | 71 | /** Initialize a map view with a given Mapbox map ID, automatically loading metadata and markers. 72 | * 73 | * By default, `canReplaceMapContent` will be set to `YES`, which means Apple's maps will be hidden, and if the map ID represents a map with partial-world coverage, areas for which the map has no tiles will appear blank. If you have a full-world map with transparency and wish to show Apple's maps below it, set `canReplaceMapContent` to `NO` before adding your overlay to an `MKMapView`. 74 | * 75 | * Also by default, asynchronous network requests will be started to load the metadata (center coordinate, zoom, etc) and markers associated with your map ID, if there are any. To receive notification when the asynchronous requests complete, set a delegate which implements `MBXRasterTileOverlayDelegate`. 76 | * 77 | * In order for the tile overlay to appear on your map, you must implement `-[MKMapViewDelegate mapView:rendererForOverlay:]` and return an instance of `MBXRasterTileRenderer`. 78 | * 79 | * In order for markers to appear on your map, you must implement `-[MKMapViewDelegate mapView:viewForAnnotation:]` and return an `MKAnnotationView` initialized from an `MBXPointAnnotation`, including the `image` property. 80 | * 81 | * @param mapID The Mapbox map ID. 82 | * @return An initialized raster tile overlay, or `nil` if an overlay could not be initialized. */ 83 | - (instancetype)initWithMapID:(NSString *)mapID; 84 | 85 | /** Initialize a map view with a given Mapbox map ID while specifying whether to load metadata and markers. 86 | * @param mapID The Mapbox map ID. 87 | * @param includeMetadata Whether to load the map's metadata including center coordinate and zoom limits 88 | * @param includeMarkers Whether to load the map's markers 89 | * @return An initialized raster tile overlay, or `nil` if an overlay could not be initialized. */ 90 | - (instancetype)initWithMapID:(NSString *)mapID includeMetadata:(BOOL)includeMetadata includeMarkers:(BOOL)includeMarkers; 91 | 92 | /** Initialize a map view with a given Mapbox map ID while specifying whether to load metadata, whether to load markers, which image quality to request, and which user agent string to use. 93 | * @param mapID The Mapbox map ID. 94 | * @param includeMetadata Whether to load the map's metadata including center coordinate and zoom limits 95 | * @param includeMarkers Whether to load the map's markers 96 | * @param imageQuality The image quality to use for requesting tiles 97 | * @return An initialized raster tile overlay, or `nil` if an overlay could not be initialized. */ 98 | - (instancetype)initWithMapID:(NSString *)mapID includeMetadata:(BOOL)includeMetadata includeMarkers:(BOOL)includeMarkers imageQuality:(MBXRasterImageQuality)imageQuality; 99 | 100 | /** Initialize from an `MBXOfflineMapDatabase` object, using its stored values for metadata and markers, if it has any 101 | * @param offlineMapDatabase An offline map database object obtained from `MBXOfflineMapDownloader` 102 | * @return An initialized raster tile overlay, or `nil` if an overlay could not be initialized. */ 103 | - (instancetype)initWithOfflineMapDatabase:(MBXOfflineMapDatabase *)offlineMapDatabase; 104 | 105 | /** 106 | * Initialize a URL for retrieving custom tiles and tile size handled by the underlying provider 107 | * 108 | * @param urlString Tile server URL 109 | * @param tileSize Tile size 110 | * 111 | * @return An initialized raster tile overlay, or `nil` if an overlay could not be initialized. 112 | */ 113 | - (instancetype)initWithTileURL:(NSString *)urlString tileSize:(NSInteger)tileSize; 114 | 115 | /** @name Accessing the Delegate */ 116 | 117 | /** Delegate to notify of asynchronous resource load completion events. */ 118 | @property (weak, nonatomic) id delegate; 119 | 120 | /** @name Invalidating a Tile Overlay */ 121 | 122 | /** Mark a tile overlay as invalidated and cancel any asynchronous completion handlers for resource downloads. */ 123 | - (void)invalidateAndCancel; 124 | 125 | 126 | #pragma mark - Properties to check initialized values 127 | 128 | /** @name Getting and Setting Properties */ 129 | 130 | /** A Boolean value that indicates whether the tile content is fully opaque. 131 | * 132 | * If the tile content you provide can cover the entire drawing area with opaque content, set this property to `YES`. Doing so serves as a hint to the map view that it does not need to draw any additional content underneath your tiles. Set this property to `NO` if your tiles contain any transparency. 133 | * 134 | * The default value for this property is `YES`. */ 135 | @property (nonatomic) BOOL canReplaceMapContent; 136 | 137 | /** The map ID with which this raster tile overlay was initialized. */ 138 | @property (readonly,nonatomic) NSString *mapID; 139 | /** The map's center coordinate as parsed from the metadata JSON. */ 140 | @property (readonly,nonatomic) CLLocationCoordinate2D center; 141 | /** The map's initial zoom level as parsed from the metadata JSON. */ 142 | @property (readonly,nonatomic) NSInteger centerZoom; 143 | /** The map's array of `MBXPointAnnotation` marker annotations as parsed from the marker JSON. */ 144 | @property (readonly,nonatomic) NSArray *markers; 145 | /** A default plain text attribution message suitable for displaying in an alert dialog. */ 146 | @property (readonly,nonatomic) NSString *attribution; 147 | 148 | 149 | #pragma mark - Methods for invalidating cached metadata and markers 150 | 151 | /** @name Clearing Cached Resources */ 152 | 153 | /** Clear only the cached Metadata JSON (map center point, zoom limits, etc) */ 154 | - (void)clearCachedMetadata; 155 | 156 | /** Clear only the cached Markers JSON */ 157 | - (void)clearCachedMarkers; 158 | 159 | 160 | @end 161 | -------------------------------------------------------------------------------- /MBXMapKit/MBXRasterTileOverlay.m: -------------------------------------------------------------------------------- 1 | // 2 | // MBXRasterTileOverlay.m 3 | // MBXMapKit 4 | // 5 | // Copyright (c) 2014 Mapbox. All rights reserved. 6 | // 7 | 8 | #import "MBXMapKit.h" 9 | 10 | typedef NS_ENUM(NSUInteger, MBXRenderCompletionState) { 11 | MBXRenderCompletionStateUnknown = 0, 12 | MBXRenderCompletionStatePartial = 1, 13 | MBXRenderCompletionStateFull = 2 14 | }; 15 | 16 | typedef void (^MBXRasterTileOverlayWorkerBlock)(NSData *data, NSError **error); 17 | typedef void (^MBXRasterTileOverlayCompletionBlock)(NSData *data, NSError *error); 18 | 19 | #pragma mark - Private API for creating verbose errors 20 | 21 | @interface NSError (MBXError) 22 | 23 | + (NSError *)mbx_errorWithCode:(NSInteger)code reason:(NSString *)reason description:(NSString *)description; 24 | 25 | + (NSError *)mbx_errorCannotOpenOfflineMapDatabase:(NSString *)path sqliteError:(const char *)sqliteError; 26 | 27 | + (NSError *)mbx_errorQueryFailedForOfflineMapDatabase:(NSString *)path sqliteError:(const char *)sqliteError; 28 | 29 | @end 30 | 31 | 32 | #pragma mark - Private API for cooperating with MBXOfflineMapDatabase 33 | 34 | @interface MBXOfflineMapDatabase () 35 | 36 | - (NSData *)dataForURL:(NSURL *)url withError:(NSError **)error; 37 | 38 | @end 39 | 40 | 41 | #pragma mark - 42 | 43 | @interface MBXRasterTileOverlay () 44 | 45 | #pragma mark - Private read-write backing properties for custom tile server 46 | 47 | @property (nonatomic, strong) NSString* overlayTileURLString; 48 | @property (nonatomic, assign) NSInteger overlayTileSize; 49 | 50 | #pragma mark - Private read-write backing properties for public read-only properties 51 | 52 | @property (readwrite,nonatomic) NSString *mapID; 53 | @property (readwrite,nonatomic) MBXRasterImageQuality imageQuality; 54 | @property (readwrite,nonatomic) CLLocationCoordinate2D center; 55 | @property (readwrite,nonatomic) NSInteger centerZoom; 56 | @property (readwrite,nonatomic) NSArray *markers; 57 | @property (readwrite,nonatomic) NSString *attribution; 58 | 59 | #pragma mark - Private properties for rendering completion notification 60 | 61 | @property (nonatomic) NSMutableSet *pendingTileRenders; 62 | @property (nonatomic) MBXRenderCompletionState renderCompletionState; 63 | 64 | #pragma mark - Properties for asynchronous downloading of metadata and markers 65 | 66 | @property (nonatomic) NSDictionary *tileJSONDictionary; 67 | @property (nonatomic) NSDictionary *simplestyleJSONDictionary; 68 | @property (nonatomic) BOOL sessionHasBeenInvalidated; 69 | @property (nonatomic) NSURL *metadataURL; 70 | @property (nonatomic) NSURL *markersURL; 71 | @property (nonatomic) NSMutableArray *mutableMarkers; 72 | @property (nonatomic) NSInteger activeMarkerIconRequests; 73 | @property (nonatomic) BOOL markerIconLoaderMayInitiateDelegateCallback; 74 | @property (nonatomic) BOOL didFinishLoadingMetadata; 75 | @property (nonatomic) BOOL didFinishLoadingMarkers; 76 | 77 | @property (strong, nonatomic) MBXOfflineMapDatabase *offlineMapDatabase; 78 | 79 | @property (nonatomic) NSDictionary *metadataForPendingNotification; 80 | @property (nonatomic) NSError *metadataErrorForPendingNotification; 81 | @property (nonatomic) NSArray *markersForPendingNotification; 82 | @property (nonatomic) NSError *markersErrorForPendingNotification; 83 | @property (nonatomic) BOOL needToNotifyDelegateThatMetadataAndMarkersAreFinished; 84 | 85 | @end 86 | 87 | 88 | #pragma mark - MBXRasterTileOverlay, a subclass of MKTileOverlay 89 | 90 | @implementation MBXRasterTileOverlay 91 | 92 | 93 | #pragma mark - URL utility funtions 94 | 95 | + (NSString *)qualityExtensionForImageQuality:(MBXRasterImageQuality)imageQuality 96 | { 97 | NSString *qualityExtension; 98 | switch (imageQuality) 99 | { 100 | case MBXRasterImageQualityPNG32: 101 | qualityExtension = @"png32"; 102 | break; 103 | case MBXRasterImageQualityPNG64: 104 | qualityExtension = @"png64";; 105 | break; 106 | case MBXRasterImageQualityPNG128: 107 | qualityExtension = @"png128"; 108 | break; 109 | case MBXRasterImageQualityPNG256: 110 | qualityExtension = @"png256"; 111 | break; 112 | case MBXRasterImageQualityJPEG70: 113 | qualityExtension = @"jpg70"; 114 | break; 115 | case MBXRasterImageQualityJPEG80: 116 | qualityExtension = @"jpg80"; 117 | break; 118 | case MBXRasterImageQualityJPEG90: 119 | qualityExtension = @"jpg90"; 120 | break; 121 | case MBXRasterImageQualityFull: 122 | default: 123 | qualityExtension = @"png"; 124 | break; 125 | } 126 | return qualityExtension; 127 | } 128 | 129 | + (NSURLCache *)overlayURLCache 130 | { 131 | return [NSURLCache sharedURLCache]; 132 | } 133 | 134 | + (NSURLRequest *)overlayURLRequestForURL:(NSURL *)requestURL 135 | { 136 | NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:requestURL]; 137 | request.cachePolicy = NSURLRequestUseProtocolCachePolicy; 138 | request.timeoutInterval = 60; 139 | request.allowsCellularAccess = YES; 140 | [request addValue:[MBXMapKit userAgent] forHTTPHeaderField:@"User-Agent"]; 141 | 142 | return request; 143 | } 144 | 145 | + (NSURL *)markerIconURLForSize:(NSString *)size symbol:(NSString *)symbol color:(NSString *)color 146 | { 147 | // Make a string which follows the MapBox Core API spec for stand-alone markers. This relies on the MapBox API 148 | // for error checking. 149 | // 150 | NSMutableString *marker = [[NSMutableString alloc] initWithString:@"pin-"]; 151 | 152 | if ([size hasPrefix:@"l"]) 153 | { 154 | [marker appendString:@"l"]; // large 155 | } 156 | else if ([size hasPrefix:@"s"]) 157 | { 158 | [marker appendString:@"s"]; // small 159 | } 160 | else 161 | { 162 | [marker appendString:@"m"]; // default to medium 163 | } 164 | 165 | if ([symbol length] > 0) 166 | { 167 | [marker appendFormat:@"-%@+",symbol]; 168 | } 169 | else 170 | { 171 | [marker appendString:@"+"]; 172 | } 173 | 174 | [marker appendString:[color stringByReplacingOccurrencesOfString:@"#" withString:@""]]; 175 | 176 | #if TARGET_OS_IPHONE 177 | [marker appendString:([[UIScreen mainScreen] scale] > 1.0 ? @"@2x.png" : @".png")]; 178 | #else 179 | // Making this smart enough to handle a Retina MacBook with a normal dpi external display is complicated. 180 | // For now, just default to @1x images and a 1.0 scale. 181 | // 182 | [marker appendString:@".png"]; 183 | #endif 184 | 185 | return [NSURL URLWithString:[NSString stringWithFormat:@"https://a.tiles.mapbox.com/v4/marker/%@%@", marker, 186 | [@"?access_token=" stringByAppendingString:[MBXMapKit accessToken]]]]; 187 | } 188 | 189 | 190 | #pragma mark - Initialization 191 | 192 | - (instancetype)initWithMapID:(NSString *)mapID; 193 | { 194 | self = [super init]; 195 | if (self) 196 | { 197 | [self setupMapID:mapID includeMetadata:YES includeMarkers:YES imageQuality:MBXRasterImageQualityFull]; 198 | } 199 | return self; 200 | } 201 | 202 | 203 | - (instancetype)initWithMapID:(NSString *)mapID includeMetadata:(BOOL)includeMetadata includeMarkers:(BOOL)includeMarkers 204 | { 205 | self = [super init]; 206 | if (self) 207 | { 208 | [self setupMapID:mapID includeMetadata:includeMetadata includeMarkers:includeMarkers imageQuality:MBXRasterImageQualityFull]; 209 | } 210 | return self; 211 | } 212 | 213 | 214 | - (instancetype)initWithMapID:(NSString *)mapID includeMetadata:(BOOL)includeMetadata includeMarkers:(BOOL)includeMarkers imageQuality:(MBXRasterImageQuality)imageQuality 215 | { 216 | self = [super init]; 217 | if (self) 218 | { 219 | [self setupMapID:mapID includeMetadata:includeMetadata includeMarkers:includeMarkers imageQuality:imageQuality]; 220 | } 221 | return self; 222 | } 223 | 224 | - (instancetype)initWithOfflineMapDatabase:(MBXOfflineMapDatabase *)offlineMapDatabase 225 | { 226 | assert(offlineMapDatabase); 227 | self = [super init]; 228 | if (self) 229 | { 230 | _offlineMapDatabase = offlineMapDatabase; 231 | [self setupMapID:offlineMapDatabase.mapID includeMetadata:offlineMapDatabase.includesMetadata includeMarkers:offlineMapDatabase.includesMarkers imageQuality:offlineMapDatabase.imageQuality]; 232 | } 233 | return self; 234 | } 235 | 236 | - (instancetype)initWithTileURL:(NSString *)urlString tileSize:(NSInteger)tileSize { 237 | self = [super init]; 238 | if (self) 239 | { 240 | self.overlayTileURLString = urlString; 241 | self.overlayTileSize = tileSize; 242 | } 243 | return self; 244 | } 245 | 246 | - (void)setupMapID:(NSString *)mapID includeMetadata:(BOOL)includeMetadata includeMarkers:(BOOL)includeMarkers imageQuality:(MBXRasterImageQuality)imageQuality 247 | { 248 | // Save the map configuration 249 | // 250 | _mapID = mapID; 251 | _imageQuality = imageQuality; 252 | _metadataURL = [NSURL URLWithString:[NSString stringWithFormat:@"https://a.tiles.mapbox.com/v4/%@.json?secure%@", 253 | _mapID, 254 | [@"&access_token=" stringByAppendingString:[MBXMapKit accessToken]]]]; 255 | _markersURL = [NSURL URLWithString:[NSString stringWithFormat:@"https://a.tiles.mapbox.com/v4/%@/features.json%@", 256 | _mapID, 257 | [@"?access_token=" stringByAppendingString:[MBXMapKit accessToken]]]]; 258 | 259 | // Use larger tiles if on retina 260 | // 261 | if ([[UIScreen mainScreen] scale] > 1) self.tileSize = CGSizeMake(512, 512); 262 | 263 | // Default to covering up Apple's map 264 | // 265 | self.canReplaceMapContent = YES; 266 | 267 | // Default attribution 268 | self.attribution = @"© Mapbox\n© OpenStreetMap Contributors"; 269 | 270 | self.pendingTileRenders = [NSMutableSet new]; 271 | 272 | // Initiate asynchronous metadata and marker loading 273 | // 274 | if(includeMetadata) 275 | { 276 | [self asyncLoadMetadata]; 277 | } 278 | else 279 | { 280 | _didFinishLoadingMetadata = YES; 281 | } 282 | 283 | if(includeMarkers) 284 | { 285 | _mutableMarkers = [[NSMutableArray alloc] init]; 286 | [self asyncLoadMarkers]; 287 | } 288 | else 289 | { 290 | _didFinishLoadingMarkers = YES; 291 | } 292 | } 293 | 294 | 295 | - (void)invalidateAndCancel 296 | { 297 | _delegate = nil; 298 | _sessionHasBeenInvalidated = YES; 299 | } 300 | 301 | 302 | #pragma mark - MKTileOverlay implementation 303 | 304 | - (MKMapRect)boundingMapRect 305 | { 306 | // Note: If you're wondering why this doesn't return a MapRect calculated from the TileJSON's bounds, it's been 307 | // tried and it doesn't work, possibly due to an MKMapKit bug. The main symptom is unpredictable visual glitching. 308 | // 309 | return MKMapRectWorld; 310 | } 311 | 312 | 313 | - (void)loadTileAtPath:(MKTileOverlayPath)path result:(void (^)(NSData *tileData, NSError *error))result 314 | { 315 | if (_sessionHasBeenInvalidated) 316 | { 317 | // If an invalidateAndCancel has been called on this tile overlay layer's data session, bail out immediately. 318 | // 319 | return; 320 | } 321 | 322 | NSURL *url = nil; 323 | if(self.overlayTileURLString == nil) { 324 | 325 | url = [NSURL URLWithString:[NSString stringWithFormat:@"https://a.tiles.mapbox.com/v4/%@/%ld/%ld/%ld%@.%@%@", 326 | _mapID, 327 | (long)path.z, 328 | (long)path.x, 329 | (long)path.y, 330 | (path.contentScaleFactor > 1.0 ? @"@2x" : @""), 331 | [MBXRasterTileOverlay qualityExtensionForImageQuality:_imageQuality], 332 | [@"?access_token=" stringByAppendingString:[MBXMapKit accessToken]] 333 | ]]; 334 | } else { 335 | self.tileSize = CGSizeMake(self.overlayTileSize, self.overlayTileSize); 336 | NSString *urlString = [NSString stringWithFormat:self.overlayTileURLString, 337 | (long)path.z, 338 | (long)path.x, 339 | (long)path.y 340 | ]; 341 | 342 | url = [NSURL URLWithString:urlString]; 343 | } 344 | 345 | MBXRasterTileOverlayCompletionBlock completionHandler = ^(NSData *data, NSError *error) { 346 | // Invoke the loadTileAtPath's completion handler 347 | // 348 | if ([NSThread isMainThread]) 349 | { 350 | result(data, error); 351 | } 352 | else 353 | { 354 | dispatch_sync(dispatch_get_main_queue(), ^{ 355 | result(data, error); 356 | }); 357 | } 358 | }; 359 | 360 | [self setRenderCompletionState:MBXRenderCompletionStateFull 361 | ifCurrentStateIs:MBXRenderCompletionStateUnknown]; 362 | 363 | [self addPendingRender:url removePendingRender:nil]; 364 | 365 | [self asyncLoadURL:url workerBlock:nil completionHandler:completionHandler]; 366 | } 367 | 368 | #pragma mark - Delegate Notifications 369 | 370 | - (void)setDelegate:(id)delegate 371 | { 372 | _delegate = delegate; 373 | 374 | // If notifications were attempted between initialization and the time the delegate was set, send 375 | // the saved notifications. This is a normal situation for offline maps because their resources 376 | // load *very* quickly using operation queues on background threads. 377 | // 378 | if(_metadataForPendingNotification || _metadataErrorForPendingNotification) 379 | { 380 | [self notifyDelegateDidLoadMetadata:_metadataForPendingNotification withError:_metadataErrorForPendingNotification]; 381 | } 382 | if(_markersForPendingNotification || _markersErrorForPendingNotification) 383 | { 384 | [self notifyDelegateDidLoadMarkers:_markersForPendingNotification withError:_markersErrorForPendingNotification]; 385 | } 386 | if(_needToNotifyDelegateThatMetadataAndMarkersAreFinished) 387 | { 388 | [self notifyDelegateDidFinishLoadingMetadataAndMarkersForOverlay]; 389 | } 390 | } 391 | 392 | - (void)notifyDelegateDidLoadMetadata:(NSDictionary *)metadata withError:(NSError *)error 393 | { 394 | if([_delegate respondsToSelector:@selector(tileOverlay:didLoadMetadata:withError:)]) 395 | { 396 | _metadataForPendingNotification = nil; 397 | _metadataErrorForPendingNotification = nil; 398 | dispatch_async(dispatch_get_main_queue(), ^{ 399 | [_delegate tileOverlay:self didLoadMetadata:metadata withError:error]; 400 | }); 401 | } 402 | else 403 | { 404 | _metadataForPendingNotification = metadata; 405 | _metadataErrorForPendingNotification = error; 406 | } 407 | } 408 | 409 | 410 | - (void)notifyDelegateDidLoadMarkers:(NSArray *)markers withError:(NSError *)error 411 | { 412 | if([_delegate respondsToSelector:@selector(tileOverlay:didLoadMarkers:withError:)]) 413 | { 414 | _markersForPendingNotification = nil; 415 | _markersErrorForPendingNotification = nil; 416 | dispatch_async(dispatch_get_main_queue(), ^{ 417 | [_delegate tileOverlay:self didLoadMarkers:markers withError:error]; 418 | }); 419 | } 420 | else 421 | { 422 | _markersForPendingNotification = markers; 423 | _markersErrorForPendingNotification = error; 424 | } 425 | } 426 | 427 | 428 | - (void)notifyDelegateDidFinishLoadingMetadataAndMarkersForOverlay 429 | { 430 | if([_delegate respondsToSelector:@selector(tileOverlayDidFinishLoadingMetadataAndMarkers:)]) 431 | { 432 | _needToNotifyDelegateThatMetadataAndMarkersAreFinished = NO; 433 | dispatch_async(dispatch_get_main_queue(), ^{ 434 | [_delegate tileOverlayDidFinishLoadingMetadataAndMarkers:self]; 435 | }); 436 | } 437 | else 438 | { 439 | _needToNotifyDelegateThatMetadataAndMarkersAreFinished = YES; 440 | } 441 | } 442 | 443 | 444 | 445 | #pragma mark - Methods for asynchronous loading of metadata and markers 446 | 447 | - (void)asyncLoadMarkers 448 | { 449 | // This block is run only if data for the URL is successfully retrieved 450 | // 451 | MBXRasterTileOverlayWorkerBlock workerBlock = ^(NSData *data, NSError **error) { 452 | id markers; 453 | id value; 454 | NSDictionary *simplestyleJSONDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:error]; 455 | if(!*error) 456 | { 457 | // Find point features in the markers dictionary (if there are any) and add them to the map. 458 | // 459 | markers = simplestyleJSONDictionary[@"features"]; 460 | 461 | if (markers && [markers isKindOfClass:[NSArray class]]) 462 | { 463 | for (value in (NSArray *)markers) 464 | { 465 | if ([value isKindOfClass:[NSDictionary class]]) 466 | { 467 | NSDictionary *feature = (NSDictionary *)value; 468 | NSString *type = feature[@"geometry"][@"type"]; 469 | 470 | if ([@"Point" isEqualToString:type]) 471 | { 472 | // Only handle point features for now. 473 | // 474 | NSString *longitude = feature[@"geometry"][@"coordinates"][0]; 475 | NSString *latitude = feature[@"geometry"][@"coordinates"][1]; 476 | NSString *title = feature[@"properties"][@"title"]; 477 | NSString *description = feature[@"properties"][@"description"]; 478 | NSString *size = feature[@"properties"][@"marker-size"]; 479 | NSString *color = feature[@"properties"][@"marker-color"]; 480 | NSString *symbol = feature[@"properties"][@"marker-symbol"]; 481 | 482 | if (longitude && latitude && size && color && symbol) 483 | { 484 | // Keep track of how many marker icons are submitted to the download queue 485 | // 486 | _activeMarkerIconRequests += 1; 487 | 488 | MBXPointAnnotation *point = [MBXPointAnnotation new]; 489 | point.title = title; 490 | point.subtitle = description; 491 | point.coordinate = CLLocationCoordinate2DMake([latitude doubleValue], [longitude doubleValue]); 492 | 493 | NSURL *markerURL = [MBXRasterTileOverlay markerIconURLForSize:size symbol:symbol color:color]; 494 | [self asyncLoadMarkerIconURL:(NSURL *)markerURL point:point]; 495 | } 496 | else 497 | { 498 | *error = [self dictionaryErrorMissingImportantKeysFor:@"Markers"]; 499 | } 500 | } 501 | } 502 | // This is the last line of the loop 503 | } 504 | } 505 | } 506 | }; 507 | 508 | // This block runs at the end of all error handling and data processing associated with the URL 509 | // 510 | MBXRasterTileOverlayCompletionBlock completionHandler = ^(NSData *data, NSError *error) { 511 | if(error) { 512 | // At this point, it's possible there was an HTTP or network error. It could also be the 513 | // case that some of the the markers are in the process of successfully loading their icons, 514 | // but there was a problem with some of the marker JSON (e.g. a bug in the Mapbox API). This 515 | // takes the fail early and fail hard approach. Any error whatsoever will prevent all the 516 | // markers from being given to the delegate. The alternative would be to quietly overlook 517 | // the fact that some of the markers probably didn't load properly. 518 | // 519 | _markerIconLoaderMayInitiateDelegateCallback = NO; 520 | [self notifyDelegateDidLoadMarkers:nil withError:error]; 521 | 522 | _didFinishLoadingMarkers = YES; 523 | if(_didFinishLoadingMetadata) { 524 | [self notifyDelegateDidFinishLoadingMetadataAndMarkersForOverlay]; 525 | } 526 | } 527 | else 528 | { 529 | if(_activeMarkerIconRequests <= 0) 530 | { 531 | // Handle the case where all the marker icons URLs finished loading before the markers.geojson/features.json finished parsing 532 | // 533 | _markers = [NSArray arrayWithArray:_mutableMarkers]; 534 | [self notifyDelegateDidLoadMarkers:_markers withError:error]; 535 | 536 | _didFinishLoadingMarkers = YES; 537 | if(_didFinishLoadingMetadata) { 538 | [self notifyDelegateDidFinishLoadingMetadataAndMarkersForOverlay]; 539 | } 540 | _markerIconLoaderMayInitiateDelegateCallback = NO; 541 | } 542 | else 543 | { 544 | // There are still icons loading, so let the last one of those handle the delegate callback 545 | // 546 | _markerIconLoaderMayInitiateDelegateCallback = YES; 547 | } 548 | } 549 | }; 550 | 551 | [self asyncLoadURL:_markersURL workerBlock:workerBlock completionHandler:completionHandler]; 552 | } 553 | 554 | 555 | - (void)asyncLoadMarkerIconURL:(NSURL *)url point:(MBXPointAnnotation *)point 556 | { 557 | // This block is run only if data for the URL is successfully retrieved 558 | // 559 | MBXRasterTileOverlayWorkerBlock workerBlock = ^(NSData *data, NSError **error) { 560 | #if TARGET_OS_IPHONE 561 | point.image = [[UIImage alloc] initWithData:data scale:[[UIScreen mainScreen] scale]]; 562 | #else 563 | point.image = [[NSImage alloc] initWithData:data]; 564 | #endif 565 | 566 | // Add the annotation for this marker icon to the collection of point annotations 567 | // and update the count of marker icons in the download queue 568 | // 569 | [_mutableMarkers addObject:point]; 570 | _activeMarkerIconRequests -= 1; 571 | }; 572 | 573 | // This block runs at the end of all error handling and data processing associated with the URL 574 | // 575 | MBXRasterTileOverlayCompletionBlock completionHandler = ^(NSData *data, NSError *error) { 576 | if(_markerIconLoaderMayInitiateDelegateCallback && _activeMarkerIconRequests <= 0) 577 | { 578 | _markers = [NSArray arrayWithArray:_mutableMarkers]; 579 | [self notifyDelegateDidLoadMarkers:_markers withError:error]; 580 | 581 | _didFinishLoadingMarkers = YES; 582 | if(_didFinishLoadingMetadata) { 583 | [self notifyDelegateDidFinishLoadingMetadataAndMarkersForOverlay]; 584 | } 585 | } 586 | }; 587 | 588 | [self asyncLoadURL:url workerBlock:workerBlock completionHandler:completionHandler]; 589 | } 590 | 591 | 592 | - (void)asyncLoadMetadata 593 | { 594 | // This block is run only if data for the URL is successfully retrieved 595 | // 596 | MBXRasterTileOverlayWorkerBlock workerBlock = ^(NSData *data, NSError **error) { 597 | _tileJSONDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:error]; 598 | if(!*error) 599 | { 600 | if (_tileJSONDictionary 601 | && _tileJSONDictionary[@"minzoom"] 602 | && _tileJSONDictionary[@"maxzoom"] 603 | && _tileJSONDictionary[@"center"] && [_tileJSONDictionary[@"center"] count] == 3 604 | && _tileJSONDictionary[@"bounds"] && [_tileJSONDictionary[@"bounds"] count] == 4) 605 | { 606 | self.minimumZ = [_tileJSONDictionary[@"minzoom"] integerValue]; 607 | self.maximumZ = [_tileJSONDictionary[@"maxzoom"] integerValue]; 608 | 609 | _centerZoom = [_tileJSONDictionary[@"center"][2] integerValue]; 610 | _center.latitude = [_tileJSONDictionary[@"center"][1] doubleValue]; 611 | _center.longitude = [_tileJSONDictionary[@"center"][0] doubleValue]; 612 | 613 | } 614 | else 615 | { 616 | *error = [self dictionaryErrorMissingImportantKeysFor:@"Metadata"]; 617 | } 618 | } 619 | }; 620 | 621 | // This block runs at the end of all error handling and data processing associated with the URL 622 | // 623 | MBXRasterTileOverlayCompletionBlock completionHandler = ^(NSData *data, NSError *error) { 624 | [self notifyDelegateDidLoadMetadata:_tileJSONDictionary withError:error]; 625 | 626 | _didFinishLoadingMetadata = YES; 627 | if(_didFinishLoadingMarkers) { 628 | [self notifyDelegateDidFinishLoadingMetadataAndMarkersForOverlay]; 629 | } 630 | }; 631 | 632 | [self asyncLoadURL:_metadataURL workerBlock:workerBlock completionHandler:completionHandler]; 633 | } 634 | 635 | 636 | - (void)asyncLoadURL:(NSURL *)url workerBlock:(MBXRasterTileOverlayWorkerBlock)workerBlock completionHandler:(MBXRasterTileOverlayCompletionBlock)completionHandler 637 | { 638 | // This method exists to: 639 | // 1. Encapsulte the boilderplate network code for checking HTTP status which is needed for every data session task 640 | // 2. Provide a single configuration point where it is possible to set breakpoints and adjust the caching policy for all HTTP requests 641 | // 3. Provide a hook point for implementing alternate methods (i.e. offline map database) of fetching data for a URL 642 | // 643 | 644 | if (_offlineMapDatabase) 645 | { 646 | // If this assert fails, it's probably because MBXOfflineMapDownloader's removeOfflineMapDatabase: method has been invoked 647 | // for this offline map database object while the database is still associated with a map overlay. That's a serious logic 648 | // error which should be checked for and avoided. 649 | // 650 | assert(_offlineMapDatabase.isInvalid == NO); 651 | 652 | // If an offline map database is configured for this overlay, use the database to fetch data for URLs 653 | // 654 | NSError *error; 655 | NSData *data = [_offlineMapDatabase dataForURL:url withError:&error]; 656 | if(!error) 657 | { 658 | // Since the URL was successfully retrieved, invoke the block to process its data 659 | // 660 | if (workerBlock) workerBlock(data, &error); 661 | } 662 | completionHandler(data,error); 663 | 664 | if (error) 665 | { 666 | [self setRenderCompletionState:MBXRenderCompletionStatePartial 667 | ifCurrentStateIs:MBXRenderCompletionStateFull]; 668 | } 669 | 670 | [self addPendingRender:nil removePendingRender:url]; 671 | } 672 | else 673 | { 674 | // In the normal case, use HTTP network requests to fetch data for URLs 675 | // 676 | [NSURLConnection sendAsynchronousRequest:[[self class] overlayURLRequestForURL:url] 677 | queue:[NSOperationQueue mainQueue] 678 | completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) 679 | { 680 | NSError *outError = nil; 681 | 682 | if (!error) 683 | { 684 | if ([response isKindOfClass:[NSHTTPURLResponse class]] && ((NSHTTPURLResponse *)response).statusCode != 200) 685 | { 686 | outError = [self statusErrorFromHTTPResponse:response]; 687 | } 688 | else 689 | { 690 | // Since the URL was successfully retrieved, invoke the block to process its data 691 | // 692 | if (workerBlock) workerBlock(data, &outError); 693 | } 694 | } 695 | else 696 | { 697 | outError = [error copy]; 698 | } 699 | 700 | completionHandler(data, outError); 701 | 702 | if (outError) 703 | { 704 | [self setRenderCompletionState:MBXRenderCompletionStatePartial 705 | ifCurrentStateIs:MBXRenderCompletionStateFull]; 706 | } 707 | 708 | [self addPendingRender:nil removePendingRender:url]; 709 | }]; 710 | } 711 | } 712 | 713 | - (void)addPendingRender:(NSURL *)addURL removePendingRender:(NSURL *)removeURL 714 | { 715 | dispatch_async(dispatch_get_main_queue(), ^{ 716 | if (addURL) [self.pendingTileRenders addObject:addURL]; 717 | 718 | if ([self.pendingTileRenders containsObject:removeURL]) [self.pendingTileRenders removeObject:removeURL]; 719 | 720 | if ([self.pendingTileRenders count] == 0) 721 | { 722 | [NSObject cancelPreviousPerformRequestsWithTarget:self]; 723 | 724 | [self performSelector:@selector(notifyRenderDelegateWithSuccess:) 725 | withObject:@(self.renderCompletionState == MBXRenderCompletionStateFull) 726 | afterDelay:0.5]; 727 | } 728 | }); 729 | } 730 | 731 | - (void)notifyRenderDelegateWithSuccess:(NSNumber *)flag 732 | { 733 | if (self.delegate && [self.delegate respondsToSelector:@selector(tileOverlayDidFinishRendering:fullyRendered:)]) 734 | { 735 | [self.delegate tileOverlayDidFinishRendering:self fullyRendered:[flag boolValue]]; 736 | } 737 | 738 | [self setRenderCompletionState:MBXRenderCompletionStateUnknown]; 739 | } 740 | 741 | #pragma mark - Helper methods 742 | 743 | - (NSError *)statusErrorFromHTTPResponse:(NSURLResponse *)response 744 | { 745 | // Return an appropriate NSError for any HTTP response other than 200. 746 | // 747 | NSString *reason = [NSString stringWithFormat:@"HTTP status %li was received", (long)((NSHTTPURLResponse *)response).statusCode]; 748 | 749 | return [NSError mbx_errorWithCode:MBXMapKitErrorCodeHTTPStatus reason:reason description:@"HTTP status error"]; 750 | } 751 | 752 | 753 | - (NSError *)dictionaryErrorMissingImportantKeysFor:(NSString *)dictionaryName 754 | { 755 | // Return an appropriate NSError for to indicate that a JSON dictionary was missing important keys. 756 | // 757 | NSString *reason = [NSString stringWithFormat:@"The %@ dictionary is missing important keys", dictionaryName]; 758 | 759 | return [NSError mbx_errorWithCode:MBXMapKitErrorCodeDictionaryMissingKeys reason:reason description:@"Dictionary missing keys error"]; 760 | } 761 | 762 | - (void)setRenderCompletionState:(MBXRenderCompletionState)newState 763 | { 764 | if ( ! [NSThread mainThread]) { 765 | dispatch_async(dispatch_get_main_queue(), ^{ 766 | _renderCompletionState = newState; 767 | }); 768 | } else { 769 | _renderCompletionState = newState; 770 | } 771 | } 772 | 773 | - (void)setRenderCompletionState:(MBXRenderCompletionState)newState ifCurrentStateIs:(MBXRenderCompletionState)checkState 774 | { 775 | if ( ! [NSThread isMainThread]) { 776 | dispatch_async(dispatch_get_main_queue(), ^{ 777 | if (_renderCompletionState == checkState) { 778 | _renderCompletionState = newState; 779 | } 780 | }); 781 | } else { 782 | if (_renderCompletionState == checkState) { 783 | _renderCompletionState = newState; 784 | } 785 | } 786 | } 787 | 788 | #pragma mark - Methods for clearing cached metadata and markers 789 | 790 | - (void)clearCachedMetadata 791 | { 792 | NSURLRequest *request = [[self class] overlayURLRequestForURL:_metadataURL]; 793 | [[[self class] overlayURLCache] removeCachedResponseForRequest:request]; 794 | } 795 | 796 | - (void)clearCachedMarkers 797 | { 798 | NSURLRequest *request = [[self class] overlayURLRequestForURL:_markersURL]; 799 | [[[self class] overlayURLCache] removeCachedResponseForRequest:request]; 800 | } 801 | 802 | 803 | 804 | @end 805 | -------------------------------------------------------------------------------- /MBXMapKit/MBXRasterTileRenderer.h: -------------------------------------------------------------------------------- 1 | // 2 | // MBXRasterTileRenderer.m 3 | // MBXMapKit 4 | // 5 | // Copyright (c) 2014 Mapbox. All rights reserved. 6 | // 7 | 8 | @import MapKit; 9 | 10 | /** An `MBXRasterTileRenderer` object handles the drawing of tiles managed by an `MBXRasterTileOverlay` object. You create instances of this class when tile overlays become visible on the map view. A renderer works closely with its associated tile overlay object to coordinate the loading and drawing of tiles at appropriate times. */ 11 | @interface MBXRasterTileRenderer : MKOverlayRenderer 12 | 13 | /** Initializes and returns a tile renderer with the specified overlay object. 14 | * 15 | * The returned renderer object works with the tile overlay object to coordinate the loading and display of its map tiles. 16 | * 17 | * @param overlay The tile overlay object whose contents you want to draw. 18 | * @return An initialized tile renderer object. */ 19 | - (instancetype)initWithTileOverlay:(MKTileOverlay *)overlay; 20 | 21 | /** Forces tiles to be reloaded and displayed. 22 | * 23 | * Use this method to remove the overlay’s existing tile images and reload them from the original source. This method automatically causes the renderer to redraw the new tiles as soon as they are loaded into memory. */ 24 | - (void)reloadData; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /MBXMapKit/MBXRasterTileRenderer.m: -------------------------------------------------------------------------------- 1 | // 2 | // MBXRasterTileRenderer.m 3 | // MBXMapKit 4 | // 5 | // Copyright (c) 2014 Mapbox. All rights reserved. 6 | // 7 | 8 | #import "MBXRasterTileRenderer.h" 9 | 10 | #pragma mark - Private API 11 | 12 | @interface MBXRasterTileRenderer () 13 | 14 | @property (nonatomic) NSMutableArray *tiles; 15 | @property (nonatomic) NSMutableSet *activeDownloads; 16 | 17 | @end 18 | 19 | @implementation MBXRasterTileRenderer 20 | 21 | #pragma mark - Setup 22 | 23 | - (instancetype)initWithOverlay:(id)overlay { 24 | NSAssert([overlay isKindOfClass:[MKTileOverlay class]], @"overlay must be an MKTileOverlay"); 25 | 26 | self = [super initWithOverlay:overlay]; 27 | 28 | if (self) { 29 | _tiles = [NSMutableArray new]; 30 | 31 | _activeDownloads = [NSMutableSet set]; 32 | 33 | [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidReceiveMemoryWarningNotification 34 | object:[UIApplication sharedApplication] 35 | queue:nil 36 | usingBlock:^(NSNotification *note) { 37 | @synchronized(self) { 38 | [self.tiles removeAllObjects]; 39 | } 40 | }]; 41 | } 42 | 43 | return self; 44 | } 45 | 46 | - (instancetype)initWithTileOverlay:(MKTileOverlay *)overlay { 47 | return [self initWithOverlay:overlay]; 48 | } 49 | 50 | - (void)dealloc { 51 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 52 | } 53 | 54 | #pragma mark - Utility 55 | 56 | - (NSUInteger)cacheMaxSize { 57 | if (((MKTileOverlay *)self.overlay).tileSize.width == 512) { 58 | return 12; 59 | } else { 60 | return 48; 61 | } 62 | } 63 | 64 | - (MKTileOverlayPath)pathForMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale { 65 | MKTileOverlay *tileOverlay = (MKTileOverlay *)self.overlay; 66 | CGFloat factor = tileOverlay.tileSize.width / 256; 67 | 68 | NSInteger x = round(mapRect.origin.x * zoomScale / (tileOverlay.tileSize.width / factor)); 69 | NSInteger y = round(mapRect.origin.y * zoomScale / (tileOverlay.tileSize.width / factor)); 70 | NSInteger z = log2(zoomScale) + 20; 71 | 72 | MKTileOverlayPath path = { 73 | .x = x, 74 | .y = y, 75 | .z = z, 76 | .contentScaleFactor = self.contentScaleFactor 77 | }; 78 | 79 | return path; 80 | } 81 | 82 | + (NSString *)xyzForPath:(MKTileOverlayPath)path { 83 | NSString *xyz = [NSString stringWithFormat:@"%li-%li-%li", 84 | (long)path.x, 85 | (long)path.y, 86 | (long)path.z]; 87 | 88 | return xyz; 89 | } 90 | 91 | + (MKTileOverlayPath)pathForXYZ:(NSString *)xyz scaleFactor:(CGFloat)scaleFactor { 92 | MKTileOverlayPath path = { 93 | .x = [[xyz componentsSeparatedByString:@"-"][0] integerValue], 94 | .y = [[xyz componentsSeparatedByString:@"-"][1] integerValue], 95 | .z = [[xyz componentsSeparatedByString:@"-"][2] integerValue], 96 | .contentScaleFactor = scaleFactor 97 | }; 98 | 99 | return path; 100 | } 101 | 102 | + (void)addImageData:(NSData *)data toRenderer:(MBXRasterTileRenderer *)renderer forXYZ:(NSString *)xyz usingBigTiles:(BOOL)usingBigTiles { 103 | while (renderer.tiles.count >= [renderer cacheMaxSize]) { 104 | [renderer.tiles removeObjectAtIndex:0]; 105 | } 106 | 107 | if (usingBigTiles) { 108 | MKTileOverlayPath parentPath = [[renderer class] pathForXYZ:xyz scaleFactor:renderer.contentScaleFactor]; 109 | parentPath.x /= 2; 110 | parentPath.y /= 2; 111 | parentPath.z--; 112 | 113 | NSString *parentXYZ = [[renderer class] xyzForPath:parentPath]; 114 | 115 | if (![[renderer.tiles valueForKeyPath:@"xyz"] containsObject:parentXYZ]) { 116 | [renderer.tiles addObject:@{ 117 | @"xyz": parentXYZ, 118 | @"data": data 119 | }]; 120 | } 121 | } else { 122 | if (![[renderer.tiles valueForKeyPath:@"xyz"] containsObject:xyz]) { 123 | [renderer.tiles addObject:@{ 124 | @"xyz": xyz, 125 | @"data": data 126 | }]; 127 | } 128 | } 129 | } 130 | 131 | + (NSData *)imageDataFromRenderer:(MBXRasterTileRenderer *)renderer forXYZ:(NSString *)xyz usingBigTiles:(BOOL)usingBigTiles { 132 | NSString *searchXYZ; 133 | if (usingBigTiles) { 134 | MKTileOverlayPath path = [[renderer class] pathForXYZ:xyz scaleFactor:renderer.contentScaleFactor]; 135 | path.x /= 2; 136 | path.y /= 2; 137 | path.z--; 138 | searchXYZ = [[renderer class] xyzForPath:path]; 139 | } else { 140 | searchXYZ = xyz; 141 | } 142 | 143 | NSDictionary *tile = nil; 144 | 145 | for (tile in renderer.tiles) { 146 | if ([tile[@"xyz"] isEqualToString:searchXYZ]) { 147 | break; 148 | } 149 | } 150 | 151 | if (!tile) { 152 | return nil; 153 | } 154 | 155 | [renderer.tiles removeObject:tile]; 156 | [renderer.tiles addObject:tile]; 157 | 158 | return tile[@"data"]; 159 | } 160 | 161 | + (BOOL)dataIsPNG:(NSData *)data { 162 | unsigned char *b = (unsigned char *)data.bytes; 163 | if (data.length > 4 && b[0] == 0x89 && b[1] == 0x50 && b[2] == 0x4e && b[3] == 0x47) { 164 | return YES; 165 | } else { 166 | return NO; 167 | } 168 | } 169 | 170 | + (BOOL)dataIsJPEG:(NSData *)data { 171 | unsigned char *b = (unsigned char *)data.bytes; 172 | if (data.length > 4 && b[0] == 0xff && b[1] == 0xd8 && b[2] == 0xff && b[3] == 0xe0) { 173 | return YES; 174 | } else { 175 | return NO; 176 | } 177 | } 178 | 179 | #pragma mark - MKOverlayRenderer Overrides 180 | 181 | - (BOOL)canDrawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale { 182 | MKTileOverlay *tileOverlay = (MKTileOverlay *)self.overlay; 183 | MKTileOverlayPath path = [self pathForMapRect:mapRect zoomScale:zoomScale]; 184 | BOOL usingBigTiles = (tileOverlay.tileSize.width == 512); 185 | MKTileOverlayPath childPath = path; 186 | 187 | if (usingBigTiles) { 188 | path.x /= 2; 189 | path.y /= 2; 190 | path.z -= 1; 191 | } 192 | 193 | NSString *xyz = [[self class] xyzForPath:childPath]; 194 | NSString *xyzQueue = [[self class] xyzForPath:path]; 195 | BOOL tileReady = NO; 196 | 197 | // introduce a new tileRect that covers the entire region of a 512px tile 198 | MKMapRect tileRect; 199 | 200 | if (usingBigTiles) { 201 | double xTile = 256.0 * path.x / (0.5 * zoomScale); 202 | double yTile = 256.0 * path.y / (0.5 * zoomScale); 203 | double wTile = 2.0 * mapRect.size.width; 204 | 205 | tileRect = MKMapRectMake(xTile, yTile, wTile, wTile); 206 | } else { 207 | tileRect=mapRect; 208 | } 209 | 210 | @synchronized(self) { 211 | tileReady = ([[self class] imageDataFromRenderer:self forXYZ:xyz usingBigTiles:usingBigTiles] != nil); 212 | } 213 | 214 | if (tileReady) { 215 | return YES; 216 | } else { 217 | __weak typeof(self) weakSelf = self; 218 | BOOL tileActive = NO; 219 | @synchronized(weakSelf) { 220 | tileActive = ([weakSelf.activeDownloads containsObject:xyzQueue]); 221 | if ( ! tileActive) { 222 | [weakSelf.activeDownloads addObject:xyzQueue]; 223 | } 224 | } 225 | if ( ! tileActive) { 226 | [(MKTileOverlay *)weakSelf.overlay loadTileAtPath:path result:^(NSData *tileData, NSError *error) { 227 | 228 | @synchronized(weakSelf) { 229 | [weakSelf.activeDownloads removeObject:xyzQueue]; 230 | } 231 | if (tileData) { 232 | NSData *tileDataCopy = [[NSData alloc] initWithBytes:tileData.bytes length:tileData.length]; 233 | 234 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 235 | CGDataProviderRef provider = CGDataProviderCreateWithCFData((CFDataRef)tileDataCopy); 236 | CGImageRef imageRef = nil; 237 | 238 | if ([[weakSelf class] dataIsPNG:tileDataCopy]) { 239 | imageRef = CGImageCreateWithPNGDataProvider(provider, nil, NO, kCGRenderingIntentDefault); 240 | } else if ([[weakSelf class] dataIsJPEG:tileDataCopy]) { 241 | imageRef = CGImageCreateWithJPEGDataProvider(provider, nil, NO, kCGRenderingIntentDefault); 242 | } 243 | 244 | if (imageRef) { 245 | @synchronized(weakSelf) { 246 | [[weakSelf class] addImageData:tileDataCopy 247 | toRenderer:weakSelf 248 | forXYZ:xyz 249 | usingBigTiles:usingBigTiles]; 250 | } 251 | } 252 | 253 | CGImageRelease(imageRef); 254 | CGDataProviderRelease(provider); 255 | 256 | [weakSelf setNeedsDisplayInMapRect:tileRect zoomScale:zoomScale]; 257 | }); 258 | } 259 | }]; 260 | } 261 | return NO; 262 | } 263 | } 264 | 265 | - (void)drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)context { 266 | MKTileOverlayPath path = [self pathForMapRect:mapRect zoomScale:zoomScale]; 267 | NSString *xyz = [[self class] xyzForPath:path]; 268 | NSData *tileData = nil; 269 | 270 | @synchronized(self) { 271 | tileData = [[self class] imageDataFromRenderer:self 272 | forXYZ:xyz 273 | usingBigTiles:(((MKTileOverlay *)self.overlay).tileSize.width == 512)]; 274 | 275 | if (!tileData) { 276 | return [self setNeedsDisplayInMapRect:mapRect zoomScale:zoomScale]; 277 | } 278 | } 279 | 280 | CGImageRef imageRef = nil; 281 | 282 | CGDataProviderRef provider = CGDataProviderCreateWithCFData((CFDataRef)tileData); 283 | if (provider) { 284 | if ([[self class] dataIsPNG:tileData]) { 285 | imageRef = CGImageCreateWithPNGDataProvider(provider, nil, NO, kCGRenderingIntentDefault); 286 | } else if ([[self class] dataIsJPEG:tileData]) { 287 | imageRef = CGImageCreateWithJPEGDataProvider(provider, nil, NO, kCGRenderingIntentDefault); 288 | } 289 | CGDataProviderRelease(provider); 290 | } 291 | 292 | if (!imageRef) { 293 | return [self setNeedsDisplayInMapRect:mapRect zoomScale:zoomScale]; 294 | } 295 | 296 | CGImageRef croppedImageRef = nil; 297 | 298 | if (CGImageGetWidth(imageRef) == 512) { 299 | CGRect cropRect = CGRectMake(0, 0, 256, 256); 300 | cropRect.origin.x += (path.x % 2 ? 256 : 0); 301 | cropRect.origin.y += (path.y % 2 ? 256 : 0); 302 | croppedImageRef = CGImageCreateWithImageInRect(imageRef, cropRect); 303 | } 304 | 305 | CGRect tileRect = CGRectMake(0, 0, 256, 256); 306 | UIGraphicsBeginImageContext(tileRect.size); 307 | CGContextDrawImage(UIGraphicsGetCurrentContext(), tileRect, (croppedImageRef ? croppedImageRef : imageRef)); 308 | CGImageRelease(croppedImageRef); 309 | CGImageRelease(imageRef); 310 | CGImageRef flippedImageRef = UIGraphicsGetImageFromCurrentImageContext().CGImage; 311 | UIGraphicsEndImageContext(); 312 | 313 | CGContextDrawImage(context, [self rectForMapRect:mapRect], flippedImageRef); 314 | } 315 | 316 | #pragma mark - MKTileOverlayRenderer Compatibility 317 | 318 | - (void)reloadData { 319 | [self setNeedsDisplay]; 320 | } 321 | 322 | @end 323 | -------------------------------------------------------------------------------- /README-old.md: -------------------------------------------------------------------------------- 1 | MBXMapKit 2 | --------- 3 | 4 | MBXMapKit extends Apple's MapKit to integrate with maps hosted on [Mapbox](http://mapbox.com), combining the performance of native maps with convenience and integration similar to [Mapbox.js](http://mapbox.com/mapbox.js). With MBXMapKit, your app is responsible for providing and managing its own `MKMapView` instance, while MBXMapKit provides tile overlays, annotations, and an offline map downloader so you can easily display maps from Mapbox both online and offline. 5 | 6 | [![](https://raw.github.com/mapbox/mbxmapkit/packaging/screenshot.png)]() 7 | 8 | ### Features 9 | 10 | The main features which MBXMapKit adds to MapKit are: 11 | 12 | * **Online Maps:** You can initialize a raster tile layer using a [Mapbox map ID](https://www.mapbox.com/help/define-map-id/), and MBXMapKit will handle the details of generating tile URLs and asynchronously loading metadata and markers. With MBXMapKit's delegate callbacks for asynchronously loaded resources, you have the option to immediately start rendering your map, then as the necessary data becomes available, to adjust the visible region and add markers to match how the map is configured in the Mapbox online editor. 13 | 14 | * **Offline Maps:** MBXMapKit includes an offline map downloader to manage the download of all resources (tiles, JSON metadata, and marker icons) necessary to display requested map regions while offline. The offline map downloader provides download progress updates, an internal mechanism for persistant storage of multiple offline map regions, the ability to remove specific offline map regions from disk, and the ability to include offline map data in iCloud backups (the default however is to *exclude* offline map data from backups). 15 | 16 | * **Mapbox Markers:** If you've configured markers for your map using the [Mapbox online editor](https://www.mapbox.com/editor), MBXMapKit makes it easy to add them to your `MKMapView` as `MKShape` annotations. You'll need to include some simple boilerplate code in your view controller's `-[MKMapViewDelegate mapView:viewForAnnotation:]` in order to connect annotations with regular `MKAnnotationView` instances (see the iOS sample app). 17 | 18 | * **Performance Caching:** MBXMapKit uses `NSURLSession` and `NSURLCache` for performance caching of the tiles, JSON metadata, and icons required for loading maps. In contrast to the [Mapbox iOS SDK](http://mapbox.com/mapbox-ios-sdk) and earlier versions of MBXMapKit, this is a traditional cache which is not designed to be used for long term persistence of map data while offline. That capability is provided by a separate mechanism. 19 | 20 | ### Concepts 21 | 22 | The fundamental concept to understand about MBXMapKit is that it is designed to add features to Apple's MapKit rather than to replace or encapsulate MapKit. 23 | 24 | You are responsible for managing your own `MKMapView`, and MBXMapKit will stay out of your way. MBXMapKit provides `MKTileOverlay` and `MKShape` subclasses which you can add to your `MKMapView` in combination with overlays and annotations from other sources. To see how MBXMapKit makes that process fairly painless, and for several examples of configuring `MKMapView` for different visual goals, please take a look at the [view controller in the iOS sample app](./Sample Project/MBXMapKit iOS/MBXViewController.m). 25 | 26 | With MBXMapKit, there is a clear distinction between performance caching and persistent offline storage. If iOS decides it needs to free up disk space by deleting items in app cache directories, offline map data won't be affected. 27 | 28 | ### Requirements 29 | 30 | * iOS 7.0+ 31 | * Xcode 5+ 32 | * Automatic Reference Counting (ARC) 33 | 34 | ### Releases 35 | 36 | Generally speaking, MBXMapKit follows the conventions described by GitHub's [Release Your Software](https://github.com/blog/1547-release-your-software) post. 37 | 38 | Typically we develop new features as branches, and then merge them into the `master` as we are preparing for a release. When an official release is ready, we create a tag with the version number. You can view the list of releases at https://github.com/mapbox/mbxmapkit/releases. 39 | 40 | ### Installation 41 | 42 | To include MBXMapKit in your app you will need to: 43 | 44 | 1. Copy all the `.m` and `.h` files from the `MBXMapKit` folder into your project. 45 | 46 | 1. Make sure that the `.m` files are included in your build target (select the project, select build target, select *Build Phases* tab, expand the *Compile Sources* heading, and make sure all the `.m` files are listed). 47 | 48 | 1. Make sure you have the map capability turned on for your build target (select project, select build target, select *Capabilities* tab, flip the switch to `ON` for Maps). 49 | 50 | 1. Make sure that your build target is linked with `libsqlite3.dylib` (select project, select build target, select *Build Phases* tab, expand *Link Binary With Libraries*, and check the list). When you turn on the map capability, the MapKit framework should be added automatically, but you will probably need to add `libsqlite3.dylib` unless you are already using SQLite for something else. 51 | 52 | 1. Study the view controller in the iOS sample app. It's meant to be liberally copied and pasted. In particular, take a look at `-viewDidLoad`, `-resetMapViewAndRasterOverlayDefaults`, `-actionSheet:clickedButtonAtIndex:`, the `MBXOfflineMapDownloaderDelegate` callbacks, `-mapView:rendererForOverlay:`, `-mapView:viewForAnnotation:`, and the `MBXRasterTileOverlayDelegate` callbacks if you have any questions about how things work. 53 | 54 | 1. **Provide some prominent means to display any applicable map data copyright attribution messages.** For maps which include [OpenStreetMap](http://mapbox.com/about/maps) data, that means you need something which links to the OSM copyright page (see sample app for an example). More details are available at https://www.mapbox.com/help/attribution/ and http://www.openstreetmap.org/copyright. 55 | 56 | ### Support 57 | 58 | If you have questions about how to use MBXMapKit, or are encountering problems, here are our suggestions for how to proceed: 59 | 60 | 1. Read all of this README, review the documentation in the MBXMapKit header files, and check if what you're trying to do is similar to anything in the sample app. 61 | 62 | 2. Search the web for your problem or error message. This can be very helpful for distinguishing MBXMapKit specific problems from more general issues with MKMapKit, and it may also guide you to relevant GitHub issues or StackOverflow questions. In many cases, documentation and blog posts about using MKMapKit will also be applicable to MBXMapKit. 63 | 64 | 3. Familiarize yourself with the documentation and developer resources on Mapbox.com ([Help](https://www.mapbox.com/help/), [Guides](https://www.mapbox.com/guides/), and [Developers](https://www.mapbox.com/developers/)). 65 | 66 | 4. Take a look at the MBXMapKit [issues](https://github.com/mapbox/mbxmapkit/issues?state=open) on GitHub. 67 | 68 | 5. Take a look at [Mapbox questions](http://stackoverflow.com/questions/tagged/mapbox?sort=votes&pageSize=100) on StackOverflow. 69 | 70 | 6. If none of that helps, you can file an [issue](https://github.com/mapbox/mbxmapkit/issues?state=open) on GitHub or ask a question on [StackOverflow](http://stackoverflow.com/questions/tagged/mapbox?sort=votes&pageSize=100). 71 | 72 | ### Sample App 73 | 74 | The sample app is meant to demonstrate the full capabilities of MBXMapKit and to show examples of different ways to configure an `MKMapView`. We've also found it to be useful for testing for responsiveness and visual glitching during work on the API implementation. 75 | 76 | A quick tour: 77 | 78 | 1. Start the sample app. 79 | 80 | 1. Tap the info button in the bottom right corner to bring up an action sheet with several map configurations and an option to view the attribution dialog. 81 | 82 | 1. Try the different map configurations, then take a look at `-resetMapViewAndRasterOverlayDefaults` and `-actionSheet:clickedButtonAtIndex:` from `MBXViewController.m` to understand what's going on. The basic idea is that when the map configuration gets switched, the `MKMapView` is reverted to a known state by removing overlays and markers, then new overlays and markers are added for the new configuration. 83 | 84 | 1. Note that *World baselayer, no Apple* map includes several orange swim markers in Santa Cruz, CA. The callout text comes from the map's `features.json` file, which is loaded from the Mapbox API, and the icons are also loaded from the Mapbox API after the necessary specifications are parsed out of `features.json`. The map center coordinate and zoom scale come from the map's metadata JSON (i.e., `your-map-id.json`). The markers, centering, and zoom get applied to the map by way of the view controller's `MBXRasterTileOverlayDelegate` callback implementations. 85 | 86 | 1. Note how *Offline map downloader* provides a view in the center of the screen with controls to begin, cancel, suspend, and resume the downloading of an offline map region. To select the region to be downloaded, just adjust the visible map region before hitting the begin button. When a download is active, you should see the progress indicator at the bottom of the screen. The progress indicator will remain on screen if you switch to other maps. If you kill the app before the download is complete, it should resume when you re-launch the app. 87 | 88 | 1. Note how the *Offline map viewer* will show the most recent offline map region which was completely downloaded at the time you switched to *Offline map viewer*. Note how the offline map includes markers, initial centering, and initial zoom, even when airplane mode is enabled. There is also a button with a confirmation dialog for deleting all the stored maps. While the sample app only shows the most recently downloaded offline map, the API is designed so that you can enumerate all the available offline maps and use whichever ones you want. 89 | 90 | ### Platforms 91 | 92 | MBXMapKit is officially supported for iOS 7.0 and later. While iOS is the main priority, we also hope to keep things OS X friendly (10.9 and later since MapKit is required). That means you may notice instances of `#if TARGET_OS_IPHONE` around `UIImage`/`NSImage` and such, and it's possible the OS X side of some of those things may be broken. 93 | 94 | If you encounter an OS X related problem and want to file an issue or pull request on GitHub, it would be welcome and appreciated. In particular, if you're working on an OS X app which needs something more than Mapbox.js in a `WebView` (offline maps?) we'd [like to hear about it](http://github.com/mapbox/mbxmapkit/issues/new). 95 | 96 | ### See Also 97 | 98 | Check out the [overview guide](http://mapbox.com/mbxmapkit/) for summary details. 99 | 100 | You might also be interested in the [Mapbox iOS SDK](http://mapbox.com/mapbox-ios-sdk/), which is a ground-up rewrite meant as a replacement for Apple's MapKit, not an extension of it. If you've always wished MapKit was open source for complete customizability, the Mapbox iOS SDK is for you. 101 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | MBXMapKit 2 | --------- 3 | 4 | **MBXMapKit is deprecated in favor of the [Mapbox iOS SDK](http://mapbox.com/ios-sdk) 2.0.0 and above. The future of Mapbox maps is vector rendering, and Apple shows no signs of allowing custom vector rendering in MapKit, so we recommend that you use the OpenGL-based Mapbox iOS SDK 2.0.0 instead.** 5 | 6 | **NOTE: Unlimited, per-user Mapbox pricing plans cannot be used with this version of the Mapbox iOS SDK.** 7 | 8 | See [this doc](README-old.md) for the original project details. 9 | -------------------------------------------------------------------------------- /Sample Project/MBXMapKit iOS/Base.lproj/Main_iPad.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 59 | 60 | 61 | 62 | 63 | 73 | 80 | 88 | 98 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | -------------------------------------------------------------------------------- /Sample Project/MBXMapKit iOS/Base.lproj/Main_iPhone.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 35 | 36 | 37 | 38 | 39 | 49 | 56 | 64 | 74 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | -------------------------------------------------------------------------------- /Sample Project/MBXMapKit iOS/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "29x29", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-29.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "29x29", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-29@2x.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-29@3x.png", 19 | "scale" : "3x" 20 | }, 21 | { 22 | "size" : "40x40", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-40@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "40x40", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-40@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "57x57", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-57.png", 37 | "scale" : "1x" 38 | }, 39 | { 40 | "size" : "57x57", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-57@2x.png", 43 | "scale" : "2x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "29x29", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-29-1.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "29x29", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-29@2x-1.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "40x40", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-40.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "40x40", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-40@2x-1.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "50x50", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-50.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "50x50", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-50@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "72x72", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-72.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "72x72", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-72@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "76x76", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-76.png", 109 | "scale" : "1x" 110 | }, 111 | { 112 | "size" : "76x76", 113 | "idiom" : "ipad", 114 | "filename" : "Icon-76@2x.png", 115 | "scale" : "2x" 116 | }, 117 | { 118 | "size" : "120x120", 119 | "idiom" : "car", 120 | "filename" : "Icon-120.png", 121 | "scale" : "1x" 122 | } 123 | ], 124 | "info" : { 125 | "version" : 1, 126 | "author" : "xcode" 127 | } 128 | } -------------------------------------------------------------------------------- /Sample Project/MBXMapKit iOS/Images.xcassets/AppIcon.appiconset/Icon-120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/mbxmapkit/3fdb6e81978d6f76a9785514453566a836fd5d66/Sample Project/MBXMapKit iOS/Images.xcassets/AppIcon.appiconset/Icon-120.png -------------------------------------------------------------------------------- /Sample Project/MBXMapKit iOS/Images.xcassets/AppIcon.appiconset/Icon-29-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/mbxmapkit/3fdb6e81978d6f76a9785514453566a836fd5d66/Sample Project/MBXMapKit iOS/Images.xcassets/AppIcon.appiconset/Icon-29-1.png -------------------------------------------------------------------------------- /Sample Project/MBXMapKit iOS/Images.xcassets/AppIcon.appiconset/Icon-29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/mbxmapkit/3fdb6e81978d6f76a9785514453566a836fd5d66/Sample Project/MBXMapKit iOS/Images.xcassets/AppIcon.appiconset/Icon-29.png -------------------------------------------------------------------------------- /Sample Project/MBXMapKit iOS/Images.xcassets/AppIcon.appiconset/Icon-29@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/mbxmapkit/3fdb6e81978d6f76a9785514453566a836fd5d66/Sample Project/MBXMapKit iOS/Images.xcassets/AppIcon.appiconset/Icon-29@2x-1.png -------------------------------------------------------------------------------- /Sample Project/MBXMapKit iOS/Images.xcassets/AppIcon.appiconset/Icon-29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/mbxmapkit/3fdb6e81978d6f76a9785514453566a836fd5d66/Sample Project/MBXMapKit iOS/Images.xcassets/AppIcon.appiconset/Icon-29@2x.png -------------------------------------------------------------------------------- /Sample Project/MBXMapKit iOS/Images.xcassets/AppIcon.appiconset/Icon-29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/mbxmapkit/3fdb6e81978d6f76a9785514453566a836fd5d66/Sample Project/MBXMapKit iOS/Images.xcassets/AppIcon.appiconset/Icon-29@3x.png -------------------------------------------------------------------------------- /Sample Project/MBXMapKit iOS/Images.xcassets/AppIcon.appiconset/Icon-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/mbxmapkit/3fdb6e81978d6f76a9785514453566a836fd5d66/Sample Project/MBXMapKit iOS/Images.xcassets/AppIcon.appiconset/Icon-40.png -------------------------------------------------------------------------------- /Sample Project/MBXMapKit iOS/Images.xcassets/AppIcon.appiconset/Icon-40@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/mbxmapkit/3fdb6e81978d6f76a9785514453566a836fd5d66/Sample Project/MBXMapKit iOS/Images.xcassets/AppIcon.appiconset/Icon-40@2x-1.png -------------------------------------------------------------------------------- /Sample Project/MBXMapKit iOS/Images.xcassets/AppIcon.appiconset/Icon-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/mbxmapkit/3fdb6e81978d6f76a9785514453566a836fd5d66/Sample Project/MBXMapKit iOS/Images.xcassets/AppIcon.appiconset/Icon-40@2x.png -------------------------------------------------------------------------------- /Sample Project/MBXMapKit iOS/Images.xcassets/AppIcon.appiconset/Icon-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/mbxmapkit/3fdb6e81978d6f76a9785514453566a836fd5d66/Sample Project/MBXMapKit iOS/Images.xcassets/AppIcon.appiconset/Icon-40@3x.png -------------------------------------------------------------------------------- /Sample Project/MBXMapKit iOS/Images.xcassets/AppIcon.appiconset/Icon-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/mbxmapkit/3fdb6e81978d6f76a9785514453566a836fd5d66/Sample Project/MBXMapKit iOS/Images.xcassets/AppIcon.appiconset/Icon-50.png -------------------------------------------------------------------------------- /Sample Project/MBXMapKit iOS/Images.xcassets/AppIcon.appiconset/Icon-50@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/mbxmapkit/3fdb6e81978d6f76a9785514453566a836fd5d66/Sample Project/MBXMapKit iOS/Images.xcassets/AppIcon.appiconset/Icon-50@2x.png -------------------------------------------------------------------------------- /Sample Project/MBXMapKit iOS/Images.xcassets/AppIcon.appiconset/Icon-57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/mbxmapkit/3fdb6e81978d6f76a9785514453566a836fd5d66/Sample Project/MBXMapKit iOS/Images.xcassets/AppIcon.appiconset/Icon-57.png -------------------------------------------------------------------------------- /Sample Project/MBXMapKit iOS/Images.xcassets/AppIcon.appiconset/Icon-57@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/mbxmapkit/3fdb6e81978d6f76a9785514453566a836fd5d66/Sample Project/MBXMapKit iOS/Images.xcassets/AppIcon.appiconset/Icon-57@2x.png -------------------------------------------------------------------------------- /Sample Project/MBXMapKit iOS/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/mbxmapkit/3fdb6e81978d6f76a9785514453566a836fd5d66/Sample Project/MBXMapKit iOS/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png -------------------------------------------------------------------------------- /Sample Project/MBXMapKit iOS/Images.xcassets/AppIcon.appiconset/Icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/mbxmapkit/3fdb6e81978d6f76a9785514453566a836fd5d66/Sample Project/MBXMapKit iOS/Images.xcassets/AppIcon.appiconset/Icon-60@3x.png -------------------------------------------------------------------------------- /Sample Project/MBXMapKit iOS/Images.xcassets/AppIcon.appiconset/Icon-72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/mbxmapkit/3fdb6e81978d6f76a9785514453566a836fd5d66/Sample Project/MBXMapKit iOS/Images.xcassets/AppIcon.appiconset/Icon-72.png -------------------------------------------------------------------------------- /Sample Project/MBXMapKit iOS/Images.xcassets/AppIcon.appiconset/Icon-72@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/mbxmapkit/3fdb6e81978d6f76a9785514453566a836fd5d66/Sample Project/MBXMapKit iOS/Images.xcassets/AppIcon.appiconset/Icon-72@2x.png -------------------------------------------------------------------------------- /Sample Project/MBXMapKit iOS/Images.xcassets/AppIcon.appiconset/Icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/mbxmapkit/3fdb6e81978d6f76a9785514453566a836fd5d66/Sample Project/MBXMapKit iOS/Images.xcassets/AppIcon.appiconset/Icon-76.png -------------------------------------------------------------------------------- /Sample Project/MBXMapKit iOS/Images.xcassets/AppIcon.appiconset/Icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/mbxmapkit/3fdb6e81978d6f76a9785514453566a836fd5d66/Sample Project/MBXMapKit iOS/Images.xcassets/AppIcon.appiconset/Icon-76@2x.png -------------------------------------------------------------------------------- /Sample Project/MBXMapKit iOS/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "extent" : "full-screen", 12 | "idiom" : "iphone", 13 | "subtype" : "retina4", 14 | "filename" : "Launch.png", 15 | "minimum-system-version" : "7.0", 16 | "orientation" : "portrait", 17 | "scale" : "2x" 18 | }, 19 | { 20 | "orientation" : "portrait", 21 | "idiom" : "ipad", 22 | "extent" : "full-screen", 23 | "minimum-system-version" : "7.0", 24 | "scale" : "1x" 25 | }, 26 | { 27 | "orientation" : "landscape", 28 | "idiom" : "ipad", 29 | "extent" : "full-screen", 30 | "minimum-system-version" : "7.0", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "orientation" : "portrait", 35 | "idiom" : "ipad", 36 | "extent" : "full-screen", 37 | "minimum-system-version" : "7.0", 38 | "scale" : "2x" 39 | }, 40 | { 41 | "orientation" : "landscape", 42 | "idiom" : "ipad", 43 | "extent" : "full-screen", 44 | "minimum-system-version" : "7.0", 45 | "scale" : "2x" 46 | } 47 | ], 48 | "info" : { 49 | "version" : 1, 50 | "author" : "xcode" 51 | } 52 | } -------------------------------------------------------------------------------- /Sample Project/MBXMapKit iOS/Images.xcassets/LaunchImage.launchimage/Launch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/mbxmapkit/3fdb6e81978d6f76a9785514453566a836fd5d66/Sample Project/MBXMapKit iOS/Images.xcassets/LaunchImage.launchimage/Launch.png -------------------------------------------------------------------------------- /Sample Project/MBXMapKit iOS/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Sample Project/MBXMapKit iOS/MBXAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // MBXAppDelegate.h 3 | // MBXMapKit iOS Demo v030 4 | // 5 | // Copyright (c) 2014 Mapbox. All rights reserved. 6 | // 7 | 8 | @import UIKit; 9 | 10 | @interface MBXAppDelegate : UIResponder 11 | 12 | @property (strong, nonatomic) UIWindow *window; 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /Sample Project/MBXMapKit iOS/MBXAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // MBXAppDelegate.m 3 | // MBXMapKit iOS Demo v030 4 | // 5 | // Copyright (c) 2014 Mapbox. All rights reserved. 6 | // 7 | 8 | #import "MBXAppDelegate.h" 9 | 10 | @implementation MBXAppDelegate 11 | 12 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 13 | { 14 | // Override point for customization after application launch. 15 | return YES; 16 | } 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /Sample Project/MBXMapKit iOS/MBXMapKit iOS-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | com.mapbox.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 0.1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 0.1.0 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main_iPhone 31 | UIMainStoryboardFile~ipad 32 | Main_iPad 33 | UIRequiredDeviceCapabilities 34 | 35 | armv7 36 | 37 | UISupportedInterfaceOrientations 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationLandscapeLeft 41 | UIInterfaceOrientationLandscapeRight 42 | 43 | UISupportedInterfaceOrientations~ipad 44 | 45 | UIInterfaceOrientationPortrait 46 | UIInterfaceOrientationPortraitUpsideDown 47 | UIInterfaceOrientationLandscapeLeft 48 | UIInterfaceOrientationLandscapeRight 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /Sample Project/MBXMapKit iOS/MBXMapKit iOS-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_3_0 10 | #warning "This project uses features only available in iOS SDK 3.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | #import 15 | #import 16 | #endif 17 | -------------------------------------------------------------------------------- /Sample Project/MBXMapKit iOS/MBXViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // MBXViewController.h 3 | // MBXMapKit iOS Demo v030 4 | // 5 | // Copyright (c) 2014 Mapbox. All rights reserved. 6 | // 7 | 8 | @import UIKit; 9 | @import MapKit; 10 | 11 | #import "MBXMapKit.h" 12 | 13 | @interface MBXViewController : UIViewController 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Sample Project/MBXMapKit iOS/MBXViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // MBXViewController.m 3 | // MBXMapKit iOS Demo v030 4 | // 5 | // Copyright (c) 2014 Mapbox. All rights reserved. 6 | // 7 | 8 | #import "MBXViewController.h" 9 | #import "MBXMapKit.h" 10 | 11 | @interface MBXViewController () 12 | 13 | @property (weak, nonatomic) IBOutlet MKMapView *mapView; 14 | @property (nonatomic) MBXRasterTileOverlay *rasterOverlay; 15 | @property (nonatomic) UIActionSheet *actionSheet; 16 | 17 | @property (weak, nonatomic) IBOutlet UIView *offlineMapProgressView; 18 | @property (weak, nonatomic) IBOutlet UIProgressView *offlineMapProgress; 19 | @property (weak, nonatomic) IBOutlet UIView *offlineMapDownloadControlsView; 20 | @property (weak, nonatomic) IBOutlet UIButton *offlineMapButtonHelp; 21 | @property (weak, nonatomic) IBOutlet UIButton *offlineMapButtonBegin; 22 | @property (weak, nonatomic) IBOutlet UIButton *offlineMapButtonCancel; 23 | @property (weak, nonatomic) IBOutlet UIButton *offlineMapButtonSuspendResume; 24 | @property (weak, nonatomic) IBOutlet UIView *removeOfflineMapsView; 25 | 26 | 27 | @property (nonatomic) BOOL viewHasFinishedLoading; 28 | @property (nonatomic) BOOL currentlyViewingAnOfflineMap; 29 | 30 | @end 31 | 32 | @implementation MBXViewController 33 | 34 | 35 | #pragma mark - Initialization 36 | 37 | - (void)viewDidLoad 38 | { 39 | [super viewDidLoad]; 40 | 41 | // Set the Mapbox access token for API access 42 | // 43 | [MBXMapKit setAccessToken:@"pk.eyJ1IjoianVzdGluIiwiYSI6IlpDbUJLSUEifQ.4mG8vhelFMju6HpIY-Hi5A"]; 44 | 45 | // Configure the amount of storage to use for NSURLCache's shared cache: You can also omit this and allow NSURLCache's 46 | // to use its default cache size. These sizes determines how much storage will be used for performance caching of HTTP 47 | // requests made by MBXOfflineMapDownloader and MBXRasterTileOverlay. Please note that these values apply only to the 48 | // HTTP cache, and persistent offline map data is stored using an entirely separate mechanism. 49 | // 50 | NSUInteger memoryCapacity = 4 * 1024 * 1024; 51 | NSUInteger diskCapacity = 40 * 1024 * 1024; 52 | NSURLCache *urlCache = [[NSURLCache alloc] initWithMemoryCapacity:memoryCapacity diskCapacity:diskCapacity diskPath:nil]; 53 | //[urlCache removeAllCachedResponses]; 54 | [NSURLCache setSharedURLCache:urlCache]; 55 | 56 | // Start with the offline map download progress and controls hidden (progress will be shownn from elsewhere as needed) 57 | // 58 | _offlineMapProgressView.hidden = YES; 59 | _offlineMapDownloadControlsView.hidden = YES; 60 | _removeOfflineMapsView.hidden = YES; 61 | 62 | // Let the shared offline map downloader know that we want to be notified of changes in its state. This will allow us to 63 | // update the download progress indicator and the begin/cancel/suspend/resume buttons 64 | // 65 | MBXOfflineMapDownloader *sharedDownloader = [MBXOfflineMapDownloader sharedOfflineMapDownloader]; 66 | [sharedDownloader setDelegate:self]; 67 | 68 | // Turn off distracting MKMapView features which aren't relevant to this demonstration 69 | _mapView.showsBuildings = NO; 70 | _mapView.rotateEnabled = NO; 71 | _mapView.pitchEnabled = NO; 72 | 73 | // Let the mapView know that we want to use delegate callbacks to provide customized renderers for tile overlays and views 74 | // for annotations. In order to make use of MBXRasterTileOverlay and MBXPointAnnotation, it is essential for your app to set 75 | // this delegate and implement MKMapViewDelegate's mapView:rendererForOverlay: and mapView:(MKMapView *)mapView viewForAnnotation: 76 | // methods. 77 | // 78 | _mapView.delegate = self; 79 | 80 | // Show the network activity spinner in the status bar 81 | // 82 | [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; 83 | 84 | // Configure a raster tile overlay to use the initial sample map 85 | // 86 | _rasterOverlay = [[MBXRasterTileOverlay alloc] initWithMapID:@"examples.map-pgygbwdm"]; 87 | 88 | // Let the raster tile overlay know that we want to be notified when it has asynchronously loaded the sample map's metadata 89 | // (so we can set the map's center and zoom) and the sample map's markers (so we can add them to the map). 90 | // 91 | _rasterOverlay.delegate = self; 92 | 93 | // Add the raster tile overlay to our mapView so that it will immediately start rendering tiles. At this point the MKMapView's 94 | // default center and zoom don't match the center and zoom of the sample map, but that's okay. Adding the layer now will prevent 95 | // a percieved visual glitch in the UI (an empty map), and we'll fix the center and zoom when tileOverlay:didLoadMetadata:withError: 96 | // gets called to notify us that the raster tile overlay has finished asynchronously loading its metadata. 97 | // 98 | [_mapView addOverlay:_rasterOverlay]; 99 | 100 | // If there was a suspended offline map download, resume it... 101 | // Note how the call above to initialize the shared map downloader happens before its delegate can be set. So now, in order 102 | // to know whether there might be a suspended download which was restored from disk, we need to poll and invoke any 103 | // necessary handler functions on our own. 104 | // 105 | if(sharedDownloader.state == MBXOfflineMapDownloaderStateSuspended) 106 | { 107 | [self offlineMapDownloader:sharedDownloader stateChangedTo:MBXOfflineMapDownloaderStateSuspended]; 108 | [self offlineMapDownloader:sharedDownloader totalFilesExpectedToWrite:sharedDownloader.totalFilesExpectedToWrite]; 109 | [self offlineMapDownloader:sharedDownloader totalFilesWritten:sharedDownloader.totalFilesWritten totalFilesExpectedToWrite:sharedDownloader.totalFilesExpectedToWrite]; 110 | [[MBXOfflineMapDownloader sharedOfflineMapDownloader] resume]; 111 | } 112 | } 113 | 114 | 115 | #pragma mark - Things for switching between maps 116 | 117 | - (UIActionSheet *)universalActionSheet 118 | { 119 | // This is the list of options for selecting which map should be shown by the demo app 120 | // 121 | return [[UIActionSheet alloc] initWithTitle:nil delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:nil otherButtonTitles:@"World baselayer, no Apple",@"World overlay, Apple satellite",@"World baselayer, Apple labels",@"Regional baselayer, no Apple",@"Regional overlay, Apple streets",@"Alpha overlay, Apple streets", @"Offline map downloader", @"Offline map viewer", @"Show attribution",nil]; 122 | } 123 | 124 | 125 | - (IBAction)iPadInfoButtonAction:(id)sender { 126 | // This responds to the info button from the iPad storyboard getting pressed 127 | // 128 | if(_actionSheet.visible) { 129 | [_actionSheet dismissWithClickedButtonIndex:_actionSheet.cancelButtonIndex animated:YES]; 130 | _actionSheet = nil; 131 | } else { 132 | _actionSheet = [self universalActionSheet]; 133 | [_actionSheet showFromRect:((UIButton *)sender).frame inView:self.view animated:YES]; 134 | } 135 | } 136 | 137 | 138 | - (IBAction)iPhoneInfoButtonAction:(id)sender { 139 | // This responds to the info button from the iPhone storyboard getting pressed 140 | // 141 | if(_actionSheet.visible) { 142 | [_actionSheet dismissWithClickedButtonIndex:_actionSheet.cancelButtonIndex animated:YES]; 143 | _actionSheet = nil; 144 | } else { 145 | _actionSheet = [self universalActionSheet]; 146 | [_actionSheet showFromRect:((UIButton *)sender).frame inView:self.view animated:YES]; 147 | } 148 | } 149 | 150 | 151 | - (void)resetMapViewAndRasterOverlayDefaults 152 | { 153 | // Reset the MKMapView to some reasonable defaults. 154 | // 155 | _mapView.mapType = MKMapTypeStandard; 156 | _mapView.scrollEnabled = YES; 157 | _mapView.zoomEnabled = YES; 158 | _offlineMapDownloadControlsView.hidden = YES; 159 | _removeOfflineMapsView.hidden = YES; 160 | 161 | // Make sure that any downloads (tiles, metadata, marker icons) which might be in progress for 162 | // the old tile overlay are stopped, and remove the overlay and its markers from the MKMapView. 163 | // The invalidation step is necessary to avoid the possibility of visual glitching or crashes due to 164 | // delegate callbacks or asynchronous completion handlers getting invoked for downloads which might 165 | // be still in progress. 166 | // 167 | [_mapView removeAnnotations:_rasterOverlay.markers]; 168 | [_mapView removeOverlay:_rasterOverlay]; 169 | [_rasterOverlay invalidateAndCancel]; 170 | [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; 171 | _currentlyViewingAnOfflineMap = NO; 172 | } 173 | 174 | 175 | #pragma mark - UIActionSheetDelegate protocol implementation 176 | 177 | - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex 178 | { 179 | // This switches between maps in response to action sheet selections 180 | // 181 | switch(buttonIndex) { 182 | case 0: 183 | { 184 | // OSM world map 185 | [self resetMapViewAndRasterOverlayDefaults]; 186 | [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; 187 | _rasterOverlay = [[MBXRasterTileOverlay alloc] initWithMapID:@"examples.map-pgygbwdm"]; 188 | _rasterOverlay.delegate = self; 189 | [_mapView addOverlay:_rasterOverlay]; 190 | break; 191 | } 192 | case 1: 193 | { 194 | // OSM over Apple satellite 195 | [self resetMapViewAndRasterOverlayDefaults]; 196 | _mapView.mapType = MKMapTypeSatellite; 197 | [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; 198 | _rasterOverlay = [[MBXRasterTileOverlay alloc] initWithMapID:@"justin.map-9tlo4knw" includeMetadata:YES includeMarkers:NO]; 199 | _rasterOverlay.delegate = self; 200 | _rasterOverlay.canReplaceMapContent = NO; 201 | [_mapView addOverlay:_rasterOverlay]; 202 | break; 203 | } 204 | case 2: 205 | { 206 | // Terrain under Apple labels 207 | [self resetMapViewAndRasterOverlayDefaults]; 208 | [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; 209 | _rasterOverlay = [[MBXRasterTileOverlay alloc] initWithMapID:@"justin.map-mf07hryq" includeMetadata:YES includeMarkers:NO]; 210 | _rasterOverlay.delegate = self; 211 | [_mapView insertOverlay:_rasterOverlay atIndex:0 level:MKOverlayLevelAboveRoads]; 212 | break; 213 | } 214 | case 3: 215 | { 216 | // Tilemill bounded region (scroll & zoom limited to programmatic control only) 217 | [self resetMapViewAndRasterOverlayDefaults]; 218 | _mapView.scrollEnabled = NO; 219 | _mapView.zoomEnabled = NO; 220 | [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; 221 | _rasterOverlay = [[MBXRasterTileOverlay alloc] initWithMapID:@"justin.NACIS2012" includeMetadata:YES includeMarkers:NO]; 222 | _rasterOverlay.delegate = self; 223 | [_mapView addOverlay:_rasterOverlay]; 224 | break; 225 | } 226 | case 4: 227 | { 228 | // Tilemill region over Apple 229 | [self resetMapViewAndRasterOverlayDefaults]; 230 | [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; 231 | _rasterOverlay = [[MBXRasterTileOverlay alloc] initWithMapID:@"justin.clp-2011-11-03-1200" includeMetadata:YES includeMarkers:NO]; 232 | _rasterOverlay.delegate = self; 233 | _rasterOverlay.canReplaceMapContent = NO; 234 | [_mapView addOverlay:_rasterOverlay]; 235 | break; 236 | } 237 | case 5: 238 | { 239 | // Tilemill transparent over Apple 240 | [self resetMapViewAndRasterOverlayDefaults]; 241 | [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; 242 | _rasterOverlay = [[MBXRasterTileOverlay alloc] initWithMapID:@"justin.pdx_meters" includeMetadata:YES includeMarkers:NO]; 243 | _rasterOverlay.delegate = self; 244 | _rasterOverlay.canReplaceMapContent = NO; 245 | [_mapView addOverlay:_rasterOverlay]; 246 | break; 247 | } 248 | case 6: 249 | { 250 | // Offline Map Downloader 251 | [self resetMapViewAndRasterOverlayDefaults]; 252 | [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; 253 | _rasterOverlay = [[MBXRasterTileOverlay alloc] initWithMapID:@"examples.map-pgygbwdm" includeMetadata:YES includeMarkers:YES]; 254 | _rasterOverlay.delegate = self; 255 | [_mapView addOverlay:_rasterOverlay]; 256 | _offlineMapDownloadControlsView.hidden = NO; 257 | [self offlineMapDownloader:[MBXOfflineMapDownloader sharedOfflineMapDownloader] stateChangedTo:[[MBXOfflineMapDownloader sharedOfflineMapDownloader] state]]; 258 | break; 259 | } 260 | case 7: 261 | { 262 | // Offline Map Viewer 263 | [self resetMapViewAndRasterOverlayDefaults]; 264 | _currentlyViewingAnOfflineMap = YES; 265 | MBXOfflineMapDatabase *offlineMap = [[[MBXOfflineMapDownloader sharedOfflineMapDownloader] offlineMapDatabases] lastObject]; 266 | if (offlineMap) 267 | { 268 | _rasterOverlay = [[MBXRasterTileOverlay alloc] initWithOfflineMapDatabase:offlineMap]; 269 | _rasterOverlay.delegate = self; 270 | _removeOfflineMapsView.hidden = NO; 271 | 272 | [_mapView addOverlay:_rasterOverlay]; 273 | } 274 | else 275 | { 276 | [[[UIAlertView alloc] initWithTitle:@"No Offline Maps" 277 | message:@"No offline maps have been downloaded." 278 | delegate:nil 279 | cancelButtonTitle:nil 280 | otherButtonTitles:@"OK", nil] show]; 281 | } 282 | break; 283 | } 284 | case 8: 285 | { 286 | // Show Attribution 287 | [self attribution:_rasterOverlay.attribution]; 288 | break; 289 | } 290 | } 291 | } 292 | 293 | 294 | #pragma mark - AlertView stuff 295 | 296 | - (void)areYouSureYouWantToDeleteAllOfflineMaps 297 | { 298 | NSString *title = @"Are you sure you want to remove your offline maps?"; 299 | NSString *message = @"This will permently delete your offline map data. This action cannot be undone."; 300 | UIAlertView *areYouSure = [[UIAlertView alloc] initWithTitle:title message:message delegate:self cancelButtonTitle:nil otherButtonTitles:@"No", @"Yes", nil]; 301 | [areYouSure show]; 302 | } 303 | 304 | - (void)areYouSureYouWantToCancel 305 | { 306 | NSString *title = @"Are you sure you want to cancel?"; 307 | NSString *message = @"Canceling an offline map download permanently deletes its partially downloaded map data. This action cannot be undone."; 308 | UIAlertView *areYouSure = [[UIAlertView alloc] initWithTitle:title message:message delegate:self cancelButtonTitle:nil otherButtonTitles:@"No", @"Yes", nil]; 309 | [areYouSure show]; 310 | } 311 | 312 | - (void)attribution:(NSString *)attribution 313 | { 314 | NSString *title = @"Attribution"; 315 | NSString *message = attribution; 316 | UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title message:message delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"Mapbox Details", @"OSM Details", nil]; 317 | [alert show]; 318 | } 319 | 320 | 321 | - (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex 322 | { 323 | if([alertView.title isEqualToString:@"Are you sure you want to cancel?"]) 324 | { 325 | // For the are you sure you want to cancel alert dialog, do the cancel action if the answer was "Yes" 326 | // 327 | if([[alertView buttonTitleAtIndex:buttonIndex] isEqualToString:@"Yes"]) 328 | { 329 | [[MBXOfflineMapDownloader sharedOfflineMapDownloader] cancel]; 330 | } 331 | } 332 | else if([alertView.title isEqualToString:@"Are you sure you want to remove your offline maps?"]) 333 | { 334 | // For are you sure you want to remove offline maps alert dialog, do the remove action if the answer was "Yes" 335 | // 336 | if([[alertView buttonTitleAtIndex:buttonIndex] isEqualToString:@"Yes"]) 337 | { 338 | if(_currentlyViewingAnOfflineMap) 339 | { 340 | [self resetMapViewAndRasterOverlayDefaults]; 341 | } 342 | for(MBXOfflineMapDatabase *db in [MBXOfflineMapDownloader sharedOfflineMapDownloader].offlineMapDatabases) 343 | { 344 | [[MBXOfflineMapDownloader sharedOfflineMapDownloader] removeOfflineMapDatabase:db]; 345 | } 346 | 347 | } 348 | } 349 | else if([alertView.title isEqualToString:@"Attribution"]) 350 | { 351 | // For the attribution alert dialog, open the Mapbox and OSM copyright pages when their respective buttons are pressed 352 | // 353 | if([[alertView buttonTitleAtIndex:buttonIndex] isEqualToString:@"Mapbox Details"]) 354 | { 355 | [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"https://www.mapbox.com/tos/"]]; 356 | } 357 | if([[alertView buttonTitleAtIndex:buttonIndex] isEqualToString:@"OSM Details"]) 358 | { 359 | [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"http://www.openstreetmap.org/copyright"]]; 360 | } 361 | } 362 | } 363 | 364 | 365 | 366 | 367 | #pragma mark - Offline map download controls 368 | 369 | - (IBAction)offlineMapButtonActionHelp:(id)sender 370 | { 371 | NSString *title = @"Offline Map Downloader Help"; 372 | NSString *message = @"Arrange the map to show the region you want to download for offline use, then press [Begin]. [Suspend] stops the downloading in such a way that you can [Resume] it later. [Cancel] stops the download and discards the partially downloaded files."; 373 | UIAlertView *help = [[UIAlertView alloc] initWithTitle:title message:message delegate:nil cancelButtonTitle:nil otherButtonTitles:@"OK", nil]; 374 | [help show]; 375 | } 376 | 377 | 378 | - (IBAction)offlineMapButtonActionBegin:(id)sender 379 | { 380 | [[MBXOfflineMapDownloader sharedOfflineMapDownloader] beginDownloadingMapID:_rasterOverlay.mapID mapRegion:_mapView.region minimumZ:_rasterOverlay.minimumZ maximumZ:MIN(16,_rasterOverlay.maximumZ)]; 381 | } 382 | 383 | 384 | - (IBAction)offlineMapButtonActionCancel:(id)sender 385 | { 386 | [self areYouSureYouWantToCancel]; 387 | } 388 | 389 | - (IBAction)offlineMapButtonActionSuspendResume:(id)sender { 390 | if ([[MBXOfflineMapDownloader sharedOfflineMapDownloader] state] == MBXOfflineMapDownloaderStateSuspended) 391 | { 392 | [[MBXOfflineMapDownloader sharedOfflineMapDownloader] resume]; 393 | } 394 | else 395 | { 396 | [[MBXOfflineMapDownloader sharedOfflineMapDownloader] suspend]; 397 | } 398 | } 399 | 400 | 401 | - (IBAction)removeOfflineMapsButtonAction:(id)sender { 402 | // Remove offline maps 403 | // 404 | [self areYouSureYouWantToDeleteAllOfflineMaps]; 405 | } 406 | 407 | 408 | #pragma mark - MBXOfflineMapDownloaderDelegate implementation (progress indicator, etc) 409 | 410 | - (void)offlineMapDownloader:(MBXOfflineMapDownloader *)offlineMapDownloader stateChangedTo:(MBXOfflineMapDownloaderState)state 411 | { 412 | switch (state) 413 | { 414 | case MBXOfflineMapDownloaderStateAvailable: 415 | _offlineMapButtonBegin.enabled = YES; 416 | _offlineMapButtonCancel.enabled = NO; 417 | [_offlineMapButtonSuspendResume setTitle:@"Suspend" forState:UIControlStateNormal]; 418 | _offlineMapButtonSuspendResume.enabled = NO; 419 | break; 420 | case MBXOfflineMapDownloaderStateRunning: 421 | _offlineMapButtonBegin.enabled = NO; 422 | _offlineMapButtonCancel.enabled = YES; 423 | [_offlineMapButtonSuspendResume setTitle:@"Suspend" forState:UIControlStateNormal]; 424 | _offlineMapButtonSuspendResume.enabled = YES; 425 | break; 426 | case MBXOfflineMapDownloaderStateCanceling: 427 | _offlineMapButtonBegin.enabled = NO; 428 | _offlineMapButtonCancel.enabled = NO; 429 | [_offlineMapButtonSuspendResume setTitle:@"Suspend" forState:UIControlStateNormal]; 430 | _offlineMapButtonSuspendResume.enabled = NO; 431 | break; 432 | case MBXOfflineMapDownloaderStateSuspended: 433 | _offlineMapButtonBegin.enabled = NO; 434 | _offlineMapButtonCancel.enabled = YES; 435 | [_offlineMapButtonSuspendResume setTitle:@"Resume" forState:UIControlStateNormal]; 436 | _offlineMapButtonSuspendResume.enabled = YES; 437 | break; 438 | } 439 | } 440 | 441 | 442 | - (void)offlineMapDownloader:(MBXOfflineMapDownloader *)offlineMapDownloader totalFilesExpectedToWrite:(NSUInteger)totalFilesExpectedToWrite 443 | { 444 | [_offlineMapProgress setProgress:0.0 animated:NO]; 445 | _offlineMapProgressView.hidden = NO; 446 | } 447 | 448 | 449 | - (void)offlineMapDownloader:(MBXOfflineMapDownloader *)offlineMapDownloader totalFilesWritten:(NSUInteger)totalFilesWritten totalFilesExpectedToWrite:(NSUInteger)totalFilesExpectedToWrite 450 | { 451 | if (totalFilesExpectedToWrite != 0) 452 | { 453 | float progress = ((float)totalFilesWritten) / ((float)totalFilesExpectedToWrite); 454 | [_offlineMapProgress setProgress:progress animated:YES]; 455 | } 456 | } 457 | 458 | 459 | - (void)offlineMapDownloader:(MBXOfflineMapDownloader *)offlineMapDownloader didEncounterRecoverableError:(NSError *)error 460 | { 461 | if(error.code == MBXMapKitErrorCodeURLSessionConnectivity) 462 | { 463 | // For some reason the offline map downloader wasn't able to make an HTTP connection. This probably means there is a 464 | // network connectivity problem, so stop trying to download stuff. Please note how this is a minimal example which probably isn't 465 | // very suitable to copy over into real apps. In contexts where there is a reasonable expectation of intermittent network 466 | // connectivity, an approach with some capability to resume when the network re-connects would probably be better. 467 | // 468 | [offlineMapDownloader suspend]; 469 | NSLog(@"The offline map download was suspended in response to a network connectivity error: %@",error); 470 | } 471 | else if(error.code == MBXMapKitErrorCodeHTTPStatus) 472 | { 473 | // The HTTP status response for one of the urls requested by the offline map came back as something other than 200. This is 474 | // not necessarily bad, but it probably indicates a problem with the parameters used to begin an offline map download. For 475 | // example, you might have requested markers for a map that doesn't have any. 476 | // 477 | NSLog(@"The offline map downloader encountered an HTTP status error: %@",error); 478 | } 479 | else if(error.code == MBXMapKitErrorCodeOfflineMapSqlite) 480 | { 481 | // There was an sqlite error with the offline map. The most likely explanation is that the disk is running out of space. 482 | // 483 | NSLog(@"The offline map downloader encountered an sqlite error: %@",error); 484 | } 485 | } 486 | 487 | 488 | - (void)offlineMapDownloader:(MBXOfflineMapDownloader *)offlineMapDownloader didCompleteOfflineMapDatabase:(MBXOfflineMapDatabase *)offlineMapDatabase withError:(NSError *)error 489 | { 490 | _offlineMapProgressView.hidden = YES; 491 | 492 | if(error) 493 | { 494 | if(error.code == MBXMapKitErrorCodeDownloadingCanceled) 495 | { 496 | // Ignore cancellations, 497 | } 498 | else 499 | { 500 | // ...but pay attention to other errors 501 | // 502 | NSLog(@"The offline map download completed with an error: %@",error); 503 | } 504 | } 505 | } 506 | 507 | 508 | #pragma mark - MKMapViewDelegate protocol implementation 509 | 510 | - (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id)overlay 511 | { 512 | // This is boilerplate code to connect tile overlay layers with suitable renderers 513 | // 514 | if ([overlay isKindOfClass:[MBXRasterTileOverlay class]]) 515 | { 516 | MBXRasterTileRenderer *renderer = [[MBXRasterTileRenderer alloc] initWithTileOverlay:overlay]; 517 | return renderer; 518 | } 519 | return nil; 520 | } 521 | 522 | 523 | - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id)annotation 524 | { 525 | // This is boilerplate code to connect annotations with suitable views 526 | // 527 | if ([annotation isKindOfClass:[MBXPointAnnotation class]]) 528 | { 529 | static NSString *MBXSimpleStyleReuseIdentifier = @"MBXSimpleStyleReuseIdentifier"; 530 | MKAnnotationView *view = [mapView dequeueReusableAnnotationViewWithIdentifier:MBXSimpleStyleReuseIdentifier]; 531 | if (!view) 532 | { 533 | view = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:MBXSimpleStyleReuseIdentifier]; 534 | } 535 | view.image = ((MBXPointAnnotation *)annotation).image; 536 | view.canShowCallout = YES; 537 | return view; 538 | } 539 | return nil; 540 | } 541 | 542 | 543 | #pragma mark - MBXRasterTileOverlayDelegate implementation 544 | 545 | - (void)tileOverlay:(MBXRasterTileOverlay *)overlay didLoadMetadata:(NSDictionary *)metadata withError:(NSError *)error 546 | { 547 | // This delegate callback is for centering the map once the map metadata has been loaded 548 | // 549 | if (error) 550 | { 551 | NSLog(@"Failed to load metadata for map ID %@ - (%@)", overlay.mapID, error?error:@""); 552 | } 553 | else 554 | { 555 | [_mapView mbx_setCenterCoordinate:overlay.center zoomLevel:overlay.centerZoom animated:NO]; 556 | } 557 | } 558 | 559 | 560 | - (void)tileOverlay:(MBXRasterTileOverlay *)overlay didLoadMarkers:(NSArray *)markers withError:(NSError *)error 561 | { 562 | // This delegate callback is for adding map markers to an MKMapView once all the markers for the tile overlay have loaded 563 | // 564 | if (error) 565 | { 566 | NSLog(@"Failed to load markers for map ID %@ - (%@)", overlay.mapID, error?error:@""); 567 | } 568 | else 569 | { 570 | [_mapView addAnnotations:markers]; 571 | } 572 | } 573 | 574 | - (void)tileOverlayDidFinishLoadingMetadataAndMarkers:(MBXRasterTileOverlay *)overlay 575 | { 576 | [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; 577 | } 578 | 579 | @end 580 | -------------------------------------------------------------------------------- /Sample Project/MBXMapKit iOS/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Sample Project/MBXMapKit iOS/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // MBXMapKit iOS Demo v030 4 | // 5 | // Copyright (c) 2014 Mapbox. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | #import "MBXAppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) 13 | { 14 | @autoreleasepool { 15 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([MBXAppDelegate class])); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Sample Project/MBXMapKit.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 012A0D861909D3F6005B69D7 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 012A0D841909D3F6005B69D7 /* InfoPlist.strings */; }; 11 | 012A0D881909D3F6005B69D7 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 012A0D871909D3F6005B69D7 /* main.m */; }; 12 | 012A0DAB1909D515005B69D7 /* Main_iPad.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 012A0DA71909D515005B69D7 /* Main_iPad.storyboard */; }; 13 | 012A0DAC1909D515005B69D7 /* Main_iPhone.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 012A0DA91909D515005B69D7 /* Main_iPhone.storyboard */; }; 14 | 012A0DB11909D52E005B69D7 /* MBXAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 012A0DAE1909D52E005B69D7 /* MBXAppDelegate.m */; }; 15 | 012A0DB21909D52E005B69D7 /* MBXViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 012A0DB01909D52E005B69D7 /* MBXViewController.m */; }; 16 | 012A0DB81909D5FC005B69D7 /* MBXMapKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 017F793D1909D1D200EF8AD1 /* MBXMapKit.m */; }; 17 | 012A0DB91909D5FC005B69D7 /* MBXOfflineMapDatabase.m in Sources */ = {isa = PBXBuildFile; fileRef = 017F793F1909D1D200EF8AD1 /* MBXOfflineMapDatabase.m */; }; 18 | 012A0DBA1909D5FC005B69D7 /* MBXOfflineMapDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 017F79411909D1D200EF8AD1 /* MBXOfflineMapDownloader.m */; }; 19 | 012A0DBB1909D5FC005B69D7 /* MBXPointAnnotation.m in Sources */ = {isa = PBXBuildFile; fileRef = 017F79431909D1D200EF8AD1 /* MBXPointAnnotation.m */; }; 20 | 012A0DBC1909D5FC005B69D7 /* MBXRasterTileOverlay.m in Sources */ = {isa = PBXBuildFile; fileRef = 017F79451909D1D200EF8AD1 /* MBXRasterTileOverlay.m */; }; 21 | DDA62C701A38BD2900B01B80 /* MBXRasterTileRenderer.m in Sources */ = {isa = PBXBuildFile; fileRef = DDA62C6F1A38BD2900B01B80 /* MBXRasterTileRenderer.m */; }; 22 | DDB97D07199D72A5006EC3A6 /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = DDB97D06199D72A5006EC3A6 /* libsqlite3.dylib */; }; 23 | DDC92F961A1544CD0082BDE8 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DDC92F951A1544CD0082BDE8 /* Images.xcassets */; }; 24 | DDC92FC61A158CFB0082BDE8 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = DDC92FC51A158CFB0082BDE8 /* LaunchScreen.xib */; }; 25 | /* End PBXBuildFile section */ 26 | 27 | /* Begin PBXFileReference section */ 28 | 012A0D7D1909D3F6005B69D7 /* MBXMapKit.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MBXMapKit.app; sourceTree = BUILT_PRODUCTS_DIR; }; 29 | 012A0D831909D3F6005B69D7 /* MBXMapKit iOS-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "MBXMapKit iOS-Info.plist"; sourceTree = ""; }; 30 | 012A0D851909D3F6005B69D7 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 31 | 012A0D871909D3F6005B69D7 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 32 | 012A0D891909D3F6005B69D7 /* MBXMapKit iOS-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "MBXMapKit iOS-Prefix.pch"; sourceTree = ""; }; 33 | 012A0DA81909D515005B69D7 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main_iPad.storyboard; sourceTree = ""; }; 34 | 012A0DAA1909D515005B69D7 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main_iPhone.storyboard; sourceTree = ""; }; 35 | 012A0DAD1909D52E005B69D7 /* MBXAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MBXAppDelegate.h; sourceTree = ""; }; 36 | 012A0DAE1909D52E005B69D7 /* MBXAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBXAppDelegate.m; sourceTree = ""; }; 37 | 012A0DAF1909D52E005B69D7 /* MBXViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MBXViewController.h; sourceTree = ""; }; 38 | 012A0DB01909D52E005B69D7 /* MBXViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBXViewController.m; sourceTree = ""; }; 39 | 0161CFE619140CEA00692A7A /* MBXConstantsAndTypes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MBXConstantsAndTypes.h; path = ../MBXMapKit/MBXConstantsAndTypes.h; sourceTree = ""; }; 40 | 017F793C1909D1D200EF8AD1 /* MBXMapKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MBXMapKit.h; sourceTree = ""; }; 41 | 017F793D1909D1D200EF8AD1 /* MBXMapKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBXMapKit.m; sourceTree = ""; }; 42 | 017F793E1909D1D200EF8AD1 /* MBXOfflineMapDatabase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MBXOfflineMapDatabase.h; sourceTree = ""; }; 43 | 017F793F1909D1D200EF8AD1 /* MBXOfflineMapDatabase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBXOfflineMapDatabase.m; sourceTree = ""; }; 44 | 017F79401909D1D200EF8AD1 /* MBXOfflineMapDownloader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MBXOfflineMapDownloader.h; sourceTree = ""; }; 45 | 017F79411909D1D200EF8AD1 /* MBXOfflineMapDownloader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBXOfflineMapDownloader.m; sourceTree = ""; }; 46 | 017F79421909D1D200EF8AD1 /* MBXPointAnnotation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MBXPointAnnotation.h; sourceTree = ""; }; 47 | 017F79431909D1D200EF8AD1 /* MBXPointAnnotation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBXPointAnnotation.m; sourceTree = ""; }; 48 | 017F79441909D1D200EF8AD1 /* MBXRasterTileOverlay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MBXRasterTileOverlay.h; sourceTree = ""; }; 49 | 017F79451909D1D200EF8AD1 /* MBXRasterTileOverlay.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBXRasterTileOverlay.m; sourceTree = ""; }; 50 | DDA62C6E1A38BD2900B01B80 /* MBXRasterTileRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MBXRasterTileRenderer.h; path = ../MBXMapKit/MBXRasterTileRenderer.h; sourceTree = ""; }; 51 | DDA62C6F1A38BD2900B01B80 /* MBXRasterTileRenderer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MBXRasterTileRenderer.m; path = ../MBXMapKit/MBXRasterTileRenderer.m; sourceTree = ""; }; 52 | DDB97D06199D72A5006EC3A6 /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = usr/lib/libsqlite3.dylib; sourceTree = SDKROOT; }; 53 | DDC92F951A1544CD0082BDE8 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 54 | DDC92FC51A158CFB0082BDE8 /* LaunchScreen.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LaunchScreen.xib; sourceTree = ""; }; 55 | /* End PBXFileReference section */ 56 | 57 | /* Begin PBXFrameworksBuildPhase section */ 58 | 012A0D7A1909D3F6005B69D7 /* Frameworks */ = { 59 | isa = PBXFrameworksBuildPhase; 60 | buildActionMask = 2147483647; 61 | files = ( 62 | DDB97D07199D72A5006EC3A6 /* libsqlite3.dylib in Frameworks */, 63 | ); 64 | runOnlyForDeploymentPostprocessing = 0; 65 | }; 66 | /* End PBXFrameworksBuildPhase section */ 67 | 68 | /* Begin PBXGroup section */ 69 | 012A0D811909D3F6005B69D7 /* Sample App (iOS) */ = { 70 | isa = PBXGroup; 71 | children = ( 72 | 012A0DAD1909D52E005B69D7 /* MBXAppDelegate.h */, 73 | 012A0DAE1909D52E005B69D7 /* MBXAppDelegate.m */, 74 | 012A0DAF1909D52E005B69D7 /* MBXViewController.h */, 75 | 012A0DB01909D52E005B69D7 /* MBXViewController.m */, 76 | 012A0DA71909D515005B69D7 /* Main_iPad.storyboard */, 77 | 012A0DA91909D515005B69D7 /* Main_iPhone.storyboard */, 78 | DDC92F951A1544CD0082BDE8 /* Images.xcassets */, 79 | DDC92FC51A158CFB0082BDE8 /* LaunchScreen.xib */, 80 | 012A0D821909D3F6005B69D7 /* Supporting Files */, 81 | ); 82 | name = "Sample App (iOS)"; 83 | path = "MBXMapKit iOS"; 84 | sourceTree = ""; 85 | }; 86 | 012A0D821909D3F6005B69D7 /* Supporting Files */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 012A0D831909D3F6005B69D7 /* MBXMapKit iOS-Info.plist */, 90 | 012A0D841909D3F6005B69D7 /* InfoPlist.strings */, 91 | 012A0D871909D3F6005B69D7 /* main.m */, 92 | 012A0D891909D3F6005B69D7 /* MBXMapKit iOS-Prefix.pch */, 93 | ); 94 | name = "Supporting Files"; 95 | sourceTree = ""; 96 | }; 97 | 017F793B1909D1D200EF8AD1 /* MBXMapKit */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | 017F793C1909D1D200EF8AD1 /* MBXMapKit.h */, 101 | 017F793D1909D1D200EF8AD1 /* MBXMapKit.m */, 102 | 0161CFE619140CEA00692A7A /* MBXConstantsAndTypes.h */, 103 | 017F793E1909D1D200EF8AD1 /* MBXOfflineMapDatabase.h */, 104 | 017F793F1909D1D200EF8AD1 /* MBXOfflineMapDatabase.m */, 105 | 017F79401909D1D200EF8AD1 /* MBXOfflineMapDownloader.h */, 106 | 017F79411909D1D200EF8AD1 /* MBXOfflineMapDownloader.m */, 107 | 017F79421909D1D200EF8AD1 /* MBXPointAnnotation.h */, 108 | 017F79431909D1D200EF8AD1 /* MBXPointAnnotation.m */, 109 | 017F79441909D1D200EF8AD1 /* MBXRasterTileOverlay.h */, 110 | 017F79451909D1D200EF8AD1 /* MBXRasterTileOverlay.m */, 111 | DDA62C6E1A38BD2900B01B80 /* MBXRasterTileRenderer.h */, 112 | DDA62C6F1A38BD2900B01B80 /* MBXRasterTileRenderer.m */, 113 | ); 114 | name = MBXMapKit; 115 | path = ../mbxmapkit; 116 | sourceTree = ""; 117 | }; 118 | DDB97D08199D72AB006EC3A6 /* Frameworks */ = { 119 | isa = PBXGroup; 120 | children = ( 121 | DDB97D06199D72A5006EC3A6 /* libsqlite3.dylib */, 122 | ); 123 | name = Frameworks; 124 | path = ../mbxmapkit; 125 | sourceTree = ""; 126 | }; 127 | DDD5905A17D7D086009291B8 = { 128 | isa = PBXGroup; 129 | children = ( 130 | 012A0D811909D3F6005B69D7 /* Sample App (iOS) */, 131 | 017F793B1909D1D200EF8AD1 /* MBXMapKit */, 132 | DDB97D08199D72AB006EC3A6 /* Frameworks */, 133 | DDD5906417D7D086009291B8 /* Products */, 134 | ); 135 | sourceTree = ""; 136 | }; 137 | DDD5906417D7D086009291B8 /* Products */ = { 138 | isa = PBXGroup; 139 | children = ( 140 | 012A0D7D1909D3F6005B69D7 /* MBXMapKit.app */, 141 | ); 142 | name = Products; 143 | sourceTree = ""; 144 | }; 145 | /* End PBXGroup section */ 146 | 147 | /* Begin PBXNativeTarget section */ 148 | 012A0D7C1909D3F6005B69D7 /* MBXMapKit iOS */ = { 149 | isa = PBXNativeTarget; 150 | buildConfigurationList = 012A0DA11909D3F7005B69D7 /* Build configuration list for PBXNativeTarget "MBXMapKit iOS" */; 151 | buildPhases = ( 152 | 012A0D791909D3F6005B69D7 /* Sources */, 153 | 012A0D7A1909D3F6005B69D7 /* Frameworks */, 154 | 012A0D7B1909D3F6005B69D7 /* Resources */, 155 | DDC92F6B1A15395D0082BDE8 /* ShellScript */, 156 | ); 157 | buildRules = ( 158 | ); 159 | dependencies = ( 160 | ); 161 | name = "MBXMapKit iOS"; 162 | productName = "MBXMapKit iOS"; 163 | productReference = 012A0D7D1909D3F6005B69D7 /* MBXMapKit.app */; 164 | productType = "com.apple.product-type.application"; 165 | }; 166 | /* End PBXNativeTarget section */ 167 | 168 | /* Begin PBXProject section */ 169 | DDD5905B17D7D086009291B8 /* Project object */ = { 170 | isa = PBXProject; 171 | attributes = { 172 | CLASSPREFIX = MBX; 173 | LastUpgradeCheck = 0510; 174 | ORGANIZATIONNAME = MapBox; 175 | TargetAttributes = { 176 | 012A0D7C1909D3F6005B69D7 = { 177 | DevelopmentTeam = U8B2JGE4C6; 178 | SystemCapabilities = { 179 | com.apple.iOS = { 180 | enabled = 1; 181 | }; 182 | }; 183 | }; 184 | }; 185 | }; 186 | buildConfigurationList = DDD5905E17D7D086009291B8 /* Build configuration list for PBXProject "MBXMapKit" */; 187 | compatibilityVersion = "Xcode 3.2"; 188 | developmentRegion = English; 189 | hasScannedForEncodings = 0; 190 | knownRegions = ( 191 | en, 192 | Base, 193 | ); 194 | mainGroup = DDD5905A17D7D086009291B8; 195 | productRefGroup = DDD5906417D7D086009291B8 /* Products */; 196 | projectDirPath = ""; 197 | projectRoot = ""; 198 | targets = ( 199 | 012A0D7C1909D3F6005B69D7 /* MBXMapKit iOS */, 200 | ); 201 | }; 202 | /* End PBXProject section */ 203 | 204 | /* Begin PBXResourcesBuildPhase section */ 205 | 012A0D7B1909D3F6005B69D7 /* Resources */ = { 206 | isa = PBXResourcesBuildPhase; 207 | buildActionMask = 2147483647; 208 | files = ( 209 | 012A0DAC1909D515005B69D7 /* Main_iPhone.storyboard in Resources */, 210 | DDC92F961A1544CD0082BDE8 /* Images.xcassets in Resources */, 211 | 012A0DAB1909D515005B69D7 /* Main_iPad.storyboard in Resources */, 212 | 012A0D861909D3F6005B69D7 /* InfoPlist.strings in Resources */, 213 | DDC92FC61A158CFB0082BDE8 /* LaunchScreen.xib in Resources */, 214 | ); 215 | runOnlyForDeploymentPostprocessing = 0; 216 | }; 217 | /* End PBXResourcesBuildPhase section */ 218 | 219 | /* Begin PBXShellScriptBuildPhase section */ 220 | DDC92F6B1A15395D0082BDE8 /* ShellScript */ = { 221 | isa = PBXShellScriptBuildPhase; 222 | buildActionMask = 2147483647; 223 | files = ( 224 | ); 225 | inputPaths = ( 226 | ); 227 | outputPaths = ( 228 | ); 229 | runOnlyForDeploymentPostprocessing = 0; 230 | shellPath = /bin/sh; 231 | shellScript = "export PATH=$HOME/bin:/usr/local/bin:/usr/bin\n\n#\n# don't burn during Debug builds\n#\nif [[ ${CONFIGURATION} != \"Release\" ]]; then\n echo \"Skipping icon burning due to ${CONFIGURATION} configuration\"\n exit 0\nfi\n\necho \"Burning app icons...\"\n\n#\n# retrieve git info\n#\ncommit=`git rev-parse --short HEAD`\nbranch=`git rev-parse --abbrev-ref HEAD`\nrepo=`git remote show -n origin | grep 'Fetch URL' | sed -e 's/.*github\\.com:mapbox\\///' -e 's/\\.git$//' -e 's/^mapbox-//'`\n\n#\n# work directly in app bundle\n#\nbundle_path=\"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}\"\necho \"working in ${bundle_path}\"\ncd \"${bundle_path}\"\n\nfor file in `find . -type f -name AppIcon\\*.png`\ndo\n iconoblast \"$file\" $commit $branch $repo\ndone\ncd \"$OLDPWD\""; 232 | }; 233 | /* End PBXShellScriptBuildPhase section */ 234 | 235 | /* Begin PBXSourcesBuildPhase section */ 236 | 012A0D791909D3F6005B69D7 /* Sources */ = { 237 | isa = PBXSourcesBuildPhase; 238 | buildActionMask = 2147483647; 239 | files = ( 240 | 012A0DB81909D5FC005B69D7 /* MBXMapKit.m in Sources */, 241 | 012A0DB91909D5FC005B69D7 /* MBXOfflineMapDatabase.m in Sources */, 242 | DDA62C701A38BD2900B01B80 /* MBXRasterTileRenderer.m in Sources */, 243 | 012A0DBA1909D5FC005B69D7 /* MBXOfflineMapDownloader.m in Sources */, 244 | 012A0DBB1909D5FC005B69D7 /* MBXPointAnnotation.m in Sources */, 245 | 012A0DBC1909D5FC005B69D7 /* MBXRasterTileOverlay.m in Sources */, 246 | 012A0D881909D3F6005B69D7 /* main.m in Sources */, 247 | 012A0DB21909D52E005B69D7 /* MBXViewController.m in Sources */, 248 | 012A0DB11909D52E005B69D7 /* MBXAppDelegate.m in Sources */, 249 | ); 250 | runOnlyForDeploymentPostprocessing = 0; 251 | }; 252 | /* End PBXSourcesBuildPhase section */ 253 | 254 | /* Begin PBXVariantGroup section */ 255 | 012A0D841909D3F6005B69D7 /* InfoPlist.strings */ = { 256 | isa = PBXVariantGroup; 257 | children = ( 258 | 012A0D851909D3F6005B69D7 /* en */, 259 | ); 260 | name = InfoPlist.strings; 261 | sourceTree = ""; 262 | }; 263 | 012A0DA71909D515005B69D7 /* Main_iPad.storyboard */ = { 264 | isa = PBXVariantGroup; 265 | children = ( 266 | 012A0DA81909D515005B69D7 /* Base */, 267 | ); 268 | name = Main_iPad.storyboard; 269 | sourceTree = ""; 270 | }; 271 | 012A0DA91909D515005B69D7 /* Main_iPhone.storyboard */ = { 272 | isa = PBXVariantGroup; 273 | children = ( 274 | 012A0DAA1909D515005B69D7 /* Base */, 275 | ); 276 | name = Main_iPhone.storyboard; 277 | sourceTree = ""; 278 | }; 279 | /* End PBXVariantGroup section */ 280 | 281 | /* Begin XCBuildConfiguration section */ 282 | 012A0DA21909D3F7005B69D7 /* Debug */ = { 283 | isa = XCBuildConfiguration; 284 | buildSettings = { 285 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 286 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 287 | CODE_SIGN_IDENTITY = "iPhone Developer"; 288 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 289 | GCC_PREFIX_HEADER = "MBXMapKit iOS/MBXMapKit iOS-Prefix.pch"; 290 | GCC_PREPROCESSOR_DEFINITIONS = ( 291 | "DEBUG=1", 292 | "$(inherited)", 293 | ); 294 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 295 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 296 | INFOPLIST_FILE = "MBXMapKit iOS/MBXMapKit iOS-Info.plist"; 297 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 298 | LIBRARY_SEARCH_PATHS = "$(inherited)"; 299 | PRODUCT_NAME = MBXMapKit; 300 | WRAPPER_EXTENSION = app; 301 | }; 302 | name = Debug; 303 | }; 304 | 012A0DA31909D3F7005B69D7 /* Release */ = { 305 | isa = XCBuildConfiguration; 306 | buildSettings = { 307 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 308 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 309 | CODE_SIGN_IDENTITY = "iPhone Developer"; 310 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 311 | GCC_PREFIX_HEADER = "MBXMapKit iOS/MBXMapKit iOS-Prefix.pch"; 312 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 313 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 314 | INFOPLIST_FILE = "MBXMapKit iOS/MBXMapKit iOS-Info.plist"; 315 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 316 | LIBRARY_SEARCH_PATHS = "$(inherited)"; 317 | PRODUCT_NAME = MBXMapKit; 318 | WRAPPER_EXTENSION = app; 319 | }; 320 | name = Release; 321 | }; 322 | DDD5909617D7D087009291B8 /* Debug */ = { 323 | isa = XCBuildConfiguration; 324 | buildSettings = { 325 | ALWAYS_SEARCH_USER_PATHS = NO; 326 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 327 | CLANG_CXX_LIBRARY = "libc++"; 328 | CLANG_ENABLE_MODULES = YES; 329 | CLANG_ENABLE_OBJC_ARC = YES; 330 | CLANG_WARN_BOOL_CONVERSION = YES; 331 | CLANG_WARN_CONSTANT_CONVERSION = YES; 332 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 333 | CLANG_WARN_EMPTY_BODY = YES; 334 | CLANG_WARN_ENUM_CONVERSION = YES; 335 | CLANG_WARN_INT_CONVERSION = YES; 336 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 337 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 338 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 339 | COPY_PHASE_STRIP = NO; 340 | GCC_C_LANGUAGE_STANDARD = gnu99; 341 | GCC_DYNAMIC_NO_PIC = NO; 342 | GCC_OPTIMIZATION_LEVEL = 0; 343 | GCC_PREPROCESSOR_DEFINITIONS = ( 344 | "DEBUG=1", 345 | "$(inherited)", 346 | ); 347 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 348 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 349 | GCC_WARN_UNDECLARED_SELECTOR = YES; 350 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 351 | GCC_WARN_UNUSED_FUNCTION = YES; 352 | GCC_WARN_UNUSED_VARIABLE = YES; 353 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 354 | ONLY_ACTIVE_ARCH = YES; 355 | SDKROOT = iphoneos; 356 | TARGETED_DEVICE_FAMILY = "1,2"; 357 | }; 358 | name = Debug; 359 | }; 360 | DDD5909717D7D087009291B8 /* Release */ = { 361 | isa = XCBuildConfiguration; 362 | buildSettings = { 363 | ALWAYS_SEARCH_USER_PATHS = NO; 364 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 365 | CLANG_CXX_LIBRARY = "libc++"; 366 | CLANG_ENABLE_MODULES = YES; 367 | CLANG_ENABLE_OBJC_ARC = YES; 368 | CLANG_WARN_BOOL_CONVERSION = YES; 369 | CLANG_WARN_CONSTANT_CONVERSION = YES; 370 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 371 | CLANG_WARN_EMPTY_BODY = YES; 372 | CLANG_WARN_ENUM_CONVERSION = YES; 373 | CLANG_WARN_INT_CONVERSION = YES; 374 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 375 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 376 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 377 | COPY_PHASE_STRIP = YES; 378 | ENABLE_NS_ASSERTIONS = NO; 379 | GCC_C_LANGUAGE_STANDARD = gnu99; 380 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 381 | GCC_WARN_UNDECLARED_SELECTOR = YES; 382 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 383 | GCC_WARN_UNUSED_FUNCTION = YES; 384 | GCC_WARN_UNUSED_VARIABLE = YES; 385 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 386 | SDKROOT = iphoneos; 387 | TARGETED_DEVICE_FAMILY = "1,2"; 388 | VALIDATE_PRODUCT = YES; 389 | }; 390 | name = Release; 391 | }; 392 | /* End XCBuildConfiguration section */ 393 | 394 | /* Begin XCConfigurationList section */ 395 | 012A0DA11909D3F7005B69D7 /* Build configuration list for PBXNativeTarget "MBXMapKit iOS" */ = { 396 | isa = XCConfigurationList; 397 | buildConfigurations = ( 398 | 012A0DA21909D3F7005B69D7 /* Debug */, 399 | 012A0DA31909D3F7005B69D7 /* Release */, 400 | ); 401 | defaultConfigurationIsVisible = 0; 402 | defaultConfigurationName = Release; 403 | }; 404 | DDD5905E17D7D086009291B8 /* Build configuration list for PBXProject "MBXMapKit" */ = { 405 | isa = XCConfigurationList; 406 | buildConfigurations = ( 407 | DDD5909617D7D087009291B8 /* Debug */, 408 | DDD5909717D7D087009291B8 /* Release */, 409 | ); 410 | defaultConfigurationIsVisible = 0; 411 | defaultConfigurationName = Release; 412 | }; 413 | /* End XCConfigurationList section */ 414 | }; 415 | rootObject = DDD5905B17D7D086009291B8 /* Project object */; 416 | } 417 | -------------------------------------------------------------------------------- /install_docs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ -z `which appledoc` ]; then 4 | echo "Unable to find appledoc. Consider installing it from source or Homebrew." 5 | exit 1 6 | fi 7 | 8 | VERSION=$( git tag | sort -r | sed -n '1p' ) 9 | echo "Creating new docs for $VERSION..." 10 | echo 11 | 12 | appledoc \ 13 | --output /tmp/`uuidgen` \ 14 | --project-name "MBXMapKit $VERSION" \ 15 | --project-company Mapbox \ 16 | --create-docset \ 17 | --company-id com.mapbox \ 18 | --ignore .m \ 19 | --index-desc README.md \ 20 | . -------------------------------------------------------------------------------- /remove_docs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo 4 | echo "Removing docs from ~/Library/Developer/Shared/Documentation/DocSets..." 5 | echo 6 | 7 | rm -rfv ~/Library/Developer/Shared/Documentation/DocSets/com.mapbox.MBXMapKit-* --------------------------------------------------------------------------------