Skip navigation
All Places > Developer Communities > Native App Developers > ArcGIS Runtime SDK for iOS > Blog

I'm not going to hide it, I love the ArcGIS Runtime's loadable design pattern. We find the loadable pattern across the ArcGIS runtime; no doubt you interact with it often.

In short, the pattern allows you to work with an object that might take some time figuring stuff out before it’s ready to be used. For example, it might depend on multiple (possibly remote) resources, sometimes in sequence, before it knows enough about itself to be usable. Once these resources are retrieved, the loadable object executes a callback block signaling that it's ready. An AGSMap is a good concrete example as it might need to load multiple remote layers before it knows what extent and spatial reference to use.

Some other qualities of a good loadable object include:

  • calling the completion block on a suitable thread based off the thread the load was started from (see this blog post).
  • providing an observable loadStatus and loadError, though generally you just wait for the completion block to be called.
  • handling load failure elegantly with the option to retry later if needed (perhaps the network connection was interrupted).

As an added bonus, a full implementation of <AGSLoadable> can be found in AGSLoadableBase, a class designed to be subclassed (and which saves you reinventing a lot of wheels, doing a lot of the heavy lifting of load state and callback thread considerations).

"It's only a protocol," you might say. "You can't always subclass AGSLoadablebase," you might suggest. "Try implementing AGSLoadable yourself, do you still love it?" you might pronounce.

Hot take, but of course you'd be right. As an engineer, I'm tickled by problems like this and eagerly look for solutions.

Today I'd like to share with you a delightful maneuver that allows any class to adhere to <AGSLoadable> with neither the need for a custom async implementation (yikes) nor subclassing AGSLoadableBase.

I'd like to introduce you to LoadableSurrogate & <LoadableSurrogateProxy>, a member/protocol solution that offloads the heavy lifting of async loading onto a surrogate loader.

There are two actors in this maneuver, engaged in a parent/child delegate-like relationship:

  1. LoadableSurrogate is a concrete subclass of AGSLoadableBase that routes messages to a proxy object.
  2. A proxy object that adheres to <LoadableSurrogateProxy> we'd like to make loadable with the help of a loadable surrogate.

A class can leverage this tool by creating a LoadableSurrogate member, adhering to <LoadableSurrogateProxy> and specifying the LoadableSurrogate's proxy.

In this example, I've built a simple loader that downloads an image of (my favorite muppet) Kermit the Frog hanging out on the legendary Hollywood walk of fame.

class KermitLoader: NSObject, LoadableSurrogateProxy { }

I can use the kermit loader object like any other <AGSLoadable>:

let kermitLoader = KermitLoader()  kermitLoader.load { (error) in  
  
    if let error = error {        
        print("Error: \(error.localizedDescription)")    
    }

    imageView.image = kermitLoader.kermitImage
}

The kermit loader is initialized with a LoadableSurrogate member, assigning the surrogate's proxy to self.

class KermitLoader: NSObject, LoadableSurrogateProxy {

    private let surrogate = LoadableSurrogate()         

    override init() {
        super.init()        
        surrogate.proxy = self
    }    
    /* ...

For the kermit loader to conform to <LoadableSurrogateProxy> it must also conform to <AGSLoadable>. Conveniently, all <AGSLoadable> methods can be piped through the surrogate.

    ... */
    func load(completion: ((Error?) -> Void)? = nil) {
        surrogate.load(completion: completion)    
    }         

    func retryLoad(completion: ((Error?) -> Void)? = nil) {        
        surrogate.retryLoad(completion: completion)    
    }         

    func
cancelLoad() {        
        surrogate.cancelLoad()    
    }    
    /* ...

Following the same pattern outlined above, you might opt to compute the <AGSLoadable> properties loadStatus and loadError on the fly, getting those values from the surrogate.

Instead, I've opted to persist those properties and thus, expose them to KVO.

    ... */
    @objc var loadStatus: AGSLoadStatus = .unknown

    @objc var loadError: Error? = nil
    //
    // Proxy informs of changes to `loadStatus` and `loadError`.
    //

    func loadStatusDidChange(_ status: AGSLoadStatus) {        
        self.loadStatus = status    
    }         

    func loadErrorDidChange(_ error: Error?) {        
        self.loadError = error    
    }    
    /* ...

Everything we've seen up until this point is boilerplate and can be copied and pasted. Let's get to the good stuff.

First, in order to perform the loadable operation we'll need to set up some resources and properties. We need a URL to the image, a data task, and of course a reference to the loaded image.

    ... */
    private let kermitURL = URL(string: "https://c1.staticflickr.com/2/1033/1024297684_582bc1c05a_b.jpg")!

    private var kermitSessionDataTask: URLSessionDataTask?

