Tasks and jobs

Tasks are bound to online or offline data or services and provide methods to perform asynchronous operations using those resources.

With tasks you can:

  • Download, collect, and update geographic information using GeodatabaseSyncTask
  • Download and display tiled basemaps using ExportTileCacheTask
  • Locate addresses on the map and interpolate addresses from map locations using LocatorTask
  • Calculate point-to-point or multi-stop routes and get driving directions using RouteTask
  • Perform complex GIS analysis by executing geoprocessing models using GeoprocessingTask

Tasks either return their results directly from asynchronous methods on the task, or make use of jobs to provide status updates and results.

Tasks

Some operations return results directly from asynchronous methods on the task. For more complex or longer running operations, tasks make use of jobs instead.

To use tasks that return results directly:

  1. Create the task by initializing it to use the required data or service.
  2. Define the task inputs.
    • Some operations require only simple value inputs (for example a simple geocode operation may only require an address string as input).
    • Others require input parameters (for example, to limit a geocode operation to a specific country).
  3. Call the async operation method, passing in the inputs you defined.
  4. Use the results from the operation as required, for example to display geocode results on a map.

You can create a LocatorTask using the Esri geocode service, and pass in an address to geocode. When the operation is complete, display the resulting location in a GraphicsOverlay.

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
        // Creates a new LocatorTask from a geocode service endpoint.
        let locatorTask = LocatorTask(url: URL(string: "https://geocode-api.arcgis.com/arcgis/rest/services/World/GeocodeServer")!)
        // Calls the async geocode method on the locator task, passing in an address, and returning geocode results.
        let geocodeResults = try await locatorTask.geocode(forSearchText: "380 New York Street, Redlands, CA")
        guard let firstResult = geocodeResults.first else { return }
        // Displays the first location in the geocode results.
        let graphic = Graphic(geometry: firstResult.displayLocation, attributes: firstResult.attributes, symbol: symbol)
        graphicsOverlay.addGraphic(graphic)

Define input parameters

Tasks offer numerous options that allow you to tailor the operation to your requirements. For example, when geocoding you can restrict your search to a specific area, country, category of place, and/or number of results. When an author publishes a service or packages a resource, they can choose default values for these options that suit the specific data or the most common use case for the service.

To use these default parameter values, tasks provide helper methods that create parameter objects initialized with service-specific values. You can then make any changes to the parameter values before passing them to an operation. Creating these default parameter objects is useful for operations with many options, such as tracing a utility network.

You could get the default parameters for a RouteTask and ensure that results using these parameters will return both a route and directions. Also the output spatial reference could match that of the map view.

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
        // Creates the route task parameter object initialized with service-specific values.
        let routeParameters = try await routeTask.makeDefaultParameters()
        // Explicitly sets values for some parameters.
        routeParameters.returnsDirections = true
        routeParameters.returnsRoutes = true
        routeParameters.outputSpatialReference = map.spatialReference
        // Solves the route with these parameters.
        let routeResult = try await routeTask.solveRoute(using: routeParameters)
        // Work with the results...

Some parameters objects have constructors that you can use if you know the values of all the input parameters you want to use. This can be more efficient when parameter settings are simple.

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
        // Constructs an empty parameter object and sets a few parameters.
        let geocodeParams = GeocodeParameters()
        geocodeParams.countryCode = "France"
        geocodeParams.maxResults = 5

Work online or offline

Many tasks can work either online by using services, or offline by using local data and resources. For example, you can geocode an address by using the default Esri geocoding service, your own geocoding service, a locator file (.loz), or a mobile map package (.mmpk).

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
        // Creates a locator task from a geocode service endpoint.
        let locatorTask = LocatorTask(url: URL(string: "https://geocode-api.arcgis.com/arcgis/rest/services/World/GeocodeServer")!)
        // or creates a locator task from a local .loz file.
        let offlineLocatorTask = LocatorTask(name: "locatorfilename.loz", bundle: nil)
        // or creates a locator task from a mobile map package (if it contains one).
        try await mobileMapPackage.load()
        let mmpkLocatorTask = mobileMapPackage.locatorTask

