Select to view content in your preferred language

generateGeodatabaseWithParameters detecting network error in FetchingResult status

2547
5
10-14-2015 06:00 PM
JoshuaHudson
Emerging Contributor

Ok, I have the following code in generateGeodatabaseWithParameters it fires fine and calculates the download speed. The problem I'm having is if a network disruption happens in the AGSResumableTaskJobStatus.FetchingResult status state, I no longer get  status updates, and the call never timeouts. So to the user it just looks like their download has frozen forever. I have a cancel button and I can cancel it but I would rather detect the error and let the user knows that the generate process has failed during that step.

if status == AGSResumableTaskJobStatus.FetchingResult {

                               

                                if let userinfo = userInfo  {

                                   

                                    totalBytesDownloaded = userinfo["AGSDownloadProgressTotalBytesDownloaded"] as? NSNumber

                                    totalBytesExpected = userinfo["AGSDownloadProgressTotalBytesExpected"] as? NSNumber

                                   

                                    if totalBytesDownloaded != nil && totalBytesExpected != nil {

                                       

                                        elapsedTime = CACurrentMediaTime() - weakSelf.startTimeMapDl

                                        elapsedSeconds = UInt(elapsedTime)

                                       

                                        if elapsedSeconds > 0 && elapsedSeconds != previousSeconds {

                                           

                                            var bytesPerSecond = UInt(0)

                                           

                                            if previousSeconds == 0 {

                                                dispatch_async(dispatch_get_main_queue()) {

                                                    weakSelf.currentDownloadSpeedHeaderLabel.text = "Current D/L Speed:"

                                                }

                                                bytesPerSecond = totalBytesDownloaded!.unsignedLongValue / elapsedSeconds

                                            }

                                            else {

                                                bytesPerSecond = (totalBytesDownloaded!.unsignedLongValue - previousBytesDownloaded.unsignedLongValue ) / (elapsedSeconds - previousSeconds )

                                            }

                                           

                                            //dl speed bounced between 0 and some other number if checked every second, so spreading it out over 3 seconds

                                            //for a better UI experience.  It still bounces around, but not as much.

                                           

                                            let secondsDifference = elapsedSeconds - previousSeconds

                                           

                                            if secondsDifference > 1 && previousBytesDownloaded.unsignedLongValue != totalBytesDownloaded!.unsignedLongValue {

                                             

                                                dispatch_async(dispatch_get_main_queue()) {

                                                    weakSelf.currentDownloadSpeedLabel.text = "\(NSByteCountFormatter.stringFromByteCount(Int64(bytesPerSecond), countStyle:NSByteCountFormatterCountStyle.File))"

                                                }

                                               

                                                previousBytesDownloaded = totalBytesDownloaded!

                                                previousSeconds = elapsedSeconds

                                               

                                            }

                                        }

                                    }

                                }

                            }

For other status states if there is an error I get a error message in the user info, but that does not populate or fire in the fetching result stage.

This at the top of the method call detects network errors for all states, but since FetchingResults never calls back, it can't run.

weakSelf.agsResumableTaskJob = weakSelf.gdbSyncTask?.generateGeodatabaseWithParameters(params,

                        downloadFolderPath: nil,

                        useExisting: false,

                        status: { status, userInfo in

                           

                            if let userinfo = userInfo {

                               

                                if let error = userinfo["statusRequestError"] {

                                   

                                    print("Error: Could not generate geodatabase. Error details:\(error)")

                                    errorOccured = true

                                    weakSelf.agsResumableTaskJob?.cancel()

                                   

                                    if error.localizedDescription == "The Internet connection appears to be offline." {

                                       

                                        dispatch_async(dispatch_get_main_queue()) {

                                           

                                            let alert = UIAlertController(title: "Network Error",

                                                message: "Could not download the map inventory.\r\n\r\nPlease verify you are connected to the Network and try again.",

                                                preferredStyle: UIAlertControllerStyle.Alert)

                                           

                                            alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: nil))

                                           

                                            weakSelf.presentViewController(alert, animated: true, completion: nil)

                                        }

                                    }

                                    else if error.localizedDescription == "A server with the specified hostname could not be found." {

                                       

                                        dispatch_async(dispatch_get_main_queue()) {

                                           

                                            let alert = UIAlertController(title: "Network Error",

                                                message: "Could not download the map inventory.\r\n\r\nPlease verify you are connected to the Network and try again.",

                                                preferredStyle: UIAlertControllerStyle.Alert)

                                           

                                            alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: nil))

                                           

                                            weakSelf.presentViewController(alert, animated: true, completion: nil)

                                        }

                                    }

                                    else {

                                        dispatch_async(dispatch_get_main_queue()) {

                                           

                                            let alert = UIAlertController(title: "Error",

                                                message: "Could not download the map inventory.\r\n\r\nPlease verify you are connected to the Network and try again.",

                                                preferredStyle: UIAlertControllerStyle.Alert)

                                           

                                            alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: nil))

                                           

                                            weakSelf.presentViewController(alert, animated: true, completion: nil)

                                        }

                                    }

                                   

                                }

                            }

