1

So, I am trying to do a Alamofire request, then, I'd take the information I need from the JSON data and put it into a global variable, here's my code.

struct myVariables {
static var variableOne = ""
}


func function() {
Alamofire.request(.GET, "API URL").responseJSON { response in
    if let rawJSON = response.result.value {
        // Here I just take the JSON and put it into dictionaries and parse the data.
           myVariables.variableOne = String("data")
        }
    }
}

Ok, so basically, I am trying to access variableOne's data from another Swift file. Let's say I made two Swift files and in one of those files I had a function that edited the value of global variable, in the other file, if I attempted to print that global variable, I'd see the edited value. But whenever I use Alamofire, when I try to edit a global variable, the other Swift file doesn't see the changed value. So if I tried to edit the global variable within the Alamofire request block of code, I don't see the change whenever I print the variable from another file.

If anyone knows a better way to phrase that, please do correct it.

Danny M
  • 27
  • 1
  • 11

1 Answers1

1

I suspect the problem isn't that you're not seeing the value change, but rather an issue arising from the fact that you're dealing with an asynchronous method. For example, when you call function, it returns immediately, but your variableOne may not be updated immediately, but rather later. I bet you're checking it before this asynchronous response closure had a chance to be called.

You wouldn't have this problem if, rather than using this "global" (which is a bad idea, anyway), you instead adopted the completion handler pattern yourself.

func performRequest(completionHandler: @escaping (String?, Error?) -> Void) {
    Alamofire.request("API URL").responseJSON { response in
        switch response.result {
        case .failure(let error):
            completionHandler(nil, error)
        case .success(let responseObject):
            let dictionary = responseObject as? [String: Any]
            let string = dictionary?["someKey"] as? String
            completionHandler(string, nil)
        }
    }
}

An you'd call this like so:

performRequest() { string, error in
    guard let string = string, error == nil else {
        // handle error here
        return
    }

    // use `string` here
}

// but not here, because the above closure runs asynchronously (i.e. later)

By using this completion handler pattern, we solve the "how do I know when the asynchronous method is done" problem. And by passing the necessary data back as a parameter of the closure, we can excise the use of globals, keeping the scope of our data as narrow as possible.

Clearly, you should change the parameter of the closure to match whatever is appropriate in your case. But hopefully this illustrates the basic idea.

See previous revision of this answer for Swift 2/Alamofire 3 answer.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • Thanks! Worked perfectly. – Danny M Jul 05 '16 at 17:32
  • how does this work when we have 10 asynchronous requests and we would like to know after all of them finish? – py_ios_dev Jun 08 '18 at 18:04
  • 1
    @py_ios_dev - The standard pattern is a dispatch group. So caller would (a) create group, `let group = DispatchGroup()`; (b) it would define what to do when the group finishes with `group.notify(queue: .main) { ... }`; (c) it would do `group.enter()` before it called the above `function`, and (d) it would call `group.leave()` in the completion handler it passes to `function`. Then, when the requests are done, the `notify` closure will be called. – Rob Jun 09 '18 at 15:31
  • 1
    @py_ios_dev - See https://stackoverflow.com/a/34983658/1271826 for a random example. That's with downloads, but the idea is the same with `request`... – Rob Jun 09 '18 at 15:52