Tasks and jobs

Some tasks expose operations that have multiple stages (like preparing and downloading a geodatabase), and can generate multiple progress messages (such as percentage complete). These types of tasks are always bound to ArcGIS Server (or Local Server for platforms that support it). An example is GeodatabaseSyncTask.makeGenerateGeodatabaseJob(parameters:downloadFileURL:).

Instead of returning results directly, these tasks make use of jobs to monitor status, return progress updates, and return their results. Each Job represents a specific operation of a task. Jobs are useful for longer-running operations, because they can also be paused, resumed, and canceled. Your app can support a user action or host OS terminating a running job object, and then recreate and resume the job later.

To use operations like these:

  1. Create the task by initializing it to use the required data or service.
  2. Define the input parameters for the task.
  3. Call the async operation method to get a job, passing in the input parameters you defined.
  4. Start the job.
  5. Optionally, listen for changes to the job status and check the job messages, for example to update a UI and report progress to the user.
  6. Listen for the job completion and get the results from the operation. Check for errors in the job, and if successful, use the results.
Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
        // Creates a geodatabase sync task with the URL of the feature service to sync.
        let geodatabaseSyncTask = GeodatabaseSyncTask(url: URL(string: "https://sampleserver6.arcgisonline.com/arcgis/rest/services/Sync/WildfireSync/FeatureServer")!)
        // Creates default sync geodatabase parameters.
        let syncGeodatabaseParameters = SyncGeodatabaseParameters()
        syncGeodatabaseParameters.shouldRollbackOnFailure = true
        syncGeodatabaseParameters.geodatabaseSyncDirection = .bidirectional
        // Instantiates a job to synchronize any changes between the local geodatabase with the task's feature service using the synchronization parameters.
        let syncJob = geodatabaseSyncTask.makeSyncGeodatabaseJob(parameters: syncGeodatabaseParameters, geodatabase: localGeodatabase)
        // Starts the sync job.
        syncJob.start()
        switch syncJob.status {
        case .succeeded:
            print("Job succeeded")
        case .failed:
            print("Job failed")
        default:
            print("Sync in progress")
        }
        let output = try await syncJob.output
        // Examine the results.

Calling Job.status retrieves the current Job.Status in the job's workflow. Jobs periodically fire a changed event as they are running, usually with decreasingly frequency as a job progresses. More than one JobMessage may appear in a change event. The job complete listener is called as soon as the job finishes. Whether successful or not, jobs cannot be restarted.

Report job progress

A job represents an asynchronously running operation that might take some time to finish. As described previously, you can monitor changes to job status for notification when a job has completed, failed, or been canceled, but what about the time in-between? Users may become frustrated waiting for a long job to complete without getting feedback on its progress. Fortunately, jobs provide a mechanism for reporting the current progress (percentage complete) for the running operation they represent.

As the job runs it provides a Job.progress property. This allows your app to provide more specific information about the status of a running job using UI elements like progress bars, for example.

Add a Published property wrapper to the offlineMapJob property. Once started, offlineMapJob provides regular announcements that report its progress. The Published property wrapper allows you to create an observable object that automatically announces when the progress changes. Learn more about the Published property wrapper.

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
    // A job to generate an offline map from an online map.
    @Published private(set) var offlineMapJob: GenerateOfflineMapJob!

    func generatesOfflineMap(extent: Envelope) async throws {
        // Waits for the online map to load.
        try await map.load()
        // Creates the offline map task with the online map.
        offlineMapTask = OfflineMapTask(onlineMap: map)
        // Creates parameters using an extent and scale values.
        let offlineMapParameters = GenerateOfflineMapParameters(areaOfInterest: extent, minScale: 100000, maxScale: 1000)
        // Creates a download directory to store the offline map.
        let downloadURL = try! FileManager.default.url(
            for: .itemReplacementDirectory,
            in: .userDomainMask,
            appropriateFor: Bundle.main.bundleURL,
            create: true
        )
        // Generates the offline map job using the parameters and download directory.
        offlineMapJob = offlineMapTask?.makeGenerateOfflineMapJob(parameters: offlineMapParameters, downloadDirectory: downloadURL)
        // Starts the job and waits for its output.
        offlineMapJob?.start()
        guard let output = try await offlineMapJob?.output else { return }
        // Sets the map to the offline map.
        map = output.offlineMap
    }