    var kermitImage: UIImage? = nil
    /* ...

What comes next is the custom loadable implementation. If you have ever subclassed AGSLoadableBase directly, this should feel familiar.

The proxy object is responsible for starting the load and completing with an error or nil, depending on the success of the operation. The proxy object is also responsible for canceling any async operations as well.

    ... */
    func doStartLoading(_ retrying: Bool, completion: @escaping (Error?) -> Void) {   

        if retrying {                         
            let previousDataTask = kermitSessionDataTask            
            kermitSessionDataTask = nil
            previousDataTask?.cancel()            
            kermitImage = nil
        }                 

        kermitSessionDataTask = URLSession.shared.dataTask(with: kermitURL) { [weak self] data, response, error in

            guard let self = self else { return }                         

            if let data = data, let image = UIImage(data: data) {                
                self.kermitImage = image            
            }                         

            if response == self.kermitSessionDataTask?.response {                
                completion(error)            
            }        
        }                 

        kermitSessionDataTask!.resume()    
    }    
    /* ...

The proxy object is also responsible for canceling running operations. If you want the surrogate to supply a generic CancelledError, you can return true. In this example the data task reliably provides its own cancel error in the task's callback and thus we return false.

    ... */
    func doCancelLoading() -> Bool {                 
        kermitSessionDataTask?.cancel()        
        kermitSessionDataTask = nil
        kermitImage = nil
        // Returns `false` because the URLSession returns a cancel error in the completion callback.
        // Return `true` if you want the surrogate to supply a generic cancel error.
        return false
     }
}

Cool! Now let's take a look under the hood of the LoadableSurrogate, powering much of the kermit loader.

To start, a LoadableSurrogate is a subclass of AGSLoadableBase.

class LoadableSurrogate: AGSLoadableBase { /* ...

A LoadableSurrogate passes messages to a proxy object. As you saw in the KermitLoader.init(), the kermit loader specifies itself as the proxy.

    ... */
    weak var proxy: LoadableSurrogateProxy? {        
        didSet {            
            proxy?.loadStatusDidChange(loadStatus)            
            proxy?.loadErrorDidChange(loadError)        
        }    
    }    
    /* ...

A LoadableSurrogate observes loadError and loadStatus so that it may immediately inform the proxy of changes to either of these properties.

... */
    // Cocoa requires we hold on to observers.
    private var kvo: Set<NSKeyValueObservation> = []         

    override init() {           

        super.init()     

        let loadStatusObservation = self.observe(\.loadStatus) { [weak self] (_, _) in

            guard let self = self else { return }                         

            self.proxy?.loadStatusDidChange(self.loadStatus)        
        }                 

        kvo.insert(loadStatusObservation)                 

        let loadErrorObservation = self.observe(\.loadError) { [weak self] (_, _) in

            guard let self = self else { return }                         

            self.proxy?.loadErrorDidChange(self.loadError)        
        }                 

        kvo.insert(loadErrorObservation)    
    }    
    /* ...

And finally a LoadableSurrogate handles piping the loadable method calls to and from the proxy.

    ... */
    private let UnknownError = NSError(domain: "LoadableSurrogate.UnknownError", code: 1, userInfo: [NSLocalizedDescriptionKey: "An unknown error occurred."])         

    override func doStartLoading(_ retrying: Bool) {                 

        // We want to unwrap the delegate, if we have one.
        if let proxy = proxy {               

            // Call start loading on the delegate.
            proxy.doStartLoading(retrying) { [weak self] (error) in

                guard let self = self else { return }                                 

                // Finish loading with the reponse from the delegate.
                self.loadDidFinishWithError(error)            
            }        
        }        
        else {            
            // No delegate, finish loading.
            loadDidFinishWithError(UnknownError)        
        }    
    }

    private let CancelledError = NSError(domain: "LoadableSurrogate.CancelledError", code: NSUserCancelledError, userInfo: [NSLocalizedDescriptionKey: "User did cancel."])     

    override func doCancelLoading() {  

        // Call cancel delegate method.
        if proxy?.doCancelLoading() == true {                         

            self.loadDidFinishWithError(CancelledError)        
        }    
    }
}

To see this maneuver in action, have a look at this playground.

Happy loading!

The latest release of the ArcGIS Runtime Toolkit for iOS is here. It has been updated to work with the 100.5 release of the Runtime SDK for iOS.
 
This release includes:
• New PopupController component and example; the PopupController provides a complete feature editing and collecting experience.
• New TemplatePickerViewController component and example; the TemplatePickerViewController allows the user to choose from a list of AGSFeatureTemplates, commonly used for creating a new feature.
• Fix for a TimeSlider issue when using a combination of play/pause and manual dragging.
• Project updates for Xcode 10.2

 

You can find the Toolkit here.

 

See this blog post for information on the SDK release.
 
We hope you enjoy the new release! Let us know what you're building with it.

The latest release of the Runtime SDK for iOS is here (see the release notes, and the general announcement over on the ArcGIS Blog), and it brings with it a slew of great new features, and some cool new internals that pave the way for even more goodness in future releases.

 

