Search code examples
iosswiftuialertviewalamofire

Display alert from a non-UI class


I'm using Alamofire in my application and wish to display an alert if the request has an error (e.g. wrong URL), etc.

I have this function in a separate class as it is shared among the pages of the application.

    Alamofire.request(.GET, api_url)
        .authenticate(user: str_api_username, password: str_api_password)
        .validate(statusCode: 200..<300)
        .response { (request, response, data, error) in

            if (error != nil) {
                let alertController = UIAlertController(title: "Server Alert", message: "Could not connect to API!", preferredStyle: UIAlertControllerStyle.Alert)
                alertController.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.Default,handler: nil))
                self.presentViewController(alertController, animated: true, completion: nil)
            }
    }

As Alamofire works asynchronously I need to do the error check then & there (unless you suggest otherwise) because then I want to manipulate the results and if the URL was wrong then it can get messy.

No surprise, the

self.presentViewController(alertController, animated: true, completion: nil)

does not work so how can I display this alert?


Solution

  • I'd say the conventional approach for this is to have whoever calls this network request be responsible for displaying the alert. If when the request is complete, you call back to the original calling object, they are responsible for displaying the alert. One of the reasons for this is that errors can mean different things in different contexts. You may not always want to display an alert - this provides you with more flexibility as you're building out your app. The same way that AlamoFire calls your response closure when it is done, I think it's best to pass that back to whoever initiated this call in your Downloader object.

    Update: You want to structure it the same way AlamoFire structures it. You pass the closure to AF which gets called when the AF request finishes.

    You'll have to add a closure param to your download function (See downloadMyStuff). Then, once the AF request finishes, you can call the closure you previously defined ( completion). Here's a quick example

    class Downloader {
      func downloadMyStuff(completion: (AnyObject?, NSError?) -> Void) {
    
        Alamofire.request(.GET, "http://myapi.com")
            .authenticate(user: "johndoe", password: "password")
            .validate(statusCode: 200..<300)
            .response { (request, response, data, error) in
                completion(data, error)
    
          }
      }
    }
    
    class ViewController: UIViewController {
    
    let downloader = Downloader()
    
    override func viewDidLoad() {
        super.viewDidLoad()
    
        self.downloader.downloadMyStuff { (maybeResult, maybeError) -> Void in
            if let error = maybeError {
                println("Show your alert here from error \(error)")
            }
    
            if let result: AnyObject = maybeResult {
                println("Parse your result and do something cool")
            }
        }
    
      }
    }