Using the latest sdk build (10.2.5)

0 Kudos
5 Replies
DiveshGoyal
Esri Regular Contributor

Thanks for reporting this, Joshua

I can confirm that the status or completion blocks are not invoked when the download gets interrupted. We will look into this and evaluate what is the best behavior to implement going forward. Thinking about it some more, we don't want to prematurely fail the operation because even a slight hiccup in the network while downloading the result would fail the whole job and require the user to initiate the whole process again. As it stands today, eventhough the download appears to hang indefinitely, it does automatically restart when the network is restored. So on the bright side, it is resilient to transient network conditions. Ideally, we would have some internal timeout beyond which we would notify you and let you make the appropriate decision for your app.

In the meantime, if you want to provide a better UX where the user can put the download on ice when it appears to have hung, you could provide them an option saying "Try downloading later" which could be implemented using the pause/resume methods on AGSResumableTaskJob. It might be a little tricky to know when this option should be presented - but you could try and detect if the status block stops firing for X amount of time in the middle of fetching result, or you could always have a "Cancel" and "Try later" methods along with your download indicator incase the download runs into problems (even though canceling the whole job seems like a harsh thing to do, what you really want is for the user to try again later)

0 Kudos
JoshuaHudson
Emerging Contributor

Thanks for the reply Divesh! I've noticed that most status resume once network picks up or it switches from wifi to cellular for example.

I am not seeing the behavior you describe during the fetch result stage. During the fetch result stage if I kill the network access and reenable I don't get status updates again.

Here is an example. As you can see in our enterprise app here, the download is going on. I then reach up and kill wifi (my only connection) to simulate a lost of network activity. At 3:30 I went up and reenabled the connection and as you can see the status never fired again because the progress just froze.

I already thought about detecting the no transfer for x seconds trick, and that will work if there is not another solution. Our users for this app are not technical GIS users, so we have to code in a lot of error handling and detection so they even know an issue is going on.

0 Kudos
DiveshGoyal
Esri Regular Contributor

Hmmm, can you try this scenario with the DownloadTileCache sample ? I've seen that resume the download when network is restored.

0 Kudos
JoshuaHudson
Emerging Contributor

That one did resume but paused a long wile after network was re-enabled but did eventually continue on.