Some highlights from the iOS perspective:

  • We're now promoting the use of the Dynamic Framework for integrating ArcGIS Runtime SDK for iOS into your projects (and have deprecated the Static framework, which will be removed in a future release). This is Apple's preferred approach and matches what CocoaPods has been doing since Runtime 100.1. It also makes integration with the project simpler (e.g. you no longer explicitly add ArcGIS.bundle to your projects). See Configure your Xcode Project in the iOS SDK Guide.
  • In alignment with Apple's policies and since 100.5 now requires iOS 11 or higher, support for 32-bit devices has been dropped. You shouldn't notice any change as a developer, but it means means that the SDK installer package is now much smaller to download!
  • If you've been using custom views in an AGSCallout, the Runtime now makes use of AutoLayout to fit the view. See this release note.

 

This is not specific to the iOS Runtime SDK, but if you work with selections in your map view, please note the new behavior we have introduced at 100.5. These changes were brought about as we prepare for significant features down the line (including Metal support).

 

As has been mentioned before, 100.5 is the last release of the dedicated Runtime SDK for macOS.

 

The Samples app and Toolkit have already been updated for 100.5, and the Open Source apps will be update shortly (Data Collection already has been!).

 

We hope you enjoy the new release! Let us know what you're building with it.

After some discussion, the decision has been made to deprecate the ArcGIS Runtime SDK for macOS. The upcoming 100.5 update will be the last release of the dedicated Runtime SDK for macOS. As with any other release, support will be provided according to the Product Lifecycle Support Policy.

 

It's not a decision taken lightly but, for a number of reasons, we're confident that it's the right move. 

 

Firstly: interest in the Runtime SDK for macOS has been low. By freeing up team members from maintaining it (including not just the ArcGIS Framework, but also the Samples App, guide documentation, etc.), we'll be able to implement improvements across the entire Runtime SDK family more effectively.

 

Secondly: the decision was made easier given that developers targeting macOS as a platform still have the option of using the ArcGIS Runtime SDK for Java or ArcGIS Runtime SDK for Qt. If you are considering developing Runtime apps targeting macOS, we recommend you investigate those.

 

If you have questions about this deprecation, feel free to use the comments section below or, if you're coming to the Developer Summit in Palm Springs, come and see us at the developer island.

The Runtime SDK does a lot of work behind the scenes to make it as simple as possible for you to write great, interactive mapping apps with fast, smooth user interfaces.

 

Getting out of the way

Key to that is making sure that when you ask Runtime to do something asynchronous (query some features, autocomplete an address, load a service definition etc.), that it gets off the thread you called from as quickly as possible and does its work on another thread. Runtime does this really well and you should never see it get in your way while it's doing something.

 

But when it's done doing that something, which thread does Runtime use to let you know?

 

The good news is iOS and Runtime work together so you might never have to worry about this. But you're a good developer, and you're doing some carefully thought out threading yourself, so you want to know the details, right?

 

UI and the main thread

Where you need to know which thread you're on is if you're updating your app's UI. In iOS, all UI updates must be done on the main thread (or the Main Thread Checker will come after you and your UI won't behave). So if you're not on the main thread and you want to enable that button and update that label, you need to fix that.

 

Luckily, any time iOS enters your code because of a user interaction or view-related hook (e.g. viewDidLoad()), you will already find yourself on the main thread. For a lot of developers this means not having to worry about threading at all until they build something cpu-intensive, at which point they'll need to push that work onto another thread¹.

 

Efficient behavior

Even though Runtime will use other threads to get out of your way, there are some reasons it's better not to switch threads if possible.

  • Context switching between threads takes up CPU cycles.
  • There are some common workflows where even though the pattern is asynchronous, it's quite possible Runtime already has the answer for you and can hand it over immediately. In those cases, context switching would be a waste of time.

 

Adding it all together

These considerations combine to help Runtime determine how to call back to you on the various callback blocks, observers, and change handlers provided by the Runtime SDK.

 

Here's a quick rundown of the ways the ArcGIS Runtime SDK for iOS might call back to you, and the thread you should expect to be called back on:

 

Operation/HandlerThreadNotes

Explicit Calls:

Main | Any
  • If you call from Main, Runtime will call back on Main.
  • If you don't call from Main, Runtime could call back on any thread.

 

Calling Runtime from the main thread is a pretty good indicator that you're calling because of a user interaction, so it's reasonable to expect that you'd want to update some UI when Runtime responds (and that always needs to happen on the main thread).

 

But if you didn't call Runtime from the main thread, maybe there isn't a pair of eyes waiting for the result so Runtime skips the overhead of making sure it's back on the main thread when it responds, ensuring your robots can continue at full speed.

AGSGeoViewTouchDelegate

Main
  • Runtime will always call back on Main.

 

Just as iOS enters your code on the main thread when there's some user interaction, Runtime makes sure to do the same through the AGSGeoViewTouchDelegate. So if you're handling a user tapping on the map, for example, it's safe to update your UI directly.

 

