Reputation: 819
Let me preface this by saying I'm VERY new to Swift 2 and am building my first app which calls an api (php) for data (JSON). The problem I'm running into is when I make the call to the api the other functions ran before the api can send back the data.
I've researched some type of a onComplete to call a functions after the api response is done. I'm sure for most of you this is easy, but I cant seem to figure it our.
Thanks in advance!
class ViewController: UIViewController {
var Selects = [Selectors]()
var list = [AnyObject]()
var options = [String]()
var index = 0
@IBOutlet var Buttons: [UIButton]!
override func viewDidLoad() {
super.viewDidLoad()
self.API()
self.Render()
}
func API() {
let url = NSURL(string: "http:api.php")
let request = NSMutableURLRequest(URL: url!)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
if data == nil {
print("request failed \(error)")
return
}
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: .AllowFragments)
if let songs = json["songs"] as? [[String: AnyObject]] {
for song in songs {
self.list.append(song)
}
}
self.Selects = [Selectors(Name: self.list[self.index]["name"] as? String, Options: self.BuildOptions(), Correct: 2)]
}
catch let error as NSError {
print("json error: \(error.localizedDescription)")
}
}
task.resume()
}
func BuildOptions() {
// BuildOptions stuff happens here
}
func Render() {
// I do stuff here with the data
}
}
Upvotes: 0
Views: 1204
Reputation: 2032
So I assume your Render()
method is called before data gets back from the api? Keeping your api-calling code in the view controllers is bad design, but as you're new i won't expand on that. In your case it's as simple as not calling your Render()
method in viewDidLoad()
- call it after you're done with parsing the data from JSON (after the self.Selects = [Selectors...
line). NSURLSession.sharedSession().dataTaskWithRequest(request)
method is called asynchronously , and the callback block with data, response, error
parameters is executed after this method is done with fetching your data, so it can happen after the viewDidLoad
is long done and intially had no data to work on as the asynchronous method was still waiting for response from the API.
Edit - speaking of handling api calls, it's a wise thing to keep them separated from specific view controllers to maintain a clean reusable code base. You should call the API and wait for a callback from it, so i'd just do that to your API function, it would look like this:
static func callAPI(callback: [AnyObject]? -> Void ) {
let url = NSURL(string: "http:api.php")
let request = NSMutableURLRequest(URL: url!)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
if data == nil {
completion(nil)
}
do {
var list = [AnyObject]()
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: .AllowFragments)
if let songs = json["songs"] as? [[String: AnyObject]] {
for song in songs {
self.list.append(song)
}
}
completion(list)
}
catch let error as NSError {
print("json error: \(error.localizedDescription)")
completion(nil)
}
}
task.resume()
}
Generally speaking methods should do one specific thing - in your case call the api and return data or error. Initialize your selectors in the view controllers on callback. Your view controller's viewDidLoad
would look like this using the code above:
override func viewDidLoad() {
super.viewDidLoad()
YourApiCallingClass.callApi() {
result in
if let list = result {
self.list = list
self.Selects = [Selectors(Name: self.list[self.index]["name"] as? String, Options: self.BuildOptions(), Correct: 2)]
self.Render()
} else {
//Handle situation where no data will be returned, you can add second parameter to the closue in callApi method that will hold your custom errors just as the dataTaskWithRequest does :D
}
}
}
Now you have a nice separation of concerns, API method is reusable and view controller just handles what happens when it gets the data. It'd be nice if you slapped an UIActivityIndicator in the middle of the screen while waiting, it'd look all neat and professional then :P
Upvotes: 1