Search code examples
iosswiftclosuresalamofirecompletionhandler

How to know when asynchronous request has completed using a closure?


I am using Alamofire to download data and parse it with JSON. I know that in order to announce that data is available we should use closures and no the NotificationCenter. I do not understand closures though. How would I use a closure to reload a table view once the request has completed? Here is the code.

    func downloadEvents() {
    let coreDataObject = CoreDataMethods()
    Alamofire.request(URL(string:URL)!)
        .responseJSON { response in
            switch response.result {
            case .success:
                // we create the model object
                let downloadedEvent = EventModel()
                /* we have new data remove old data from core data*/
                coreDataObject.deleteData(entityArgument: "Event")
                events.removeAll() // remove the data from array this is no longer needed FIX
                for JSONData in response.result.value as! [Dictionary<String, Any>] {
                    /* get the data from JSON and store in the event model*/
                    downloadedEvent.eTitle = JSONData[downloadedEvent.titleString] as? String
                    downloadedEvent.eDate = JSONData[downloadedEvent.dateString] as? String
                    downloadedEvent.eDescription = JSONData[downloadedEvent.descriptionString] as? String
                    downloadedEvent.eLocation = JSONData[downloadedEvent.locationline1String] as? String
                    downloadedEvent.eLocation2 = JSONData[downloadedEvent.locationline2String] as? String
                    /* if the event has an image save the url*/
                    if let image = JSONData[downloadedEvent.imageString] as? String {
                        downloadedEvent.eImageURL = image
                    } else {
                        /* else we save the default image url */
                        downloadedEvent.eImageURL = downloadedEvent.defaultImageURL
                    }
                    coreDataObject.save(eventParam: downloadedEvent)
                }
                /* post notification to reload table view FIX */
                NotificationCenter.default.post(name: RELOAD_NOTIFICATION, object: nil)
            case .failure(let error):
                print("ALAMO REQUEST FIALED: \(error)")
            }
    }
}

Solution

  • Here is the downloadEvents function with the ability to notify the caller it was successful:

    func downloadEvents(completion: @escaping (Bool, String?)-> Void) {
        let coreDataObject = CoreDataMethods()
        Alamofire.request(URL(string:URL)!)
            .responseJSON { response in
    
                switch response.result {
                case .success:
                    // we create the model object
                    let downloadedEvent = EventModel()
                    /* we have new data remove old data from core data*/
                    coreDataObject.deleteData(entityArgument: "Event")
                    events.removeAll() // remove the data from array this is no longer needed FIX
                    for JSONData in response.result.value as! [Dictionary<String, Any>] {
                        /* get the data from JSON and store in the event model*/
                        downloadedEvent.eTitle = JSONData[downloadedEvent.titleString] as? String
                        downloadedEvent.eDate = JSONData[downloadedEvent.dateString] as? String
                        downloadedEvent.eDescription = JSONData[downloadedEvent.descriptionString] as? String
                        downloadedEvent.eLocation = JSONData[downloadedEvent.locationline1String] as? String
                        downloadedEvent.eLocation2 = JSONData[downloadedEvent.locationline2String] as? String
                        /* if the event has an image save the url*/
                        if let image = JSONData[downloadedEvent.imageString] as? String {
                            downloadedEvent.eImageURL = image
                        } else {
                            /* else we save the default image url */
                            downloadedEvent.eImageURL = downloadedEvent.defaultImageURL
                        }
                        coreDataObject.save(eventParam: downloadedEvent)
                    }
    
                    completion(true, nil)
    
                    /* post notification to reload table view FIX */
                    //NotificationCenter.default.post(name: RELOAD_NOTIFICATION, object: nil)
                case .failure(let error):
                    print("ALAMO REQUEST FIALED: \(error)")
                    completion(false, "ALAMO REQUEST FIALED: \(error)")
                }
        }
    }
    

    You would then call the function like this:

    func reloadTable(){
    
        downloadEvents { (success, errMsg) in
            if success{
                DispatchQueue.main.async {
                    self.tableView.reloadData()
                }
            }
            else{
                let alertMessage: String
                if let err = errMsg{
                    alertMessage = err
                }
                else{
                    alertMessage = "An unknown error occurred."
                }
                let alert = UIAlertController.init(title: "Request Failed", message: alertMessage, preferredStyle: .alert)
                alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.cancel, handler: nil))
                DispatchQueue.main.async {
                    self.present(alert, animated: true, completion: nil)
                }
            }
        }
    }