Add a ProgressView to the MapView's toolbar and pass in the job's progress property. The progress bar automatically reports the Published job's progress as it changes.

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
    // Model is an observable object that stores the map, offline map task, and offline map job.
    @StateObject private var model = Model()

    var body: some View {
        MapView(map: model.map)

            .toolbar {
                if let progress = model.offlineMapJob?.progress {
                    ProgressView(progress)
                        .progressViewStyle(.linear)
                }
            }
    }

Pause, resume, or cancel a job

Jobs are designed to handle a user exiting an app while the job is running or having the app terminated by the host operating system. Jobs also provide a mechanism for explicitly pausing or canceling the operation.

Cancel a job

Sometimes, the results of a job are no longer required. For example, a user could change their mind about the area of a tile cache they want to download and want to cancel the job and start over.

Calling Job.cancel() changes Job.Status to canceling, cancels the Job, and waits for any asynchronous, server-side operations to be canceled. After all cancelation tasks complete (including any server-side tasks), Job.Status changes to failed and Job.cancel() call back is fired . If one or more jobs cannot be canceled, Job.cancel() fires an error .

For example, GenerateOfflineMapJob is a server-side job that launches several more server-side jobs, depending on the layers in your map. Other examples of server-side jobs include ExportTileCacheJob, ExportVectorTilesJob, GenerateGeodatabaseJob, and GeoprocessingJob.

You should always cancel unneeded jobs (for example when exiting your app) to avoid placing unnecessary load on the server.

You could examine the Job error once a job has failed. The error code NSUserCancelledError would indicate that the job has been terminated by the user.

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
        offlineMapJob.start()
        if offlineMapJob.status == .canceling {
            print("User Cancelled")
        } else {
            // Process the result.
        }

Pause and resume a job

Jobs can be long-running operations, so there is no guarantee that they will be completed while the app is running. You can pause a job explicitly using the pause method through the NSProgress API (exposed via NSProgressReporting through the Job.progress property). For example, when an app is backgrounded and does not have permissions for background operation. Pausing may also be useful if a user wishes to temporarily stop network access for any reason.

Job changed messages will not be received for a paused job. Pausing a job does not stop any server-side processes from executing. While a job is paused, outstanding requests can complete. Therefore, when resuming a job it may have a different state than when it was paused.

You can serialize a job to JSON to persist it if your app is backgrounded or the process is otherwise terminated. When you deserialize it again the Job.Status will be in the paused state regardless of its state when serialized and should be restarted to resume listening for completion. The job changed listener is a good place to update the job JSON for storage by your app.

You could serialize a job to JSON.

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
        // Converts the offline map job to JSON.
        let jobJSON = offlineMapJob.toJSON()

You could deserialize a job from JSON and then restart the job.

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
        let offlineMapJob = try GenerateOfflineMapJob.fromJSON(jsonString)
        offlineMapJob.start()
        let status = offlineMapJob.status
        let output = try await offlineMapJob.output

Loss of network connection

Additionally, jobs using services are designed to handle situations where network connectivity is temporarily lost without needing to be immediately paused. A started job will ignore errors such as network errors for a period of up to 10 minutes. If errors continue for longer, the job will fail and the message will indicate the loss of network connection.

To handle inconsistent connectivity, you can serialize and pause a job when your app loses connectivity for a few minutes to avoid job failure (as failed jobs cannot be restarted). The job can then be deserialized and resumed when connectivity returns.

Your browser is no longer supported. Please upgrade your browser for the best experience. See our browser deprecation post for more details.