The difference I saw when looking at the code is that it is a different task method:

    [self.tileCacheTask exportTileCacheWithParameters:params downloadFolderPath:nil useExisting:YES status:^(AGSResumableTaskJobStatus status, NSDictionary *userInfo) {

vs generateGeodatbaseWithParameters

Let met try again with my code but time leave it up for a couple minutes.

0 Kudos
JoshuaHudson
Emerging Contributor

Confirmed. I gave it several minutes and the status messages never came back.

Here is the whole method call and blocks:

let params = AGSGDBGenerateParameters(featureServiceInfo: weakSelf.gdbSyncTask.featureServiceInfo)

                

                    var totalBytesDownloaded : NSNumber?

                    var totalBytesExpected : NSNumber?

                    var errorOccured = false

                

                    weakSelf.agsResumableTaskJob = weakSelf.gdbSyncTask?.generateGeodatabaseWithParameters(params,

                        downloadFolderPath: nil,

                        useExisting: false,

                        status: { status, userInfo in

                        

                        

                            #if DEBUG

                                print("GDB Download Status: \(AGSResumableTaskJobStatusAsString(status))")

                            #endif

                        

                            if let userinfo = userInfo {

                            

                                if let error = userinfo["statusRequestError"] {

                                

                                    print("Error: Could not generate geodatabase. Error details:\(error)")

                                    errorOccured = true

                                    weakSelf.agsResumableTaskJob?.cancel()

                                

                                    if error.localizedDescription == "The Internet connection appears to be offline." {

                                    

                                        dispatch_async(dispatch_get_main_queue()) {

                                        

                                            let alert = UIAlertController(title: "Network Error",

                                                message: "Could not download the map inventory.\r\n\r\nPlease verify you are connected to the WSDOT Network and try again.",

                                                preferredStyle: UIAlertControllerStyle.Alert)

                                        

                                            alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: nil))

                                        

                                            weakSelf.presentViewController(alert, animated: true, completion: nil)

                                        }

                                    }

                                    else if error.localizedDescription == "A server with the specified hostname could not be found." {

                                    

                                        dispatch_async(dispatch_get_main_queue()) {

                                        

                                            let alert = UIAlertController(title: "Network Error",

                                                message: "Could not download the map inventory.\r\n\r\nPlease verify you are connected to the WSDOT Network and try again.",

                                                preferredStyle: UIAlertControllerStyle.Alert)

                                        

                                            alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: nil))

                                        

                                            weakSelf.presentViewController(alert, animated: true, completion: nil)

                                        }

                                    }

                                    else {

                                        dispatch_async(dispatch_get_main_queue()) {

                                        

                                            let alert = UIAlertController(title: "Error",

                                                message: "Could not download the map inventory.\r\n\r\nPlease verify you are connected to the WSDOT Network and try again.",

                                                preferredStyle: UIAlertControllerStyle.Alert)

                                        

                                            alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: nil))

                                        

                                            weakSelf.presentViewController(alert, animated: true, completion: nil)

                                        }

                                    }

                                

                                }

                            }

                        

                            if status == AGSResumableTaskJobStatus.Cancelled {

                                dispatch_async(dispatch_get_main_queue()) {

                                    weakSelf.stopTimer()

                                    weakSelf.downloadButton.enabled = true

                                    weakSelf.downloadButton.hidden = false

                                    weakSelf.cancelDownloadButton.enabled = false

                                    weakSelf.cancelDownloadButton.hidden = true

                                    weakSelf.progressBar.progress = 0

                                    weakSelf.timeElapsedLabel.text = "00:00:00";

                                    weakSelf.progressFileSizeLabel.text = "0 MB of 0 MB"

                                    weakSelf.progressPercentLabel.text = "0 %"

                                    weakSelf.currentDownloadSpeedLabel.text = "0 MB"

                                    weakSelf.currentDownloadSpeedHeaderLabel.text = "Current D/L Speed:"

                                }

                            }

                            else if status == AGSResumableTaskJobStatus.FetchingResult && errorOccured == false{

                            

                                if let userinfo = userInfo  {

                                

                                    totalBytesDownloaded = userinfo["AGSDownloadProgressTotalBytesDownloaded"] as? NSNumber

                                    totalBytesExpected = userinfo["AGSDownloadProgressTotalBytesExpected"] as? NSNumber

                                

                                    if(weakSelf.startTimeMapDl == 0)

                                    {

                                        weakSelf.startTimeMapDl = NSDate().timeIntervalSinceReferenceDate

                                    

                                        dispatch_async(dispatch_get_main_queue()) {

                                            weakSelf.currentDownloadSpeedHeaderLabel.text = "Current D/L Speed:"

                                        }

                                    }

                                

                                    if totalBytesDownloaded != nil && totalBytesExpected != nil {

                                    

                                        let percentage = totalBytesDownloaded!.doubleValue/totalBytesExpected!.doubleValue

                                    

                                        dispatch_async(dispatch_get_main_queue()) {

                                            weakSelf.progressBar.progress = Float((percentage/2) + 0.5)

                                        

                                            weakSelf.progressFileSizeLabel.text = "\(NSByteCountFormatter.stringFromByteCount(totalBytesDownloaded!.longLongValue, countStyle:NSByteCountFormatterCountStyle.File)) of \(NSByteCountFormatter.stringFromByteCount(totalBytesExpected!.longLongValue, countStyle:NSByteCountFormatterCountStyle.File))"

                                        }

                                    

                                        let speed = totalBytesDownloaded!.doubleValue / Double((NSDate().timeIntervalSinceReferenceDate - weakSelf.startTimeMapDl))

                                    

                                        if(speed != 0) {

                                            weakSelf.totalSpeedInBytes += speed

                                            weakSelf.numberOfSpeedReadings++

                                        }

                                    

                                        dispatch_async(dispatch_get_main_queue()) {

                                            weakSelf.currentDownloadSpeedLabel.text = "\(NSByteCountFormatter.stringFromByteCount(Int64(speed), countStyle:NSByteCountFormatterCountStyle.File))"

                                        }

                                    }

                                }

                            }

                            else if errorOccured == false {

                            

                                if status == AGSResumableTaskJobStatus.PreProcessingJob || status == AGSResumableTaskJobStatus.WaitingForDefaultParameters {

                                

                                    dispatch_async(dispatch_get_main_queue()) {

                                        weakSelf.progressFileSizeLabel.text = "Pre-Processing.."

                                    }

                                }

                            

                                if status == AGSResumableTaskJobStatus.StartingJob {

                                

                                    dispatch_async(dispatch_get_main_queue()) {

                                        weakSelf.progressFileSizeLabel.text = "Starting.."

                                    }

                                }

                            

                                if status == AGSResumableTaskJobStatus.Done {

                                

                                    dispatch_async(dispatch_get_main_queue()) {

                                        if weakSelf.numberOfSpeedReadings != 0 {

                                        

                                            let averageSpeedInBytes = weakSelf.totalSpeedInBytes / weakSelf.numberOfSpeedReadings

                                        

                                            if averageSpeedInBytes != 0 {

                                            

                                                weakSelf.currentDownloadSpeedHeaderLabel.text = "Average D/L Speed:"

                                            

                                                weakSelf.currentDownloadSpeedLabel.text = "\(NSByteCountFormatter.stringFromByteCount(Int64(averageSpeedInBytes), countStyle:NSByteCountFormatterCountStyle.File))"

                                            }

                                            else {

                                                weakSelf.currentDownloadSpeedHeaderLabel.text = "Average D/L Speed:"

                                                weakSelf.currentDownloadSpeedLabel.text = "N/A"

                                            }

                                        }

                                        else {

                                            weakSelf.currentDownloadSpeedHeaderLabel.text = "Average D/L Speed:"

                                            weakSelf.currentDownloadSpeedLabel.text = "N/A"

                                        }

                                    }

                                }

                            

                                if status == AGSResumableTaskJobStatus.Polling {

                                    dispatch_async(dispatch_get_main_queue()) {

                                        weakSelf.progressFileSizeLabel.text = "Acquiring map metadata. This can take several minutes."

                                    }

                                }

                        

                            

                                dispatch_async(dispatch_get_main_queue()) {

                                    if weakSelf.progressBar.progress < 0.5 {

                                        weakSelf.progressBar.progress += 0.01

                                    }

                                }

                            }

                        

                            dispatch_async(dispatch_get_main_queue()) {

                                weakSelf.progressPercentLabel.text = "\(Int(weakSelf.progressBar.progress * 100)) %"

                            }

                        },

                    

                        completion: { geodatabase, error in

                            if let error = error {

                                print("Error: Could not download the geodatabase from feature service.  Error details: \(error)")

                            

                            

                                    dispatch_async(dispatch_get_main_queue()) {

                                    

                                        weakSelf.stopTimer()

                                        weakSelf.downloadButton.enabled = true

                                        weakSelf.downloadButton.hidden = false

                                        weakSelf.cancelDownloadButton.enabled = false

                                        weakSelf.cancelDownloadButton.hidden = true

                                        weakSelf.progressBar.progress = 0

                                        weakSelf.timeElapsedLabel.text = "00:00:00";

                                        weakSelf.progressFileSizeLabel.text = "0 MB of 0 MB"

                                        weakSelf.progressPercentLabel.text = "0 %"

                                        weakSelf.currentDownloadSpeedLabel.text = "0 MB"

                                        weakSelf.currentDownloadSpeedHeaderLabel.text = "Current D/L Speed:"

                                    

                                        let alert = UIAlertController(title: "Error",

                                            message: "Could not download the map inventory.\r\n\r\nPlease verify you are connected to the WSDOT Network and try again.",

                                            preferredStyle: UIAlertControllerStyle.Alert)

                                    

                                        alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: nil))

                                    

                                        weakSelf.presentViewController(alert, animated: true, completion: nil)

                                    }

                            

                                return

                            }

                            else {

                            

                                #if DEBUG

                                    print("GDB download completed successfully, path is \(geodatabase.path)")

                                #endif

                            

                                let featuresPath = Utility.getDocumentsDirectoryPath() + "/HATS-features.geodatabase"

                            

                                var currentGeodatabaseDeletedOrDoesentExist = false

                                var currentGeodatabaseShmDeletedOrDoesentExist = false

                                var currentGeodatabaseWalDeletedOrDoesentExist = false

                            

                                if( NSFileManager.defaultManager().isReadableFileAtPath(featuresPath)) {

                                    do {

                                        try NSFileManager.defaultManager().removeItemAtPath(featuresPath)

                                    

                                        currentGeodatabaseDeletedOrDoesentExist = true

                                    }

                                    catch {

                                        print("Could not delete the current geodatabase file.")

                                    }

                                }

                                else

                                {

                                    currentGeodatabaseDeletedOrDoesentExist = true

                                }

                            

                                if( NSFileManager.defaultManager().isReadableFileAtPath(featuresPath + "-shm")) {

                                    do {

                                        try NSFileManager.defaultManager().removeItemAtPath(featuresPath + "-shm")

                                    

                                        currentGeodatabaseShmDeletedOrDoesentExist = true

                                    }

                                    catch {

                                        print("Could not delete the current geodatabase shm file.")

                                    }

                                }

                                else

                                {

                                    currentGeodatabaseShmDeletedOrDoesentExist = true

                                }

                            

                                if( NSFileManager.defaultManager().isReadableFileAtPath(featuresPath + "-wal")) {

                                    do {

                                        try NSFileManager.defaultManager().removeItemAtPath(featuresPath + "-wal")

                                    

                                        currentGeodatabaseWalDeletedOrDoesentExist = true

                                    }

                                    catch {

                                        print("Could not delete the current geodatabase wal file.")

                                    }

                                }

                                else

                                {

                                    currentGeodatabaseWalDeletedOrDoesentExist = true

                                }

                            

                                if currentGeodatabaseDeletedOrDoesentExist {

                                

                                    if( NSFileManager.defaultManager().isReadableFileAtPath(geodatabase.path)) {

                                        do {

                                            try NSFileManager.defaultManager().moveItemAtPath(geodatabase.path, toPath: featuresPath)

                                        }

                                        catch {

                                            print("Could not  move the downloaded geodatabase to its final path")

                                        }

                                    }

                                

                                }

                            

                                if currentGeodatabaseShmDeletedOrDoesentExist {

                                

                                    if( NSFileManager.defaultManager().isReadableFileAtPath(geodatabase.path + "-shm")) {

                                        do {

                                            try NSFileManager.defaultManager().moveItemAtPath(geodatabase.path + "-shm", toPath: featuresPath + "-shm")

                                        }

                                        catch {

                                            print("Could not  move the downloaded geodatabase shm to its final path")

                                        }

                                    }

                                

                                }

                            

                                if currentGeodatabaseWalDeletedOrDoesentExist {

                                

                                    if( NSFileManager.defaultManager().isReadableFileAtPath(geodatabase.path + "-wal")) {

                                        do {

                                            try NSFileManager.defaultManager().moveItemAtPath(geodatabase.path + "-wal", toPath: featuresPath + "-wal")

                                        }

                                        catch {

                                            print("Could not  move the downloaded geodatabase wal to its final path")

                                        }

                                    }

                                }

                            

                                Utility.setPathToNotBackupByiCloud(featuresPath)

                                Utility.setPathToNotBackupByiCloud(featuresPath + "-shm")

                                Utility.setPathToNotBackupByiCloud(featuresPath + "-wal")

                            

                                dispatch_async(dispatch_get_main_queue()) {

                                    weakSelf.cancelDownloadButton.enabled = false

                                    weakSelf.cancelDownloadButton.hidden = true

                                }

                            

                                weakSelf.registerHatsMapFeaturesGdb()

                            }

                        

                        }

                    )

0 Kudos