AnsweredAssumed Answered

Unable to take web map offline: "The feature service does not support geodatabase sync"

Question asked by corydavis343 on Jan 6, 2020
Latest reply on Jan 8, 2020 by corydavis343

I'm working on enabling offline sync in an iOS app using ArcGIS runtime 100.6. The back end is ArcGIS Enterprise 10.7.1.

 

I created a web map which only uses the World Topography Map (for Export) as a basemap and a single feature service created by ESRI to as a controlled test to guard against any mistakes I could have made while publishing the services which the production app will need to consume. These should present no problem being taken offline, but this error is generated after I initiate the download:

"The feature service does not support geodatabase sync"

I checked the web map settings on portal. It is shared with everyone and the "offline mode" switch is enabled, so I don't understand how I could be getting this error. To clarify, the basemap loads successfully, but the feature layer does not.

 

The code for initiating offline mode doesn't deviate much from the sample in this article (The function "downloadGeodatabaseForLayers() follows this article):

    func takeMapOffline(areaOfInterest: AGSGeometry) {
        //1. Declare offline map task
        //let offlineMapTask = AGSOfflineMapTask(onlineMap: self.mapView.map!)
        let offlineMap = AGSMap.init(url: URL(string: self.offlineWebMapUrl3)!)
        let offlineMapTask = AGSOfflineMapTask(onlineMap: offlineMap!)
       
        //2. Define parameters
        offlineMapTask.defaultGenerateOfflineMapParameters(withAreaOfInterest: areaOfInterest, completion: {(parameters, error) in
            print("@@@ Generate parameters 1: Start")
            if let error = error {
                print("@@@ Generate parameters 2b: Error, getting parameters failed")
                print(error)
                self.showDownloadFailedAlert("Error generating offline parameters: \(error.localizedDescription)")
                return
            }
            guard parameters != nil else {
                print("takeMapOffline: No parameters provided")
                return
            }
            if let parameters = parameters {
                parameters.maxScale = 3600
                parameters.minScale = 30000
                parameters.includeBasemap = true
                parameters.isDefinitionExpressionFilterEnabled = true
                parameters.continueOnErrors = true
                parameters.returnSchemaOnlyForEditableLayers = true
                parameters.attachmentSyncDirection = .bidirectional
                parameters.returnLayerAttachmentOption = .allLayers
                print("@@@ Generate parameters 2a: Got parameters!")
               
                //4. Create the job
                print("@@@ Generate parameters 3: create job")
                let mapStorageURL = self.getNewOfflineMapDirectoryURL()
                UserDefaults.standard.set(mapStorageURL, forKey: "OfflineMapDirectoryUrl")
                let AGSOfflineMapJob = offlineMapTask.generateOfflineMapJob(with: parameters, downloadDirectory: mapStorageURL)
               
                //5. Run the job
                self.startOfflineMapJob(job: AGSOfflineMapJob, areaOfInterest: areaOfInterest)
            }
        })
}


    func startOfflineMapJob(job: AGSGenerateOfflineMapJob, areaOfInterest: AGSGeometry) {
        job.start(statusHandler: { (status) in
            DispatchQueue.main.async {
                //Add canceling logic to SVProgressHUD
               
                SVProgressHUD.showDismissable(with: Float(job.progress.fractionCompleted), status: "Downloading map selection. This may take several minutes. Tap here to cancel.")
            }
            print("Status [\(String(describing: job.progress.fractionCompleted))]: \(status)")
            if job.status == .failed {
                DispatchQueue.main.async {
                    SVProgressHUD.dismiss()
                }
                self.showDownloadFailedAlert("Server Error")
                return
            }
        }) { (result, error) in
            DispatchQueue.main.async {
                SVProgressHUD.dismiss()
            }
            if let error = error {
                print(error)
                self.showDownloadFailedAlert(error.localizedDescription)
                return
            }
            guard let result = result else {
                self.showDownloadFailedAlert("Didn't receive a response.")
                return
               
            }
            if result.hasErrors {
                result.layerErrors.forEach{(layerError) in
                    print((layerError.key.name), " Error taking this layer offline: ", (layerError.value))
                }
                result.tableErrors.forEach{(tableError) in
                    print((tableError.key.tableName), " Error taking this table offline")
                }
                self.showDownloadFailedAlert("Error taking layers offline.")
            }
            else {
                //display the offline map
                self.mapView.map = result.offlineMap
               
                DispatchQueue.main.async {
                    self.downloadGeodatabaseForLayers(areaOfInterest: areaOfInterest)
                }
            }
        }
    }

    func downloadGeodatabaseForLayers(areaOfInterest: AGSGeometry) {
        print("@@@Downloading geodatabase")
        let syncTask = AGSGeodatabaseSyncTask(url: URL(string: testOfflineServiceUrl)!)
        syncTask.defaultGenerateGeodatabaseParameters(withExtent: areaOfInterest, completion: {[weak self] (parameters, error) in
            if let error = error {
                print("@@@Error generating geodatabase:" + error.localizedDescription)
                print(error)
                return
            }
            guard parameters != nil else {
                print("@@@No parameters")
                return
            }
            if let parameters = parameters {
                parameters.syncModel = .layer
                // define the layers and features to include
                parameters.layerOptions.removeAll()
                let layersToInclude = [0]
                for val in layersToInclude {
                parameters.layerOptions.append(AGSGenerateLayerOption(layerID: val, includeRelated: true))
                }
                parameters.returnAttachments = false
                //Start the download task
                let geodatabaseUrl = self?.getNewOfflineMapDirectoryURL().appendingPathExtension(".geodatabase")
               
                let generateJob = syncTask.generateJob(with: parameters, downloadFileURL: (geodatabaseUrl)!)
                generateJob.start(statusHandler: { (status) in
                   DispatchQueue.main.async {
                    SVProgressHUD.showProgress(Float(generateJob.progress.fractionCompleted), status: "Downloading Geodatabase")
                    }
                 }, completion: { (geodatabase, error) in
                    SVProgressHUD.dismiss()
                    if let error = error {
                        print("@@@Error downloading geodatabase: ")
                        print(error)
                        return
                    }
                    else if let geoDb = geodatabase {
                        self!.generatedGeodatabase = geoDb
                        self!.displayLayersFromGeodatabase(geoDb: geoDb)
                    }
                })
               
            }
           
        })
    }

 

If I download the app container, I can see both the mmpk file and the geodatabase file, but only the basemap displays. Can someone suggest a solution?

Outcomes