ArcGIS Maps SDK for Swift v200.x provides the next-generation mapping API designed for Swift developers. It is the successor to
ArcGIS Runtime SDK for iOS v100.x and inherits the same underlying foundation, architecture, and capabilities.
Many of the API concepts remain unchanged, however they are written to align with Swift conventions, frameworks, and features, such as SwiftUI, structured concurrency, value types, measurement, default parameters, and so on.
Migrating existing apps requires significant changes, specifically rebuilding the user interface with SwiftUI and implementing asynchronous code with async/await. A solid understanding of these technologies will help ease your transition and provide a solid foundation.
API Name Changes
Type Name Changes
Types (classes, protocols, structs, and enums) are encapsulated in the ArcGIS module and no longer have the AGS prefix in their name. Here are some examples of the resulting name changes:
Classes that contain collections provide mutating methods to modify them.
100.x:
Use dark colors for code blocksCopy
1
graphicsOverlay.graphics.add(graphic)
200.x:
Use dark colors for code blocksCopy
1
graphicsOverlay.addGraphic(graphic)
Swift Structured Concurrency Related Changes
Invoking asynchronous methods no longer requires passing in a closure callback to handle completion. You can await the asynchronous method and directly use the result or handle the error.
1
2
3
4
5
6
do {
let routeResult =tryawait routeTask.solveRoute(using: routeParameters)
// Use the result.} catch {
// Handle error.}
Starting a job no longer requires passing in closure callbacks to monitor status or handle completion.
Instead, you can start a job and await its output to get the result, and asynchronously iterate through its messages to monitor the status.
1
2
3
forawait isStarted in locationDisplay.statusChanged {
// Check if started.}
Specifically, for delegate methods, such as in route tracker provided by AGSRouteTrackerDelegate, the new matching pattern is to use an asynchronous stream.
SwiftUI is a declarative UI framework. This means that when you create a View, you also create a description of what to display with a given state. This is a change from a UIView in UIKit, where you create an view with a defined frame. In SwiftUI, views are contained in the body of a parent view; but in UIKit views were added to the view controller's root view either programmatically or using Storyboard. The following code shows how to create a map view:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// A model to store various model objects required by the geo view.privateclassModel: ObservableObject{
let map =Map(basemapStyle: .arcGISOceans)
}
// Held as a StateObject so it doesn't get recreated when the view's state changes.@StateObjectprivatevar model =Model()
// The current viewpoint of the map view.@Stateprivatevar viewpoint =Viewpoint(
center: Point(x: -117, y: 34, spatialReference: .wgs84),
scale: 1e5)
var body: someView {
MapView(
map: model.map,
viewpoint: viewpoint
)
}
View Modifiers
View modifiers are applied to the views to customize their appearance and behavior. The new MapView and SceneView have various view modifiers to achieve the same functionalities as their UIKit counterparts.
Viewpoint: Update the viewpoint state variable with the current viewpoint of the map view.
Due to differences between UIKit and SwiftUI, performing operations such as identify cannot be performed on the map view directly. In v200.x, we follow SwiftUI's reader-proxy design pattern, and introduce MapViewProxy to allow operations such as identify to be performed.
The following code shows the difference between identifying using AGSGeoView.identifyLayers in v100.15 vs. using MapViewProxy.identify in v200.x.
View updates in SwiftUI are driven by state changes. The following code shows getting the latest visible area of a map view in v100.15 compared to v200.x.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
privateclassModel: ObservableObject{
let map =Map(basemapStyle: .arcGISOceans)
}
@StateObjectprivatevar model =Model()
/// The visible area of the current map view./// Can be used in an overview map.@Stateprivatevar visibleArea: Polygon?
var body: someView {
MapView(map: model.map)
.onVisibleAreaChanged { visibleArea =$0 }
}