Upgrade your application from the Mapbox Maps SDK for iOS v6 to v10. ... The OfflineManager API can be used to create offline style packs that contain style ...
Youareusinganoutdatedbrowserandwillencountersomeproblemswithourwebsite.Pleaseconsiderupgrading.UpgradeNowMapsSDKforiOSSearchGuidesExamplesAPIReferenceTutorialsTroubleshootingAlldocsMapsSDKforiOSGuidesMigratetov10Migratetov10OnthispageRequirementsNewMapViewStructuredAPIsurfaceResourceOptionsconfiguration(includingaccesstoken)DisplayamapStyleloadingfunctionsListeningtothemap'slifecycleMapoptionsConfigurethemapModulararchitectureExample:ReplaceableHTTPstackCameraManipulatethecameraSetthemap’scameraFitthecameratoagivenshapeConfigurethedefaultbehaviorofthecameraMapstylesStyleSpecificationType-safeAPILocalizationExpressionsAnnotationsPointannotationsLineannotationsPolygonannotationsSelectingannotationsUserlocationRequestingpermissionsShowingthedevice'slocationFollowingthedevice'slocationCustomLocationProviderHandlingprivacyNewfeaturesandimprovementsPlatform-drivencameraanimationsystem3DterrainandskylayersCustomrenderedlayersNewOfflineManagerDeprecationsandremovalsShareyourfeedbackTheMapboxMapsSDKv10introducesimprovementstohowMapboxworksoniOSplatforms,aswellaschangestohowdevelopersusetheSDK.ThisdocumentsummarizesthemostimportantchangesandwalksyouthroughhowtoupgradeanapplicationusingapreviousversionoftheMapboxMapsSDKtov10.RequirementsXcode12.2+Swiftversion5.3+iOS11orgreaterNoteStartingwithv10.0.0-beta.15,buildingwithAppleSiliconissupported.NewMapViewStructuredAPIsurfaceTheMapsSDKv10aimstoprovideasinglepointofentrytoallmap-relatedtasks.TheAPIinterfaceofthenewMapViewisnowhierarchicalinnature.Methodsarestrictlyseparatedbasedontheircomponents(forexampleMapboxMap,Style,CameraandLocation).AllcapabilitiesrelatedtothemapstylewillbepartofthestylepropertyoftheMapView.Othercapabilitieslikeannotationsoruserlocationhavetheirowncorrespondingpropertiesthatencapsulatetheirfeatureset.ResourceOptionsconfiguration(includingaccesstoken)Inpre-v10theaccesstokenwasconfiguredeitherusingtheInfo.plistkeyMGLMapboxAccessTokenorbyprogrammaticallysettingMGLAccountManager.accessToken.Inv10,anewResourceOptionsManagerhasbeenintroducedtomanagetheapplication-scopedResourceOptions,whichincludesanaccesstoken.ThesharedResourceOptionsManager.defaultisanapplicationwideconvenience.It'susedbydefaultbyMapInitOptions(andinturnbyMapView.init())ifyoudonotprovideacustomResourceOptions.Youshouldusethisconveniencetosetanapplicationwideaccesstoken:funcapplication(_application:UIApplication,didFinishLaunchingWithOptionslaunchOptions:[UIApplication.LaunchOptionsKey:Any]?)->Bool{
//Overridepointforcustomizationafterapplicationlaunch.
ResourceOptionsManager.default.resourceOptions.accessToken="my-application-access-token"
returntrue
}
Ifyouchoosenottosetthetokenprogrammatically,thesharedinstancewilllookintheapplication'sInfo.plistforthevalueassociatedwiththeMBXAccessTokenkey.YoucancreateResourceOptionsManagersasyouneedthem.ThiscanbeusefulifyouneedamechanismtoshareResourceOptionsbetweenpartsofyourapplication.NoteIfyouareworkingwithStoryboardsorXibs,ResourceOptionsManager.defaultwillbeusedwhencreatingtherequiredMapInitOptions.IfyouwishtoprovideacustomMapInitOptions(whichincludestheResourceOptions),youcanconnectacustomtypeconformingtotheMapInitOptionsProviderprotocoltothemapInitOptionsProviderIBOutlet.ThisgivesyouamechanismtooverridetheaccesstokenusedbyaMapView.DisplayamapTheprimaryinterfaceinv10istheMapView,which,likeinearlierversionsoftheSDK,isaUIView.MapboxMaphasalsobeenintroduced,whichgrantsaccesstomapproperties(asopposedtoUIViewassociatedproperties).TheMapView.mapboxMappropertyistheequivalentofAndroid'sgetMapboxMap()function.Theexamplebelowdisplaysafull-screenmapwithadefaultstyle.InitializingaMapViewcreatesadefaultsetofmapoptions,providingastrongfoundation.pre-v10:importUIKit
importMapbox
classBasicMapViewController:UIViewController{
varmapView:MGLMapView!
overridefuncviewDidLoad(){
super.viewDidLoad()
mapView=MGLMapView(frame:view.bounds)
view.addSubview(mapView)
}
}
v10:importUIKit
importMapboxMaps
classBasicMapViewController:UIViewController{
varmapView:MapView!
varaccessToken:String!
overridefuncviewDidLoad(){
super.viewDidLoad()
ResourceOptionsManager.default.resourceOptions.accessToken=self.accessToken
mapView=MapView(frame:view.bounds)
view.addSubview(mapView)
}
}
Theresultwilllooklikethis:StyleloadingfunctionsYoucanseparatestyleloadingfrommapviewcreationusingthenewloadStyleURIandloadStyleJSONfunctions.ThesehaveconvenientclosurestoreturntheloadedStyleandanymaploadingerror.FunctionDescriptionloadStyleURI(_:completion:)LoadanewmapstyleasynchronousfromthespecifiedURI.loadStyleJSON(_:completion:)LoadstylefromaJSONstring.Here'sanexampleillustratinghowtodelayloadingamapstyleusingastyleURL://PassanilStyleURItostopthemapfromloadingadefaultstyle
mapView=MapView(frame:bounds,mapInitOptions:MapInitOptions(styleURI:nil))
...
mapView.mapboxMap.loadStyleURI(.streets){resultin
switchresult{
case.success(letstyle):
print("Themaphasfinishedloadingthestyle")
//Dosomethingwith`style`
caselet.failure(error):
print("Themapfailedtoloadthestyle:\(error)")
}
}
Listeningtothemap'slifecycleTheMaphasseveralstagestoitslifecycleandcommunicatestransitionsviaacallbackmechanism.ThiscallbackmechanismreplacestheMGLMapViewDelegateprotocolseeninpre-v10versionsoftheMapsSDK.Thecontainerviewcontrollerinthelastexamplecanlistenforwhenthemaphasfinishedloadingusingthefollowingcode:classBasicMapViewController:UIViewController{
varmapView:MapView!
varaccessToken:String!
varhandler:((Event)->Void)?
overridefuncviewDidLoad(){
super.viewDidLoad()
ResourceOptionsManager.default.resourceOptions.accessToken=self.accessToken
mapView=MapView(frame:view.bounds)
view.addSubview(mapView)
/**
Theclosureiscalledwhenstyledatahasbeenloaded.Thisiscalled
multipletimes.Usetheeventdatatodeterminewhatkindofstyledata
hasbeenloaded.
Whenthetypeis`style`thiseventmostcloselymatches
`-[MGLMapViewDelegatemapView:didFinishLoadingStyle:]`inSDKversions
priortov10.
*/
mapView.mapboxMap.onEvery(.styleDataLoaded){[weakself](event)in
guardletdata=event.dataas?[String:Any],
lettype=data["type"],
lethandler=self?.handlerelse{
return
}
print("Themaphasfinishedloadingstyledataoftype=\(type)")
handler(event)
}
/**
Theclosureiscalledduringtheinitializationofthemapviewand
afterany`styleDataLoaded`events;itiscalledwhentherequested
stylehasbeenfullyloaded,includingthestyle,specifiedspriteand
sourcemetadata.
Thiseventisthelastopportunitytomodifythelayoutorappearance
ofthecurrentstylebeforethemapviewisdisplayedtotheuser.
Addingalayeratthistimetothemapmayresultinthelayerbeing
presentedBEFOREtherestofthemaphasfinishedrendering.
Changestosourcesorlayersofthecurrentstyledonotcausethis
eventtobeemitted.
*/
mapView.mapboxMap.onNext(.styleLoaded){(event)in
print("Themaphasfinishedloadingstyle...Event=\(event)")
self.handler?(event)
}
/**
Theclosureiscalledwheneverthemapfinishesloadingandthemaphas
renderedallvisibletiles,eitheraftertheinitialloadORaftera
stylechangehasforcedareload.
Thisisanidealtimetoaddanyruntimestylingorannotationstothe
mapandensuresthattheselayerswouldonlybeshownafterthemaphas
beenfullyrendered.
*/
mapView.mapboxMap.onNext(.mapLoaded){(event)in
print("Themaphasfinishedloading...Event=\(event)")
self.handler?(event)
}
/**
Theclosureiscalledwheneverthemapviewisenteringanidlestate,
andnomoredrawingwillbenecessaryuntilnewdataisloadedorthere
issomeinteractionwiththemap.
-Allcurrentlyrequestedtileshavebeenrendered
-Allfade/transitionanimationshavecompleted
*/
mapView.mapboxMap.onNext(.mapIdle){(event)in
print("Themapisidle...Event=\(event)")
self.handler?(event)
}
/**
Theclosureiscalledwheneverthemaphasfailedtoload.Thiscould
bebecauseofavarietyofreasons,includinganetworkconnection
failureorafailuretofetchthestylefromtheserver.
Youcanusetheassociatederrormessagetonotifytheuserthatmap
dataisunavailable.
*/
mapView.mapboxMap.onNext(.mapLoadingError){(event)in
guardletdata=event.dataas?[String:Any],
lettype=data["type"],
letmessage=data["message"]else{
return
}
print("Themapfailedtoload..\(type)=\(message)")
}
}
}
It'sworthnotingthattheclosestequivalentofpre-v10's-[MGLMapViewDelegatemapView:didFinishLoadingStyle:]isthe.styleDataLoadedeventwhenitsassociatedtypeisstyle,thoughwerecommendedusingtheMapboxMap.loadStyleURI()functioninstead.loadStyleURI()listensforthe.styleLoadedevent.Thefollowingsimplifieddiagramhelpsexplaintheeventlifecycle:
┌─────────────┐┌─────────┐┌──────────────┐
│Application││Map││ResourceLoader│
└──────┬──────┘└────┬────┘└───────┬──────┘
│││
├─────SetstyleURL──────▶││
│├───────────getstyle───────────▶│
│││
││◀─────────styledata────────────┤
│││
│├─parsestyle─┐│
││││
│styleDataLoaded◀─────────────┘│
│◀────{"type":"style"}─────┤│
│├─────────getsprite────────────▶│
│││
││◀────────spritedata────────────┤
│││
│├──────parsesprite───────┐│
││││
│styleDataLoaded◀─────────────────────────┘│
│◀───{"type":"sprite"}─────┤│
│├─────getsourceTileJSON(s)────▶│
│││
│sourceDataLoaded│◀─────parseTileJSONdata───────┤
│◀──{"type":"metadata"}────┤│
│││
│││
│styleDataLoaded││
│◀───{"type":"sources"}────┤│
│├──────────gettiles────────────▶│
│││
│◀───────styleLoaded────────┤│
│││
│sourceDataLoaded│◀─────────tiledata─────────────┤
│◀────{"type":"tile"}──────┤│
│││
│││
│◀────renderFrameStarted────┤│
│├─────render─────┐│
││││
│◀────────────────┘│
│◀───renderFrameFinished────┤│
│├──render,alltilesloaded──┐│
││││
│◀────────────────────────────┘│
│◀────────mapLoaded─────────┤│
│││
│││
│◀─────────mapIdle──────────┤│
│┌────┴────┐│
││offline││
│└────┬────┘│
│││
├────────Setcamera──────▶││
│├───────────gettiles───────────▶│
│││
││┌──────────────│
│◀─────────mapIdle──────────┤waitingforconnectivity││
│││Maprenderscacheddata│
││──────────────┘│
│││
MapoptionsConfigurethemapThemapcreatedintheexampleaboveisinitializedwithdefaultconfigurationsformapelementssuchasgestures,ornaments,andthemap'scamera.Themap'sconfigurationscanbeupdatedviatheMapViewanditsmapboxMapproperty.pre-v10:Inpre-v10versionsoftheMapsSDK,optionsweresetontheMGLMapVieworusinganMGLMapViewDelegatemethod.privatevarrestrictedBounds:MGLCoordinateBounds!
overridefuncviewDidLoad(){
super.viewDidLoad()
letmapView=MGLMapView(frame:view.bounds)
mapView.delegate=self
mapView.showsScale=YES;
view.addSubview(mapView)
}
funcmapView(_mapView:MGLMapView,shouldChangeFromoldCamera:MGLMapCamera,tonewCamera:MGLMapCamera)->Bool{
letcurrentCamera=mapView.camera
letnewCameraCenter=newCamera.centerCoordinate
mapView.camera=newCamera
letnewVisibleCoordinates=mapView.visibleCoordinateBounds
mapView.camera=currentCamera
//TestifthenewCameraCenterandnewVisibleCoordinatesareinsideself.restrictedBounds.
letinside=MGLCoordinateInCoordinateBounds(newCameraCenter,self.restrictedBounds)
letintersects=MGLCoordinateInCoordinateBounds(newVisibleCoordinates.ne,self.colorado)&&MGLCoordinateInCoordinateBounds(newVisibleCoordinates.sw,self.restrictedBounds)
returninside&&intersects
}
v10:Intheexamplebelow,therestrictedboundsaresetdirectlyviatheMapView.mapboxMap.letrestrictedBounds=CoordinateBounds(southwest:CLLocationCoordinate2D(latitude:10,longitude:10),
northeast:CLLocationCoordinate2D(latitude:11,longitude:11))
letcameraBoundsOptions=CameraBoundsOptions(bounds:restrictedBounds)
//Configuremaptoshowascalebar
mapView.ornaments.options.scaleBar.visibility=.visible
trymapView.mapboxMap.setCameraBounds(with:cameraBoundsOptions)
ModulararchitectureTheMapsSDKv10isbeingdevelopedwithagoalofprovidingamodulararchitecture.Thissetsthefoundationforafutureplugin-basedarchitecturethatcanbeminimizedorextended.Example:ReplaceableHTTPstackYoucansupplycustomimplementationsforsomecomponentsoftheSDK.TheexamplebelowshowsyouhowtoreplacetheHTTPstack.Youmustreplacethestackearlyintheapplicationlifecycle,beforeanyAPIrequestsaremade,andyoushouldcallsetUserDefinedForCustomonceonly.importUIKit
importMapboxMaps
@UIApplicationMain
classAppDelegate:UIResponder,UIApplicationDelegate{
varwindow:UIWindow?
letcustomHTTPService=CustomHttpService()
funcapplication(_application:UIApplication,didFinishLaunchingWithOptionslaunchOptions:[UIApplication.LaunchOptionsKey:Any]?)->Bool{
HttpServiceFactory.setUserDefinedForCustom(customHTTPService)
returntrue
}
...
CustomHttpServicereferencedinthecodesnippetaboveimplementsHttpServiceInterface.Thefollowingisanexampleofapartialimplementation:classCustomHttpService:HttpServiceInterface{
funcrequest(forrequest:HttpRequest,callback:@escapingHttpResponseCallback)->UInt64{
//MakeanAPIrequest
varurlRequest=URLRequest(url:URL(string:request.url)!)
letmethodMap:[HttpMethod:String]=[
.get:"GET",
.head:"HEAD",
.post:"POST"
]
urlRequest.httpMethod=methodMap[request.method]!
urlRequest.httpBody=request.body
urlRequest.allHTTPHeaderFields=request.headers
lettask=URLSession.shared.dataTask(with:urlRequest){(data,response,error)in
letresult:Result
ifleterror=error{
//MapNSURLErrortoHttpRequestErrorType
letrequestError=HttpRequestError(type:.otherError,message:error.localizedDescription)
result=.failure(requestError)
}elseifletresponse=responseas?HTTPURLResponse,
letdata=data{
//Keysareexpectedtobelowercase
varheaders:[String:String]=[:]
for(key,value)inresponse.allHeaderFields{
guardletkey=keyas?String,
letvalue=valueas?Stringelse{
continue
}
headers[key.lowercased()]=value
}
letresponseData=HttpResponseData(headers:headers,code:Int64(response.statusCode),data:data)
result=.success(responseData)
}else{
//Error
letrequestError=HttpRequestError(type:.otherError,message:"Invalidresponse")
result=.failure(requestError)
}
letresponse=HttpResponse(request:request,result:result)
callback(response)
}
task.resume()
//Handleusedtocancelrequests
returnUInt64(task.taskIdentifier)
}
...
CameraManipulatethecameraInv10,themap’scameraishandledbytheCameraAnimationsManager.TheMapViewhasareferencetotheCameraAnimationsManagercalledcamera.CameraOptionsisthev10equivalenttopre-v10'sMGLMapCamera.Themap’sCameraOptionscontainthecentercoordinate,padding,anchor,zoom,bearing,andpitchforamap.Thecameraoptionscanbeconfiguredeitherasawholeorindividually.Themap’scameracanbeconfiguredviaprogrammaticorgesture-basedevents.ThecurrentcamerastateofthemapcanbeaccessedviathemapView'scameraStateproperty.Setthemap’scameraThecameramanagercanbeconfiguredwithaninitialcameraview.Tosetthemap’scamera,firstcreateaCameraOptionsobject,thendirectthemaptouseitviatheMapInitOptions.CameraOptionsparametersareoptional,soonlytherequiredpropertiesneedtobeset.pre-v10:letcenterCoordinate=CLLocationCoordinate2D(latitude:21.3069,
longitude:-157.8583)
mapView.setCenter(centerCoordinate,zoomLevel:14,direction:0,animated:false)
v10://SetthecentercoordinateofthemaptoHonolulu,Hawaii
letcenterCoordinate=CLLocationCoordinate2D(latitude:21.3069,
longitude:-157.8583)
//Createacamera
letcamera=CameraOptions(center:centerCoordinate,zoom:14)
letoptions=MapInitOptions(cameraOptions:camera)
letmapView=MapView(frame:frame,mapInitOptions:options)
Youcansetthemap’szoomlevel,pitch,andcentercoordinateprogrammaticallybyinitializingaCameraOptionsobjectwiththosevalues.YoucanthesetthecamerausingmapView.mapboxMap.setCamera()CameravaluesshouldbesetusingtheMapboxMap.setCamera()method,althoughtheycanbeaccessedasread-onlypropertiesonthemapview'scameraState(forexample,MapView.cameraState.centerCoordinate).FitthecameratoagivenshapeIntheMapsSDKv10,theapproachtofittingthecameratoagivenshapelikethatofpre-v10versions.Inv10,callcamera(for:)functionsontheMapboxMaptocreate:acameraforagivengeometry,acamerathatfitsasetofrectangularcoordinateboundsoracamerabasedoffofacollectionofcoordinates.Then,callease(to:)onMapView.cameratovisiblytransitiontothenewcamera.Belowisanexampleofsettingthecameratoasetofcoordinates:pre-v10://Fittingacameratoasetofcoordinatebounds
letsw=CLLocationCoordinate2D(latitude:24,longitude:-89)
letne=CLLocationCoordinate2D(latitude:26,longitude:-88)
letcoordinateBounds=MGLCoordinateBounds(sw:sw,ne:ne)
letcamera=mapView.cameraThatFitsCoordinateBounds(coordinateBounds)
mapView.setCamera(camera,animated:true)
v10:letcoordinates=[
CLLocationCoordinate2DMake(24,-89),
CLLocationCoordinate2DMake(24,-88),
CLLocationCoordinate2DMake(26,-88),
CLLocationCoordinate2DMake(26,-89),
CLLocationCoordinate2DMake(24,-89)
]
letcamera=mapView.mapboxMap.camera(for:coordinates,
padding:.zero,
bearing:nil,
pitch:nil)
mapView.camera.ease(to:camera,duration:10.0)
Configurethedefaultbehaviorofthecamerapre-v10Inpre-v10versionsoftheMapsSDK,defaultcamerabehaviorwasconfigureddirectlyonthemapviewusingMGLMapViewpropertieslikeminimumZoomLevelandminimumPitch.TolimitthecameratoaspecificareayouwouldusethemapView:shouldChangeFromCamera:toCamera:methodonMGLMapViewDelegateasshownintheRestrictmappanningtoanareaexample.v10:Inv10,therearenewCameraBoundsandCameraBoundsOptionsstructthatallowsconfigurationofdefaultbehaviorsforthecamerapositions,withtheoptionsoutlinedbelow:publicstructCameraBoundsOptions:Hashable{
///Thelatitudeandlongitudeboundstowhichthecameracenterareconstrained.
publicvarbounds:CoordinateBounds?
///Themaximumzoomlevel,inmapboxzoomlevels0-25.5.Atlowzoomlevels,
///asmallsetofmaptilescoversalargegeographicalarea.Athigher
///zoomlevels,alargernumberoftilescoverasmallergeographicalarea.
publicvarmaxZoom:CGFloat?
///Theminimumzoomlevel,inmapboxzoomlevels0-25.5.
publicvarminZoom:CGFloat?
///Themaximumallowedpitchvalueindegrees.
publicvarmaxPitch:CGFloat?
///Theminimumallowedpitchvaluedegrees.
publicvarminPitch:CGFloat?
...
TheabovepropertiescanbesetviathesetCameraBounds(with:)functiononMapView.mapboxMap:letsw=CLLocationCoordinate2DMake(-12,-46)
letne=CLLocationCoordinate2DMake(2,43)
letrestrictedBounds=CoordinateBounds(southwest:sw,northeast:ne)
trymapView.mapboxMap.setCameraBounds(with:CameraBoundsOptions(bounds:restrictedBounds,
maxZoom:15.0,
minZoom:8.0))
Thebiggestchangetothisapproachishowyourestrictthecameratoagivensetofcoordinatebounds,whichyoucannowdousingasimplerfunction-basedapproachinsteadofusingthedelegatemethodoutlinedearlier.MapstylesStyleSpecificationInv10thestyleAPIisalignedwiththeMapboxStyleSpecification.Sources,Layers,andLightallworkintheexactsamemannerasintheStyleSpecification.TheStyleobjectintheMapView.mapboxMapisresponsibleforallfunctionalityrelatedtoruntimestyling.NoteAddsourcesandlayerstothemaponlyafterithasfinishedloadingitsinitialstyle.Usethelifecyclecallbackstobeinformedofwhenthestylehasfinishedloading.Type-safeAPIEverySourceandLayerdeclaredintheStyleSpecificationexistsinv10asanexactly-typedSwiftstruct.EverypropertywithinthosesourcesandlayersismodeledusingfamiliarSwifttypes.ThismeansthatcreatingnewsourcesandlayersshouldfeelfamiliartoiOSdeveloperswhowriteinSwift.Example:GeoJSONSourceForexample,aminimalGeoJSONsourcecanbecreatedusingthefollowingcode:varmyGeoJSONSource=GeoJSONSource()
myGeoJSONSource.maxzoom=14
GeoJSONsourceshaveadatapropertythatyoucansettoeitheraURLorinlineGeoJSON.TheSwiftstructrepresentingaGeoJSONsourceismodeled1:1withthisexpectation.TheSDKusesturf-swifttomodelGeoJSON.ThismeansthatcraftingGeoJSONatruntimeisbothstraightforwardandbackedbythetype-safetyandcodableconformancethatTurfprovides.//Settingthe`data`propertywithaurlpointingtoaGeoJSONdocument
myGeoJSONSource.data=.url(someGeoJSONDocumentURL)
//SettingaTurffeaturetothe`data`property
myGeoJSONSource.data=.featureCollection(someTurfFeatureCollection)
TheMapView.mapboxMapholdsastyleobjectthatcanbeusedtoadd,remove,orupdatesourcesandlayers.trymapView.mapboxMap.style.addSource(myGeoJSONSource,id:"my-geojson-source")
pre-v10:ThisfunctionalitypreviouslyrequiredcreationofanMGLShapeSource:letshapeSource=MGLShapeSource(identifier:"my-geojson",url:URL(string:""),options:nil)
self.mapView.addSource(shapeSource)
NoteUpdatingGeoJSONsourceshappensdifferentlyinv10.You’llneedtocallstyle.updateGeoJSONSource(withId:geoJSON:)toupdatethedatabelongingtoaGeoJSONSource.Example:BackgroundlayerAddingabackgroundlayerfurtherdemonstratesthetype-safetyprovidedinv10.Asmentionedearlier,allLayersarealsoSwiftstructs.Thefollowingcodesetsupabackgroundlayerandsetsitsbackgroundcolortored:varmyBackgroundLayer=BackgroundLayer(id:"my-background-layer")
myBackgroundLayer.backgroundColor=.constant(StyleColor(.red))
Oncealayeriscreated,addittothemap:trymapView.mapboxMap.style.addLayer(myBackgroundLayer)
Localizationv10allowsdeveloperstochangethelanguagedisplayedbyamapstylebasedonagivenlocale.Thiscapabilitycanbeenabledwiththefollowingcode://ChangeslocaletoSpanish
mapView.mapboxMap.style.localizeLabels(into:Locale(identifier:"en"))
Thereisalsoanopportunitytoprovideanarrayoflayeridssothatonlyasubsetoflayerswillbelocalized.Thiscanbeachievedwiththefollowingcode://ChangesallcountrylabelstoJapanese
mapView.mapboxMap.style.localizeLabels(into:Locale(identifier:"ja"),forLayerIds:["country-label"])
ExpressionsInthebackgroundlayerexampleabove,thebackgroundColorpropertyofthelayerissettoaconstantvalue.However,thebackgroundColorproperty(andalllayoutandpaintproperties)alsosupportexpressions.Inv10,expressionshavebeenredesignedfromthegroundup.ThenewExpressiondomain-specificlanguage(DSL)directlymodelsexpressionsbasedontheMapboxStyleSpecificationandremovesthedependencyonNSExpression.Inv10,expressionsexistasfamiliarandtype-safeSwiftstructs.TheyarealsobackedbySwiftfunctionbuilderstomaketheexperienceofwritinganexpressionlikethewaySwiftUIworks.ConsideraninterpolateexpressionwritteninrawJSON:[
"interpolate",
["linear"],
["zoom"],
0,
"hsl(0,79%,53%)",
14,
"hsl(233,80%,47%)"
]
IfthisexpressionisappliedtothebackgroundColorpaintpropertyofalayer(the"land"layer,intheexamplescreenshotabove),thenthebackgroundColorwouldbeinterpolatedfromredtobluebasedonthezoomlevelofthemap'scamera.pre-v10:Inpre-v10versionsoftheMapsSDK,theexpressionwouldbewrittenlikethis:letcolorStops:[NSNumber:UIColor]=[
0:.red,
14:.blue
];
layer.backgroundColor=NSExpression(format:
"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel,'linear',nil,%@)",
colorStops];
v10:IntheMapsSDKv10,aJSONexpressionfromMapboxStudiocanbeuseddirectlyorcanbetranslatedtoSwift.TousetheJSONexpressiondirectly:letexpressionString=
"""
[
"interpolate",
["linear"],
["zoom"],
0,
"hsl(0,79%,53%)",
14,
"hsl(233,80%,47%)"
]
"""
ifletexpressionData=expressionString.data(using:.utf8){
letexpJSONObject=tryJSONSerialization.jsonObject(with:expressionData,options:[])
trymapView.mapboxMap.style.setLayerProperty(for:"land",
property:"background-color",
value:expJSONObject)
}
TotranslatethesameJSONexpressiontoSwift:Exp(.interpolate){
Exp(.linear)
Exp(.zoom)
0
UIColor.red
14
UIColor.blue
}
UsethefullpowerofSwiftandtheiOSruntimewhendefiningthisexpression.Forexample,toadjustthisexpressionbasedonwhethertheuserhasdarkmodeenabled:varisDarkModeEnabled=traitCollection.userInterfaceStyle==.dark?true:false
letlowZoomColor=isDarkModeEnabled?UIColor.black.expression:UIColor.red
lethighZoomColor=isDarkModeEnabled?UIColor.grey.expression:UIColor.blue
Exp(.interpolate){
Exp(.linear)
Exp(.zoom)
0
lowZoomColor
14
highZoomColor
}
ComposeexpressionsInv10,expressionscanalsobecomposedandbuiltinamodularway.Theycanbeinfinitelynestedordefinedseparatelyandaddedtothestack.Exp(.interpolate){
Exp(.linear)
Exp(.zoom)
Exp(.subtract){
10
3
}
UIColor.red
Exp(.sum){
7
7
}
UIColor.blue
}
AnnotationsYoucanadd"annotations"tothemapusingpoint,line,polygonandcircleshapeswiththeMapView’sAnnotationOrchestrator.UsetheAnnotationOrchestratortocreateannotationmanagersbasedonthetypeofannotationthatyou'reinterestedin.Everyannotationmanagerisresponsibleforacollectionofannotations.Onceamanagerhasbeencreated,youcancreateandaddindividuallystyledinstancesofthecorrespondingannotationtype.Annotationswerealsosupportedpre-v10bywayofMGLShapeobjectsthatcouldconformtotheMGLAnnotationprotocol.Forexample,addingasinglepointannotationthatmarksacoordinatewithanimagepreviouslylookedlikethis:pre-v10classViewController:UIViewController,MGLMapViewDelegate{
overridefuncviewDidLoad(){
super.viewDidLoad()
...
mapView.delegate=self
letpointAnnotation=MGLPointAnnotation()
pointAnnotation.coordinate=CLLocationCoordinate2D(latitude:43,longitude:10)
mapView.addAnnotation(pisa)
}
funcmapView(_mapView:MGLMapView,imageForannotation:MGLAnnotation)->MGLAnnotationImage?{
varcustomImage=UIImage(named:"star")!
returnMGLAnnotationImage(image:customImage,reuseIdentifier:"star")
}
}
IntheMapsSDKv10,thedelegate-basedapproachisnolongerused,movinginfavorofcustomSwiftstructstorepresentannotationsthatcanbecustomizedwithaproperty-basedapproach.Thelargestchangetoannotationsisthattheyareallpoweredinternallybystylelayers.Viewannotationsavailableinv10.2.0andhigherInv6,viewannotationswerepossiblewiththeMGLAnnotationViewclass.Inv10,viewannotationsareonlyavailableinv10.2.0andhigher.FormoredetailsseetheViewannotationsguide.Seecodeexamplesforimplementingpoint,line,polygonandcircleannotationsinMapsSDKv10below.PointannotationsApointannotationisamarkerthatisplacedatadeveloper-specifiedcoordinate://Makea`PointAnnotationManager`whichwillberesponsibleformanagingacollectionof`PointAnnotion`s.
letpointAnnotationManager=mapView.annotations.makePointAnnotationManager()
//Initializeapointannotationwithageometry("coordinate"inthiscase)
//andconfigureitwithacustomimage(sourcedfromtheassetcatalogue)
varcustomPointAnnotation=PointAnnotation(coordinate:customCoordinate)
//Maketheannotationshowaredpin.See/ios/maps/examples/point-annotation/foracompleteexample.
customPointAnnotation.image=.init(image:UIImage(named:"red_pin")!,name:"red_pin")
//Addtheannotationtothemanagerinordertorenderitonthemao.
pointAnnotationManager.annotations=[customPointAnnotation]
ThisresultsinamarkeratthecustomCoordinatespecified.ThePointAnnotationManagercreatedabovealsocontrolsthelifecycleoftheannotationsinitspurview.AnnotationmanagersarekeptalivebytheAnnotationOrchestratoruntiltheyareremovedexplicitlyviaacalltoremoveAnnotationManager(withId:)orimplicitlybycreatinganotherannotationmanagerwiththesameID.LineannotationsAlineannotationconnectsalistofcoordinatesonthemapwithaline.SimilartothePointAnnotationexample,youmustcreateinstancesofLineAnnotationswithsomegeometryandthenaddittoaLineAnnotationManager://LinefromNewYorkCity,NYtoWashington,D.C.
letlineCoordinates=[
CLLocationCoordinate2DMake(40.7128,-74.0060),
CLLocationCoordinate2DMake(38.9072,-77.0369)
]
//Createthelineannotation.
varlineAnnotation=PolylineAnnotation(lineCoordinates:lineCoordinates)
//Customizethestyleofthelineannotation
lineAnnotation.lineColor=StyleColor(.red)
lineAnnotation.lineOpacity=0.8
lineAnnotation.lineWidth=10.0
//CreatethePolylineAnnotationManagerresponsibleformanaging
//thislineannotations(andothersifyousochoose)
letlineAnnnotationManager=mapView.annotations.makePolylineAnnotationManager()
//Addtheannotationtothemanager.
lineAnnnotationManager.annotations=[lineAnnotation]
PolygonannotationsTheAnnotationManagercanalsoaddapolygontothemapbytakingalistofcoordinatesthatitwillthenattempttoconnect.Youcanalsocreateapolygonwithanemptyspaceinthemiddle(likeadoughnut)bytakingaseparatelistofcoordinates.TheorderofthecoordinatesinthelistmatterandshouldconformtotheGeoJSONspecification.//Describethepolygon'sgeometry
letouterRingCoords=[
CLLocationCoordinate2DMake(24.51713945052515,-89.857177734375),
CLLocationCoordinate2DMake(24.51713945052515,-87.967529296875),
CLLocationCoordinate2DMake(26.244156283890756,-87.967529296875),
CLLocationCoordinate2DMake(26.244156283890756,-89.857177734375),
CLLocationCoordinate2DMake(24.51713945052515,-89.857177734375)
]
//Thispolygonhasanintereriorpolygonwhichrepresentsaholeintheshape.
letinnerRingCoords=[
CLLocationCoordinate2DMake(25.085598897064752,-89.20898437499999),
CLLocationCoordinate2DMake(25.085598897064752,-88.61572265625),
CLLocationCoordinate2DMake(25.720735134412106,-88.61572265625),
CLLocationCoordinate2DMake(25.720735134412106,-89.20898437499999),
CLLocationCoordinate2DMake(25.085598897064752,-89.20898437499999)
]
///CreatethePolygonwiththeouterringandinnerring
letouterRing=Turf.Ring(coordinates:outerRingCoords)
letinnerRing=Turf.Ring(coordinates:innerRingCoords)
letpolygon=Turf.Polygon(outerRing:outerRing,innerRings:[innerRing])
//CreatethePolygonAnnotationManager
letpolygonAnnotationManager=mapView.annotations.makePolygonAnnotationManager()
//Createthepolygonannotation
varpolygonAnnotation=PolygonAnnotation(polygon:makePolygon())
//Stylethepolygonannotation
polygonAnnotation.fillColor=StyleColor(.red)
polygonAnnotation.fillOpacity=0.8
//Addthepolygonannotationtothemanager
polygonAnnotationManager.annotations=[polygonAnnotation]
SelectingannotationsAnnotationscanalsobeinteractedwithviaatapgesture.EnsuringthatadelegateconformstoAnnotationInteractionDelegateenablestapevents:classMyViewController:UIViewController,AnnotationInteractionDelegate{
@IBOutletvarmapView:MapView!
overridefuncviewDidLoad(){
super.viewDidLoad()
//Createthepointannotation,whichwillberenderedwitharedpin.
letcoordinate=mapView.cameraState.center
varpointAnnotation=PointAnnotation(coordinate:coordinate)
pointAnnotation.image=.init(image:UIImage(named:"red_pin")!,name:"red_pin")
//Createthepointannotationmanager
letpointAnnotationManager=mapView.annotations.makePointAnnotationManager()
//Allowtheviewcontrollertoacceptannotationselectionevents.
pointAnnotationManager.delegate=self
//Addtheannotationtothemap.
pointAnnotationManager.annotations=[pointAnnotation]
}
//MARK:-AnnotationInteractionDelegate
publicfuncannotationManager(_manager:AnnotationManager,
didDetectTappedAnnotationsannotations:[Annotation]){
print("Annotationstapped:\(annotations)")
}
}
View-basedannotationsarenotsupported.Allpointannotationsmustbeconfiguredwithastaticimage.Draganddropsupportisnotsupported.However,thiscanbeimplementedwithanadvancedusageofvariousstyleAPIstoupdatethedatasourceofaGeoJSONSourceafterrespondingtoaUIGestureRecognizer.UserlocationTheUserLocationcomponentprovidesamechanismbywhichthedevice’scurrentlocationcanbeobservedandrespondedto.Italsohandlespermissionsandprivacy,includingfullvs.reducedaccuracy(whichwasaddediniOS14).RequestingpermissionsUsersmustgrantpermissionbeforeaniOSappcanaccessinformationabouttheirlocation.Duringthispermissionprompt,acustomstringmaybepresentedexplaininghowlocationwillbeused.ThisisspecifiedbyaddingthekeyNSLocationWhenInUseUsageDescriptiontotheInfo.plistfilewithavaluethatdescribeswhytheapplicationisrequestingthesepermissions.NSLocationWhenInUseUsageDescription
Yourlocationisusedtoimprovethemapexperience.
Showingthedevice'slocationDisplaythedevice'slocationwiththefollowingcode:mapView.location.options.puckType=.puck2D()
Theabovecodewillenablerenderingofapuckthatrepresentsthedevice'scurrentlocation.Itwillalsohandlerequestingpermissionsandallthenuancesthatcomewithdifferentpermissionlevels.Followingthedevice'slocationv6trackingmodeshavebeenreplacedbytheViewportAPI(availablestartinginv10.3).Besidesfollowingobjectsonamap,itcanalsobeextendedwithcustomstatesandtransitions.Formoredetails,seetheUserlocationguide.CustomLocationProviderThelocationframeworkhasitsowndefaultlocationproviderwhichwillhandlelocationupdates,permissions,status,andmore.But,acustomlocationprovidercanbeusedinstead.LocationProviderisaprotocol,encapsulatingfunctionsandpropertiesthatwillhandlelocationupdates,permissionchanges,andmore.Itnotifiesitsdelegatewhenchangesinlocation,heading,authorization,orerrorstatusoccur.ThisislikeMGLLocationManagerinpre-v10versionsoftheMapsSDK.pre-v10Inpre-v10versionsoftheMapsSDK,acustomlocationprovidercouldbeprovidedbymakingacustomMGLLocationManagerobject:classCustomLocationManager:NSObject,MGLLocationManager{
//Implementprotocolrequiredfunctions
}
self.mapView.locationManager=CustomLocationManager()
v10:AcustomlocationprovidermustconformtotheLocationProviderprotocolandimplementtherequiredfunctionsfromtheprotocol.ThecustomprovidercanbepassedtotheLocationManagerwiththefollowingcode:mapView.location.overrideLocationProvider(with:customLocationProvider)
HandlingprivacyIniOS14,Appleintroducedanewlayerofprivacyprotectiontolocation.ThereisnowanewpropertycalledaccuracyAuthorization.Thisallowstheusertoselectbetweenfullaccuracyandreducedaccuracymodesfortheirlocationtracking.Whenfullaccuracymodeisenabled,thecodewillfunctionasitdidinpreviousiOSversions.But,inreducedaccuracymode,thedevicewillsendlocationupdateslessoftenandwillspecifyalargeradiusinwhichthedeviceislocatedratherthanapreciselocation.Ifauserdecidestoturnofffullaccuracylocationanddenytemporaryfullaccuracy,thenthe2Dpuckwillberenderedasanapproximatering,whichlookslikethis:Tohandlechangesinlocationprivacy,therearetwooptions:lettheMapsSDKhandlethechangesorimplementthedelegate.LettheSDKhandlechangesinlocationprivacyTheMapsSDK'sdefaultimplementationwilllistenforachangeinaccuracyauthorization.Iftheuserhasdecidedtoturnofffullaccuracylocation,theninthenextappsession,theuserwillbepromptedwithanalerttorequestatemporaryfullaccuracyauthorization.Youcansetthisfunctionalityandthecontentsofthedialogueintheapp’sInfo.plistfile.AddthekeyNSLocationTemporaryUsageDescriptionDictionaryandaddthefollowingkey-valuepair:Key:LocationAccuracyAuthorizationDescriptionValue:AsentencedescribingwhytheapplicationneedsfullaccuracylocationsNSLocationTemporaryUsageDescriptionDictionary
LocationAccuracyAuthorizationDescription
Thisapplicationtemporarilyrequiresyourpreciselocation.
ImplementthelocationpermissionsdelegateTheLocationPermissionsDelegateprotocolhasasetofdelegatemethodsthatwillbecalledwhentheusermakeschangestotheapp'saccuracyauthorization.classViewController:UIViewController{
//Thiscontroller'sinitializationhasbeenomittedinthiscodesnippet
varmapView:MapView!
overridefuncviewDidLoad(){
super.viewDidLoad()
mapView.location.delegate=self
}
//Selectorthatwillbecalledasaresultofthedelegatebelow
funcrequestPermissionsButtonTapped(){
mapView.location.requestTemporaryFullAccuracyPermissions(withPurposeKey:"CustomKey")
}
}
extensionViewController:LocationPermissionsDelegate{
funclocationManager(_locationManager:LocationManager,didChangeAccuracyAuthorizationaccuracyAuthorization:CLAccuracyAuthorization){
ifaccuracyAuthorization==.reducedAccuracy{
//Presentabuttononscreenthatasksforfullaccuracy
//Thisbuttoncanhaveaselectorasdefinedabove
}
}
}
Thiscodesnippetalsohasourrecommendationforhowtohandlealocationpermissionchange.AwithPurposeKeyparameterisspecified.Thisvaluemustcorrespondtothekeyofakey-valuepairthatwasspecifiedinInfo.plist.NSLocationTemporaryUsageDescriptionDictionary
CustomKey
Wetemporarilyrequireyourpreciselocationforanoptimalexperience.
NewfeaturesandimprovementsPlatform-drivencameraanimationsystemTheMapsSDKv10introducesanewcameraanimationsystemwhichleveragesCoreAnimation.Alongwithprovidingfamiliarhigh-levelanimationssuchaseaseToorflyTo,theSDKprovidesexpressivenewwaysofcontrollinganimationswithmoregranularity.SupportforavarietyofanimationcurvesthatatransitioncouldfollowisincludedintheMapsSDKv10.Forexample,considerasituationthatnecessitateszoomingintothemapwhilealsorotatingthemap.ThemapView.cameraexposesasetofpowerfulnewmakeAnimator*functionsthatallowforbuildingthisinanexpressivemanner:lazyvarmyCustomAnimator:BasicCameraAnimator={
mapView.camera.makeAnimator(duration:5,curve:.easeInOut){(transition)in
//Transitionthezoomlevelofthemap'scamerato`7`
transition.zoom.toValue=7
//Transitionthebearingofthemap'scamerato`180`degrees
transition.bearing.toValue=180
}
}()
....
....
//Call`startAnimation()`tostarttheanimation(atsomepointafterthe`.mapLoaded`event)
myCustomAnimator.startAnimation()
Thisanimationwillresultinthefollowingchangestothemap'scamera:Themap'szoomwillanimatetoazoomlevelof7fromthevalueitwasbeforestartAnimation()wascalled.Themap'sbearingwillanimateto180degreesfromthevalueitwasbeforestartAnimation()iscalled.Theentireanimationwilllastfor5secondsandwillinterpolatewitha.easeInOutcurve.ControlcameratransitionswithmoregranularityThetransitionconstructpassedintoevery"animation"blockallowsyoutocontrolbothwheretheanimationcompletes(inotherwords,thefinalmapcamera)anditsstartingposition.Thesnippetbelowillustratesthis:lazyvarmyCustomAnimator:BasicCameraAnimator={
mapView.camera.makeAnimator(duration:5,curve:.easeInOut){(transition)in
//Transitionthezoomlevelofthemap'scamerafrom`10`to`7`
transition.zoom.fromValue=10
transition.zoom.toValue=7
//Transitionthebearingofthemap'scamerafrom`45`degreesto`180`degrees
transition.bearing.fromValue=45
transition.bearing.toValue=180
}
}()
....
....
//Call`startAnimation()`tostarttheanimation(atsomepointafterthe`.mapLoaded`event)
myCustomAnimator.startAnimation()
Thisanimationwillresultinthefollowingchangestothemap'scamera:First,themap'szoomwillchangeto10andthebearingwillchangeto45withoutananimatedtransition.Themap'szoomwillthenanimatetoazoomlevelof7fromaninitialvalueof10.Themap'sbearingwillanimateto180degreesfromaninitialvalueof45degrees.Theentireanimationwilllastfor5secondsandwillinterpolatewitha.easeInOutcurve.ChainanimationsBasicCameraAnimatoralsosupportscompletionblockswiththefollowingAPI:myCustomAnimator.addCompletion{positionin
print("Animationcompleteatposition:\(position)")
}
Thesecompletionblockscanbeusedtochainanimationstoexecuteoneafteranother,asinthisexample://StoretheCameraAnimatorssotheydon'tfalloutofscope
lazyvarzoomAnimator:BasicCameraAnimator={
letanimator=mapView.camera.makeAnimator(duration:4,curve:.easeInOut){(transition)in
transition.zoom.toValue=14
}
animator.addCompletion{[unownedself](_)in
print("Animatingcamerapitchfrom0degrees->55degrees")
self.pitchAnimator.startAnimation()
}
returnanimator
}()
lazyvarpitchAnimator:BasicCameraAnimator={
letanimator=mapView.camera.makeAnimator(duration:2,curve:.easeInOut){(transition)in
transition.pitch.toValue=55
}
animator.addCompletion{[unownedself](_)in
print("Animatingcamerabearingfrom0degrees->45degrees")
self.bearingAnimator.startAnimation()
}
returnanimator
}()
lazyvarbearingAnimator:BasicCameraAnimator={
letanimator=mapView.camera.makeAnimator(duration:4,curve:.easeInOut){(transition)in
transition.bearing.toValue=-45
}
animator.addCompletion{(_)in
print("Allanimationscomplete!")
}
returnanimator
}()
//StartthezoomAnimatoratsomepointafterthe`.mapLoaded`event
zoomAnimation.startAnimation()
AnimationOwnerAnimationOwnerisastructusedtokeeptrackthesource(orowner)foreachanimation.AnimationOwnerhasthefollowingpre-definedvalues:gestures:supportforanimationsrunbygestures.unspecified:supportforanon-specificanimation.CustomvaluesmaybecreatedviaAnimationOwner(rawValue:).NotesoncameraanimationABasicCameraAnimator'slifecycleisimportant.IfaBasicCameraAnimatorisdestroyedbeforeananimationiscomplete,theanimationwillimmediatelystop.CameraAnimatorsarecreatedwithadefaultownerof.unspecified.TheCameraManagerwillholdalistofallactiveanimators.3DterrainandskylayersIntheMapsSDKv10,youcanshowdramaticelevationchangesagainstanatmosphericbackdropbyenabling3Dterrainandusingthenewskylayer.Toconfigurethis,firstaddaRasterDemSourcetoamapstyle,withaURLsourcepointedtoMapbox'sglobaldigitalelevationmodel(DEM),MapboxTerrainRGB.Then,createaTerrainobjectanddefinetheexaggerationfactor,whichactsasascaletorepresentextrusion.Thehigherthevalue,themoreexaggeratedlandfeaturesathigherelevationswillappear.Addinganewskylayergivestheappearanceofthesunilluminatingland,especiallywhenthecamera’spitchischanged.Toenable3Dterrainandaddanewskylayer://Addterrain
vardemSource=RasterDemSource()
demSource.url="mapbox://mapbox.mapbox-terrain-dem-v1"
demSource.tileSize=512
demSource.maxzoom=14.0
trymapView.mapboxMap.style.addSource(demSource,id:"mapbox-dem")
varterrain=Terrain(sourceId:"mapbox-dem")
terrain.exaggeration=.constant(1.5)
//Addskylayer
trymapView.mapboxMap.style.setTerrain(terrain)
varskyLayer=SkyLayer(id:"sky-layer")
skyLayer.skyType=.constant(.atmosphere)
skyLayer.skyAtmosphereSun=.constant([0.0,0.0])
skyLayer.skyAtmosphereSunIntensity=.constant(15.0)
trymapView.mapboxMap.style.addLayer(skyLayer)
Note3Dterrainandskylayersarestillinanexperimentalstate.Itmightnotworkasexpectedwiththemapcameraanimationsystem.CustomrenderedlayersFormoreadvancedrenderingoperations,theMapsSDKv10supportsinsertingcustomMetallayerswithinthemap’sstyle.Thisunlocksmanynewopportunitiesforcustomizationandisspecificallytailoredtothoselookingtoaddtheirowncustomrenderingcode.Foranimplementationdemo,seethe“Addacustomrenderedlayer”examplewithintheExamplesapplicationformoredetails.NewOfflineManagerv10introducesanewOfflineManagerAPIthatmanagesstylepacksandproducestilesetdescriptorsforthetilestore.ReadmoreaboutOfflineManagerintheOfflineguide.TheOfflineManagerAPIcanbeusedtocreateofflinestylepacksthatcontainstyledata,suchasstyledefinition,sprites,fontsandotherresources.TilesetdescriptorscreatedbytheOfflineManagerAPIareusedtocreatetilepacksviaTileStoreAPI.MobilemapsSDKsusetilepacksforrenderingmapcontent.DeprecationsandremovalsExpressionsupportusingNSExpressionhasbeenremoved.OpenGLmaprenderinghasbeenremovedinfavorofMetal.Storingyouraccesstokenintheapplication'sInfo.plistisdiscouraged.ThelegacyOfflineManagerhasbeendeprecatedandrenamedtoOfflineRegionManager.Shareyourfeedback