However, if you're observing some property that happens to be changing because of user interaction (e.g. using KVO to monitor mapScale), the "State Feedback" rule below applies.

AGSLoadableMain
  • Runtime will always call back on Main².

 

More often than not, customer code calls into load() or retryLoad() from the main thread. If an item is already loaded, Runtime can then respond immediately with no context switching required. It makes the loadable pattern very fast for the case where, as the result of a user interaction, you need to ensure something is loaded before doing something else (e.g. determining a feature layer's renderer). In that case, only the very first interaction will incur a performance penalty, but you write your code once and it'll handle the first time or the 100th.

State Feedback:

Any
  • Runtime could call back on any thread.

 

Context switching back to the main thread could mean that feedback is returned out of order or be delayed, so Runtime will provide that feedback immediately on whichever thread is current. If you need to update your UI as a result, you should dispatch your UI code to the main thread yourself with something like:

DispatchQueue.main.async {
    /* update the UI */
}

 

Summary

The above can largely be summarized like this:

  • Things that update status or state (progress, KVO, etc.) can happen on any thread.
  • Deliberate actions (Tasks and Jobs) that are called from the main thread will receive their status updates and results on the main thread. If they're call from some other thread, the thread used to respond could be any thread.
  • AGSLoadable and AGSGeoViewTouchDelegate will always call back on the main thread.

 

Let me know in the comments if you have questions about any of the topics brought up here. This can be a complex issue, but iOS and the Runtime SDK make sure that you usually don't need to worry about it.

 


¹ iOS includes a powerful thread abstraction API (Grand Central Dispatch, or GCD) that's worth learning about to help with concurrent programming and slick app experiences. Here's a great 2-part tutorial.

² Note: this load() behavior was updated at release 100.3 to provide a number of performance optimizations. Up until release 100.2.1, Runtime would promise to call back on the main thread only if you called in from the main thread (identical to the current Task/Job behavior).

You've been asking and we listened. The Runtime Example Apps team is thrilled to announce the release of Data Collection for iOS, the newest member of the ArcGIS Runtime Example Apps family. 

 

We built the app as the springboard for your organization's iOS data collection solution. The app is designed to consume your organization's web maps, out of the box. Written in Swift, this app demonstrates best practices for consuming the ArcGIS Runtime iOS SDK.

 

A user can view and edit data (including related records) in both connected and disconnected work environments and easily synchronize changes between an offline map and its corresponding web map.

 

To demonstrate the app, we curated a sample web map named Trees of Portland. Let's take a quick look at the app in action.

 

Collect Data

 

To add a new feature, tap the plus button located in the navigation bar at the top-right and pan the map until the pin is in the correct location. Tap the green check button and you're presented with a form to fill out the tree's attributes.

 

Add feature

 

Complete the form and your new feature is added to the map. You can then add related records, if your web map is configured accordingly.

 

Add related record

 

Take Map Offline

 

Select an extent of the map to take offline and the app kicks off a generate offline map task. Once downloaded a user can toggle between working offline and . Tap to synchronize changes bi-directionally.

 

Generate offline map

 

For the complete picture, have a look at the documentation. We encourage you to clone or fork the open source app which you can download from GitHub and build using the Runtime iOS SDK (v 100.3 or later). Contributions to the project are welcome as is general feedback. 

At their annual developer's conference a couple of weeks ago, Apple announced that they will be deprecating OpenGL and OpenGL ES with the releases of macOS 10.14 and iOS 12 later this year. What does this mean for the ArcGIS Runtime SDKs, which use these technologies, and for your apps built on top of them?

 

Short version: just carry on as normal. Work is already underway at Esri to adopt Metal well in advance of Apple removing OpenGL and OpenGL ES. As a Runtime developer, you'll simply download an updated version of the Runtime SDK.

 

For the longer version, read on.

 

Runtime Engineering

 

The ArcGIS Runtime engineering team here at Esri began planning for Metal support earlier this year as part of a broader review of Runtime GPU performance and capabilities. Once complete this work will dispense with the need for OpenGL on iOS and macOS.

 

That said, it's also important to understand what Apple mean by "deprecation"…

 

Apple Deprecations

 

When Apple deprecates a technology, it's a warning that the technology will be removed in "some future version of the OS" (see Apple's notices here for OpenGL and here for OpenGL ES). That means it will work until at least Fall 2019 when iOS 13 and macOS 10.15 will be released.

 

But in Apple's own words, they take it even further:

 

Deprecated APIs typically remain present and usable in the system for a reasonable time past the release in which they were deprecated.

 

This means it's quite possible we'll see OpenGL and OpenGL ES stick around through iOS 13 and macOS 10.15, taking us to Fall 2020. Although Apple could always opt for a more aggressive timeline, experience tells us that's unlikely.

 

Cross Platform ArcGIS Runtimes

 

What about the ArcGIS Runtime SDKs that target iOS and macOS as part of a cross-platform strategy? Well, thanks to the encapsulation of functionality in the common Runtime core, all of the above considerations and timelines also apply to the ArcGIS Runtime SDK for .NET (Xamarin.iOS) and to the ArcGIS Runtime SDK for Qt.

 

