从异步线程返回值

时间:2015-03-03 21:56:45

标签: swift asynchronous nsurlsession

在一个操场文件中,我有一个函数应该返回一个数组,其中Lesson是一个自定义数据类型。在屏幕右边的操场上,它显示一个数组,其中的值应该在数组中,但是如果我尝试将返回的数组分配给变量以供进一步使用,则数组似乎是空的。登记/> 我已经在网上看到,这与NSURLSession.sharedSession()有关,这是一个异步线程,因此在函数获取数据之前返回我的数组。

代码:

//
//  CCApp
//
//  Created by Milo Cesar on 02-03-15.
//  Copyright (c) 2015 Experium. All rights reserved.
//

import Foundation
import XCPlayground

/*
Custom Data Types
Day:    Used to easily transition from day to int
Lesson: Used to store all the necessary info to be displayed to the user
*/

//Day Enum
enum Day: Int{
case Maandag = 0, Dinsdag, Woensdag, Donderdag, Vrijdag
}

//Lesson Struct
struct Lesson {
var day:Day
var start:String
var end:String
var lesson:String
var room:String
var teacher:String
var groups:String
var type:String
var isBreak:Bool
var isCanceled:Bool
}

/*
Code from medium.com used to get & parse JSON
source: https://medium.com/swift-programming/learn-nsurlsession-using-swift-ebd80205f87c
*/
func httpGet(request: NSURLRequest!, callback: (String, String?) -> Void) {
var session = NSURLSession.sharedSession()
var task = session.dataTaskWithRequest(request){
    (data, response, error) -> Void in
    if error != nil {
        callback("", error.localizedDescription)
    } else {
        var result = NSString(data: data, encoding:
            NSASCIIStringEncoding)!
        callback(result, nil)
    }
}
task.resume()
}

func parseJSON(inputData: NSData) -> Array<AnyObject>{
var error: NSError?
var boardsDictionary = NSJSONSerialization.JSONObjectWithData(inputData, options: NSJSONReadingOptions.MutableContainers, error: &error) as Array<AnyObject>

return boardsDictionary
}

/*
Code from medium.com mixed with own input and adaptations.
*/

var request = NSMutableURLRequest(URL: NSURL(string: "http://api.ccapp.it/v1/student/110919/schedule/10")!)


func loadLesson() -> Array<Lesson>{
var loadedLesson:Array<Lesson> = []

//Get JSON Data using http://api.ccapp.it/v1/student/110919/schedule/10 as JSON location
httpGet(request) {
    (data, error) -> Void in
    //Check for Errors
    if error != nil {
        println(error)
    } else {
    //If there are no errors debug (print data) and transistion from JSON to Lesson Struct
        println(data)
        loadedLesson = setupLesson(data)
    }
}
return loadedLesson
}

func setupLesson(data: String) -> Array<Lesson>{
//Clear Array when we get load a new schedule
var loadedLesson:Array<Lesson> = []

//Encode NSString to NSData
let jsonData = data.dataUsingEncoding(NSASCIIStringEncoding)

//parse NSData and return Array<AnyObject>
    //AnyObject is here a Array<NSDictionary>
var jsonArray = parseJSON(jsonData!)

//Loop through the avaidable days in the schedule
for days in 0...jsonArray.count-1{
    //Validate the before stated Array<NSDictionary>
    if let day: AnyObject = jsonArray[days] as? Array<NSDictionary>{
        //I think this line and the previous line should be able to be one line of code but I got no clue how so just cast it as a Array<NSDictionary> (again?)
        if let dayArray:Array<NSDictionary> = day as AnyObject? as Array<NSDictionary>? {

            //Loop through the lessons of the day
            for lessonList in 0...dayArray.count-1{
                //Get all values and store them in a local variable for easy access
                var day:Day
                var start:String = dayArray[lessonList].valueForKey("start") as String
                var end:String = dayArray[lessonList].valueForKey("end") as String
                var lessonID:String = dayArray[lessonList].valueForKey("lesson") as String
                var room:String = dayArray[lessonList].valueForKey("room") as String
                var teacher:String = dayArray[lessonList].valueForKey("teacher") as String
                var groups:String = dayArray[lessonList].valueForKey("groups") as String
                var type:String = dayArray[lessonList].valueForKey("type") as String
                var isBreak:Bool = dayArray[lessonList].valueForKey("break") as Bool
                var isCanceled:Bool = dayArray[lessonList].valueForKey("canceled") as Bool

                //Create a new Lesson Value for easy storage
                var lesson = Lesson(day: Day.Maandag, start:start, end:end, lesson:lessonID, room:room, teacher:teacher, groups:groups, type:type, isBreak:isBreak, isCanceled:isCanceled)

                //Add the lesson to the array
                loadedLesson.append(lesson)
            }
        }
    }
}
//return the now filled array of lessons
return loadedLesson
}

//Receive a empty Array List
var lessonList = loadLesson()
lessonList.isEmpty

XCPSetExecutionShouldContinueIndefinitely(continueIndefinitely: true)

2 个答案:

答案 0 :(得分:3)

这是正确的:您的HTTP请求是异步的,这意味着请求在后台线程上运行,同时允许用户界面操作以及在主线程上继续无阻碍地继续运行的任何其他操作。为了获得结果数据,您可以将匿名回调函数作为参数传递给loadLesson,这将在请求完全处理后返回您的数组:

func loadLesson(onComplete: ([Lesson]) -> ()) {

    httpGet(request) {
        (data, error) -> Void in

        if error != nil {
            println(error)
        } else {
            let lessons = setupLesson(data)
            onComplete(lessons)
        }
    }
}

loadLesson { (lessons) in
    for lesson in lessons {
        println(lesson)
    }
}

现在,您不是立即尝试访问数据,而是将作为参数传递给loadLesson的块等待执行,直到从httpGet内调用它为止。

答案 1 :(得分:1)

您需要在请求完成后将要执行的所有内容放入完成块。您在请求有机会完成之前返回loadedLesson,因此返回一个空数组。

处理此问题的一种方法是在加载数据时调用另一个函数。

例如:

func finishedLoading(array: Array<Lesson>) {
    // Do something with the array
}
func loadLesson() {
    //Get JSON Data using http://api.ccapp.it/v1/student/110919/schedule/10 as JSON location
    httpGet(request) {
        (data, error) -> Void in
        //Check for Errors
        if error != nil {
            println(error)
        } else {
        //If there are no errors debug (print data) and transistion from JSON to Lesson Struct
            println(data)
            finishedLoading(setupLesson(data))
        }
    }
}

旁注:

您可以使用for lessonList in 0...dayArray.count-1代替..<,这将在数组结束前停止迭代:

 for lessonList in 0..<dayArray.count {
     // inner loop
 }