fetchData(completion:) memory consumption

1086
2
Jump to solution
01-21-2017 11:57 AM
LinnartBaeker
New Contributor II
fetchData(completion:)

method on AGSPortalItem can lead to out of memory kills of the app. It appears that shortly before the completion handler is called, the app suddenly needs four times the size of the download in memory with no apparent reason. In my case its an 100 MB Download. This leads to the App getting killed under Memory pressure. 

On another note it looks like the complete download is stored in memory the whole time. This is probably not ideal even without the 4x memory bug.

portalItem.fetchData { [weak self](data, fetchError) -> Void in

                if let error = fetchError {

                    print("Could not fetch data due to error : \(error)")

                }else{

                    print("Downloaded portal item")

                    print("Title : \(portalItem.title)")

                    print("Type : \(portalItem.type.rawValue)")

                    print("ID : \(portalItem.itemID)")

                }

            }

Tags (1)
0 Kudos
1 Solution

Accepted Solutions
RyanOlson1
Esri Contributor

This is an issue that I can log and we can look into. 

The good news is that in the meantime you can try this workaround below. This kicks off a request manually, telling the request to save the data to disk (instead of memory) and also to use the portal's credentials.

You already have a portal and portal item, so you don't need those 2 lines. The rest should get you going.

    func fetchData(){
        portal = AGSPortal.arcGISOnline(withLoginRequired: true)
        let pi = AGSPortalItem(portal: self.portal!, itemID: "c580e6bbaedc4c6e82b9c50fbe6b46fd")
        
        // get url for fetching data endpoint
        if let fetchDataURL = portal?.url?.appendingPathComponent("sharing/rest/content/items/\(pi.itemID)/data"){
            
            // create an operation setting the remote resource as the portal (so that it will use the portal's credentials)
            let op = AGSRequestOperation(remoteResource: portal, url: fetchDataURL, queryParameters: nil)
            
            // set output file so that the data isn't stored in memory as it's downloaded
            op.outputFileURL = FileManager.default.temporaryDirectory.appendingPathComponent("myData.dat")
            
            // set completion handler
            op.registerListener(self){ result, error in
                if let error = error{
                    // download of data failed
                    print("error occurred: \(error.localizedDescription)")
                }
                else if let outputFile = result as? URL{
                    // download is complete and file is saved to disk
                    print("download complete: \(outputFile)")
                }
            }
            
            // add the operation to the shared queue so that it can start
            AGSOperationQueue.shared().addOperation(op)
        }
        
    }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

View solution in original post

2 Replies
RyanOlson1
Esri Contributor

This is an issue that I can log and we can look into. 

The good news is that in the meantime you can try this workaround below. This kicks off a request manually, telling the request to save the data to disk (instead of memory) and also to use the portal's credentials.

You already have a portal and portal item, so you don't need those 2 lines. The rest should get you going.

    func fetchData(){
        portal = AGSPortal.arcGISOnline(withLoginRequired: true)
        let pi = AGSPortalItem(portal: self.portal!, itemID: "c580e6bbaedc4c6e82b9c50fbe6b46fd")
        
        // get url for fetching data endpoint
        if let fetchDataURL = portal?.url?.appendingPathComponent("sharing/rest/content/items/\(pi.itemID)/data"){
            
            // create an operation setting the remote resource as the portal (so that it will use the portal's credentials)
            let op = AGSRequestOperation(remoteResource: portal, url: fetchDataURL, queryParameters: nil)
            
            // set output file so that the data isn't stored in memory as it's downloaded
            op.outputFileURL = FileManager.default.temporaryDirectory.appendingPathComponent("myData.dat")
            
            // set completion handler
            op.registerListener(self){ result, error in
                if let error = error{
                    // download of data failed
                    print("error occurred: \(error.localizedDescription)")
                }
                else if let outputFile = result as? URL{
                    // download is complete and file is saved to disk
                    print("download complete: \(outputFile)")
                }
            }
            
            // add the operation to the shared queue so that it can start
            AGSOperationQueue.shared().addOperation(op)
        }
        
    }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
LinnartBaeker
New Contributor II

I can confirm that this workaround does indeed work as expected. Thanks a lot you are a life saver.

0 Kudos