Your Apps

 

Once Esri releases a version of the Runtime SDK that includes support for Metal, you should plan on updating the Runtime SDK used by your apps before Apple removes support for OpenGL.

In mid-december, just in time for the holidays, we released ArcGIS Runtime 100.2.0 across all supported platforms (which of course includes iOS). It is available for you to download here. This is an exciting release for the entire team because it brings us closer to functional parity with the 10.2.x versions of Runtime and is what we originally envisioned Runtime 100 to be.

 

Here are some highlights:

 

  • Read and edit Shapefiles
  • Read and edit vector data from GeoPackages
  • Read rasters from GeoPackages
  • Support for multi-layered symbology
  • Time support
  • Display and identify WMS layers
  • Display and identify ENC layers (Electronic Navigational Charts)
  • Create and update mosaic raster datasets
  • On-device GPU based analysis - viewshed and line of sight on the GPU
  • Statistical queries
  • Improved support for geotransformations and custom geotransformations
  • Offline mapping enhancements including Preplanned workflows
  • Support for transactional editing

 

Take a look at our main blog post for more details.

 

Also, at this release we no longer support Xcode 8 or iOS 9 (see the Release Notes for more details).

 

We're already hard at work on 100.3.0 but in the meantime keep an eye out for 100.2.1 which will add a couple of bits of functionality that we couldn't quite get done in time for 100.2.0 (like encrypted ENC layers and support for some older WMS versions).

Each of our Example Apps is designed to provide some inspiration for your own. If you’re interested in building your own custom app because one of our off-the-shelf apps isn’t quite right for you, then the Example Apps are a great place to start.

 

With the Maps App for iOS, we show how you might build the foundation of your own ArcGIS Runtime mapping app using Swift.

 

The open source app (which you can download and build from GitHub) highlights fundamental Runtime functionality and showcases some approaches to coding against asynchronous services. It includes a robust internal framework and a modern, decoupled UI.

 

In later posts, we’ll take a look at the UI/UX and what it took to put the app together, but for now let’s take a look at using the app.

 

Search & Geocode

To search, simply start typing into the Search Bar at the top of the screen.

 

As you type, you’ll see suggestions appear, and you can either pick a suggestion or search for the text you’ve typed.

 

 

By default, you can search for places or addresses using the ArcGIS  World Geocoder, and the suggestions will prioritize matches close to the center of the map.

 

Reverse Geocode

If you tap and hold on the map, you’ll see a magnifier. Use this to pick a point on the map and when you’re done, you’ll get the address of that point.

 

 

Turn-by-turn Directions

Whether you’ve searched or reverse-geocoded, the results panel includes a “Directions” button. Tap this to calculate directions from your current location to the search result.

 

At the top of the screen you’ll see an overview of the entire route, and at the bottom you can see turn-by-turn directions. Just swipe through them and the map will update to display the current step. If you ever want to go back to the entire route, simply tap the route summary at the top of the screen.

 

 

Note: Since the routing service consumes ArcGIS  credits, you'll need to log in to get directions. The ArcGIS Runtime includes a Credentials Cache and by default, if you've already logged in, the Runtime is able to intelligently make use of cached credentials to avoid prompting you for a login.

 

Switch Basemaps

The application also allows you to pick from a set of basemaps. If you are logged in to an ArcGIS  Organization or to an ArcGIS Portal, then the list of basemaps will reflect those configured for your account. If not, then you'll get to pick from the default ArcGIS  basemaps.

 

 

Browse your Web Maps

The last bit of functionality the app provides is the ability to browse and open your Web Maps. When logged in to ArcGIS  the Maps App makes use of the Runtime Portal API to query your content and present you with a list of Web Maps. Simply tap one to open it in the app.

 

 

What did we learn building this functionality?

There are some interesting points to consider from all this.

 

Authentication

In the case of getting directions and browsing Web Maps, the user must log in. But how should your app behave when the user isn't logged in?

 

When not logged in, we decided to allow the user to search and geocode using the ArcGIS  World Geocoding Service (which is free to use, as long as you're not storing the results).

 

But once the user is logged in, the Maps App uses the Portal API to determine which Geocoding Service and Routing Service to use and, as mentioned above, which basemaps to list. Your ArcGIS  Organization's Administrator can configure these settings, so it's important that your app reads and honors that configuration.

 

Lastly, consider how a user should be prompted to log in. For the Maps App we opted to make use of the ArcGIS Runtime's integration with OAuth 2.0, which made implementing login really straightforward.

 

iOS Location Permissions

It's also important for an iOS app to behave properly if the user hasn't enabled Location Services or has explicitly denied the app access to their location. When possible, asking for directions will get directions from the current location, but if that's not available then the app will get directions from the center of the current map view.

A short while ago we released the next generation of ArcGIS Runtime SDKs for iOS, macOS, Android, Java, Qt and .Net. It is the first time we were able to release all the SDKs at the same time and with the same set of functionality. This was possible because the Runtime SDKs were rebuilt from the ground up using a completely new architecture and introduce a number of new concepts, patterns, and capabilities that are common to all the SDKs. We're very excited for you to try out this latest version (v100.0) and you can read more about its new and notable features in the Release Notes.

 

Background

Version 100.0 has a number of changes from the previous 10.x line of releases, far too many to compile an exhaustive list, but in this post I will provide an overview of some of those changes so that it can guide your migration efforts. The changes were necessary to accommodate an ambitious agenda of new capabilities such as visualizaiton of 3D scenes, direct read and processing of local rasters, new data formats such as mobile map packages and vector tiles, and many more yet to come, but most importantly, to more closely unify all the different SDKs to have a common conceptual design and similar programming patterns to help many of you who are involved in building apps on multiple development platforms.

 


Before you migrate


Eventhough v100.0 is packed with a lot of new capabilities, unfortunately it does not provide all the functionality that was available with 10.2.x at this time. We are working hard to add the missing pieces with subsequent releases and our goal is to achieve full functional parity later this year. So before you begin migrating existing apps to v100.0, you should refer to choosing the right version to see if the current release contains all the functionality your existing apps require. If not, don't worry. We have extended the support lifecycle for version 10.2.x so that you can continue to use it until equivalent functionality is available in the latest releases.


Overview of changes

 

Web Map

AGSWebMap and its related classes that used to represent a web map have been removed. A web map is now represented by AGSMap which can have a variety of sources. This makes working with maps simple and consistent regardless of where it originates from, be it on a remote ArcGIS portal, inside a mobile map package (.mmpk file) on disk, or even it is only in memory that has been programmatically created by combining layers and a basemap.

 

 

Layers

In general, layers do not automatically load their metadata asynchronously and they no longer have a delegate (AGSLayerDelegate) that is notified when the layer is initialized or when it fails to load. Rather, layers now follow the loadable pattern (AGSLoadable) that is used to explicitly load their metadata and monitor their load status. Refer to the loadable pattern topic for more information.

 

AGSDynamicMapServiceLayer has been renamed to AGSArcGISMapImageLayer. Sublayer visibility is now controlled by removing or adding sublayers to the mapImageSublayers array.

 

AGSTiledServiceLayer and AGSLocalTiledLayer have been combined into a single AGSArcGISTiledLayer that can display tiles from a remote ArcGIS Tile Service or a local tile cache (AGSTileCache).

 

AGSFeatureLayer no longer connects directly to a feature service. Rather, it requires a feature table which contains the feature data to be displayed by the layer. The query and editing capabilities of the layer are now performed on the table instead. For offline scenarios, you can use an AGSGeodatabaseFeatureTable that represents data in a local geodatabase on disk, and for connected scenarios, you can use an AGSServiceFeatureTable which represents the feature data in a remote feature service. For cases when the data exists only in the app in memory and not on disk or in a remote service, you can use AGSFeatureCollectionTable.


AGSImageServiceLayer, AGSWMTSLayer, and AGSWMSLayer have been removed. They are being considered for a future release.


Map View

You can no longer add layers directly to the mapview. Instead, layers need to be added to a map which is then set on the mapview. To display graphics, you need to add a graphics overlay to the mapview containing those graphics. These graphics are displayed on top of all other map layers.

 

To programmatically navigate the mapview, you need to change its viewpoint using setViewpoint...() methods. AGSMapViewDidEndZoomingNotification and AGSMapViewDidEndPanningNotification notifications have been removed. To be informed when a user pans or zooms the maps, you can register a block with the viewpointChangedHandler.

 

To control which gestures and interactions are supported by the mapview, you can change its interactionOptions settings.

 

To be notified about touch events on the mapview, you need to set a touch delegate on the mapview. This delegate must conform to the AGSGeoViewTouchDelegate protocol which is a replacement of the previous AGSMapViewTouchDelegate protocol. The new delegate methods are similar to the old ones but there are some minor name changes. You must implement these delegate methods with the correct signatures to be sure they are invoked. Also, the touch events no longer automatically return the graphics or features that were hit-tested at the touch location. You need to manually perform the hit-testing in the touch event handler using identifyGraphicsOverlay...() or identifyLayer...() methods on the mapview.

 

AGSMapViewLayerDelegate has been removed. As a replacement for mapViewDidLoad() delegate method, you can monitor the mapview's spatialReference property to know when it is fully initialized to display its map contents. The other delegate methods are no longer relevant as the mapview does not perform automatic hit-testing of features/graphics on touch events as described above.

 

AGSMapViewCalloutDelegate has been removed. The mapview no longer automatically displays or dismisses the callout when a user taps on graphics or features the map. You are responsible for displaying and dismissing the callout by listening to the touch events.

 

Callout

The mapview no longer automatically displays or dismisses the callout when a user taps on graphics or features on the map. You are responsible for displaying and dismissing the callout by listening to the touch events and hit-testing for graphics or features using identifyGraphicsOverlay...() or identifyLayer...() methods on the mapview.

AGSHitTestable,  AGSInfoTemplateDelegate and  AGSLayerCalloutDelegate have also been removed for the same reason.


Graphics

Graphics Layer (AGSGraphicsLayer) has been replaced by Graphics Overlay (AGSGraphicsOverlay`). Graphics need to be added to a graphics overlay instead of a graphic layer. Graphics Overlays display graphics on top of all other layers in a map. They cannot be interleaved with other layer like was the case with Graphics Layers.

If you want to interleave your own data with the map layers, consider using a feature collection layer (AGSFeatureCollectionLayer) instead of graphics overlays. A feature collection layer displays data in a feature collection which contains features organized into individual feature collection tables. You can create one of these feature collection tables in memory and add your feature data to it.

 

Sketch

Sketch Layer (AGSSketchGraphicsLayer) has been replaced by Sketch Editor (AGSSketchEditor). A sketch editor needs to be set on the mapview and then started to begin creating or modifying a sketch.

The AGSSketchGraphicsLayerGeometryDidChangeNotification notification has been replaced with AGSSketchEditorGeometryDidChangeNotification

 

Geometries

Mutable geometries (AGSMutablePoint, AGSMutableEnvelope, AGSMutablePolygon, AGSMutablePolyline, AGSMutableMultipoint) have been replaced with geometry builders  - AGSPointBuilder, AGSEnvelopeBuilder, AGSPolygonBuilder, AGSPolylineBuilder, AGSMultipointBuilder.

 

Instance methods on geometry engine (AGSGeometryEngine) have been changed to static class methods.

 

Tasks

Geodatabase Sync Task (AGSGDBSyncTask) has been renamed to AGSGeodatabaseSyncTask. Also, the asynchronous operations to generate new a geodatabase and sync an existing geodatabase now return a job handle (AGSGenerateGeodatabaseJob, and AGSSyncGeodatabaseJob) to represent the execution on the service. The job has to be explicitly started to initiate the processing on the service and it also provides the results of the execution in the completion block.

 

Export Tile Cache Task (AGSExportTileCacheTask) also returns a job handle (AGSExportTileCacheJob, and AGSEstimateTileCacheSizeJob) for the asynchronous operations to generate a tile cache and estimate the tile cache size. The job has to be explicitly started to initiate the processing on the service and it also provides the results of the execution in the completion block.

 

Geometry Service Task (AGSGeometryServiceTask) has been removed. Use AGSGeometryEngine instead.

 

Query Task (AGSQueryTask) has been removed. Use the query...() methods on AGSServiceFeatureTable instead.

 

Identify Task (AGSIdentifyTask) has been removed. Use identifyGraphicsOverlay...() or identifyLayer...() methods on AGSMapView instead.

 

Locator (AGSLocator) has been renamed to AGSLocatorTask. AGSLocatorDelegate has been removed and replaced with completion blocks on the locator task's asynchronous operations that previously required the delegates.

 

AGSRouteTaskDelegate has been removed and replaced with completion blocks on the route task's asynchronous operations that previously required the delegates.

 

Geoprocessor (AGSGeoprocessor) has been renamed to AGSGeoprocessingTask. The asynchronous operations to perform the geoprocessing function returns a job handle (AGSGeoprocessingJob). The job has to be explicitly started to submit it to the service and it also provides the results of the execution in the completion block. The AGSGeoprocessorDelegate has been removed and replaced with completion blocks on the job's asynchronous operations.

 

Find Task (AGSFindTask), Closest Facility Task, (AGSClosestFacilityTask), Service Area Task (AGSServiceAreaTask) have been removed. They are being considered for a future release.


Portal

The following delegates have been removed - AGSPortalDelegate, AGSPortalGroupDelegate, AGSPortalInfoDelegate, AGSPortalItemDelegate, AGSPortalUserDelegate. They have been replaced with completion blocks on the corresponding asynchronous operations that required the delegates.

 

AGSPortal, AGSPortalItem, and AGSPortalUser classes implement the loadable pattern (AGSLoadable) which means they must be loaded for their information to be available.

 

The following notifications have been removed. The AGSLoadable protocol provides mechanisms to monitor the loadStatus of AGSPortal
- AGSPortalDidLoadNotification
- AGSPortalDidFailToLoadNotification

 

Authentication

Performing authentication has been re-architected around a central authentication manager (AGSAuthenticationManager) that issues challenges (AGSAuthenticationChallenge) when a resource (such as a layer or a task) attempts to access a secure service. Additionally, the SDK contains an in-built challenge handler that tries to resolve the challenge by displaying UI to solicit user details, but developers can choose to customize this behavior and handle challenges themselves by providing a delegate (AGSAuthenticationManagerDelegate).

AGSOAuthLoginViewController has been removed. To support oAuth based authentication in your app you need to set the app's oAuth configuration (AGSOAuthConfiguration) on AGSAuthenticationManager. The configuration includes, among other things, a redirectURL which must use a custom URL scheme for the app. This will allow the user to sign in using an external Safari browser which offers more security as compared to performing the authentication in app. When the user finishes logging in through Safari, the app will be invoked and passed the credentials to access the resource. You will need to use AGSApplicationDelegate to handle this app invocation and complete the oAuth authentication workflow.

 

Licensing

Like earlier, you have access to all the capabilities during development and testing, however the licensing model for deployment has changed to include 4 levels - Lite, Basic, Standard, and Advanced where each level unlocks a different set capabilities. You can license your app either by using a license key or a by logging into a portal with a named user. More details are available in the License your app topic.


Additional resources

The information above provides a high level overview of the changes between version 10.2.x and 100.0, but as you embark on migrating your apps, you may find yourself deep in the weeds needing more fine-grained, detailed information about how to change references to classes, methods or properties that have changed and no longer compile. We know that many of you borrow heavily from the SDK samples or use them as a starting point and build on it. So to ease your migration path a little and provide more detailed information, we've ported the most popular 10.2.x samples to the latest version of the SDK. You can download the diff and open it in Safari to see a simple side-by-side comparison of what code needed to be changed. The diff is searchable in Safari so you can look for specific classes, methods, or properties and quickly find the corresponding changes. We've also made the ported samples available for download in a branch on GitHub.

 

We hope this post is useful as you migrate to 100.0. Feel free to reach out to the community on the Geonet forum for any questions or to share your experience, we'll be watching that space and there to help you along the way.

Want to test drive the latest capabilities of ArcGIS Runtime but don't have a developer setup ready to go? Ever wish you could see the technology in action before you roll up your sleeves and dive in? You're in luck. We're excited to announce that the samples for ArcGIS Runtime SDK for iOS are now available on Apple's App Store.

Download the app on your iPhone or iPad and explore the samples to get a first hand experience of the functionality that is available for you to incorporate into your own custom apps. Browse the code behind each sample from within the app and see how easy it is to use the SDK.

The app is entirely developed in Swift and the source code is available on GitHub

Samples are organized into the following categories -

+ Maps - Open, create, interact with and save maps
+ Layers - Layer types offered by the SDK
+ Features - Work with Feature layers and geodatabases
+ Edit Data - Add, delete and edit features and attachments
+ Display Information - Display graphics, popups, callouts, and sketches
+ Search - Find an address, place, or point of interest
+ Geometry - Create geometries and perform operations
+ Route & Navigation - Find a route around barriers
+ Scenes - Display scenes, 3D symbols, and scene layers

More categories and samples will be added with future updates. Stay tuned!

If you've upgraded your development environment to the recently released XCode 7, you might have noticed that your apps started encountering problems when making network connections. Don't worry, you're not alone. Our samples also encounter the same problems when built using iOS 9 SDK.

 

If you're wondering what's wrong, here's the deal. Apple started enforcing more stringent policies regarding network connections for apps built with iOS 9. These policies block plain HTTP connections and require that you exclusively use secure HTTPS connections that support forward secrecy. Details can be found in Apple's Technote

 

While these changes have a noble goal - to make your app more secure - they do pose problems when third-party services you rely upon don't meet all of Apple's guidelines. On occasion, you may deliberately want to use plain HTTP connections to avoid unnecessary overhead for information that isn't sensitive and doesn't need to be protected. Fortunately, Apple provides a way to ease these policy restrictions in your app.

 

We've updated our samples to relax these restrcitions by adding the following declaration to the info.plist file -

 

    <key>NSAppTransportSecurity</key>
    <dict>
        <key>NSExceptionDomains</key>
        <dict>
            <key>arcgisonline.com</key>
            <dict>
                <key>NSIncludesSubdomains</key>
                <true/>
                <key>NSThirdPartyExceptionAllowsInsecureHTTPLoads</key>
                <true/>
                <key>NSThirdPartyExceptionRequiresForwardSecrecy</key>
                <false/>
            </dict>
            <key>arcgis.com</key>
            <dict>
                <key>NSIncludesSubdomains</key>
                <true/>
                <key>NSThirdPartyExceptionAllowsInsecureHTTPLoads</key>
                <true/>
                <key>NSThirdPartyExceptionRequiresForwardSecrecy</key>
                <false/>
            </dict>
        </dict>
    </dict>

 

This change allows the samples to make HTTPS connections without requiring forward secrecy. It also permits plain HTTP connections to ArcGIS Online so that the samples can quickly load basemap layers and use sample services without needing the data to be encrypted. ArcGIS Online already supports HTTPS connections using industry leading TLS 1.2 so you can choose to use HTTPS exclusively in your app if you so desire, and we'll soon be adding support for forward secrecy ciphers so that your apps can connect to ArcGIS Online without needing to change your app's transport security policies.