ios - 在加载数据之前完成所有异步请求?

标签 ios swift firebase asynchronous firebase-realtime-database

我遇到了一个问题,我有多个异步请求从 Facebook API 和我的 Firebase 数据库中获取图像和信息。我想执行我所有的异步请求,然后将我从 Facebook API/Firebase 数据库中获取的所有数据存储到一个我可以快速加载的完整对象中。我已经为每个异步请求设置了完成处理程序,我认为这会强制程序“等待”直到请求完成,然后让程序继续,但这似乎对我不起作用。以下是我的尝试:

func setupEvents(completion: (result: Bool, Event: Event) -> Void){
    // Get a reference to Events
    eventsReference = Firebase(url:"<DB Name>")
    eventAttendeesRef = Firebase(url:"<DB Name>")

    //Read the data at our posts reference
    println("Event References: \(eventsReference)")
    eventsReference.observeEventType(FEventType.ChildAdded, withBlock: { (snapshot) -> Void in

        let eventName = snapshot.value["eventName"] as? String
        let eventLocation = snapshot.value["eventLocation"] as? String
        let eventCreator = snapshot.value["eventCreator"] as? String

        var attendees: NSMutableDictionary = [:]
        var attendeesImages = [UIImage]()
        let attendee: NSMutableDictionary = [:]

        let group = dispatch_group_create()

        //Get attendees first
        dispatch_group_enter(group)
        self.getAttendees(snapshot.key as String, completion:{ (result, name, objectID) -> Void in
            if(result == true){
                println("Finished grabbing \(name!) \(objectID!)")
                attendees.addEntriesFromDictionary(attendee as [NSObject : AnyObject])
            }
            else {
                println("False")
            }
            dispatch_group_leave(group)
        })

        //Get attendees photos
        dispatch_group_enter(group)
        self.getAttendeesPictures(attendee, completion: { (result, image) -> Void in
            if result == true {
                println("Finished getting attendee photos. Now to store into Event object.")
                attendeesImages.append(image!)
            }
            else{
                println("false")
            }
            dispatch_group_leave(group)
        })

        dispatch_group_notify(group, dispatch_get_main_queue()) {
            println("both requests done")
            //Maintain array snapshot keys
            self.eventIDs.append(snapshot.key)

            if snapshot != nil {
                let event = Event(eventName: eventName, eventLocation:eventLocation, eventPhoto:eventPhoto, fromDate:fromDate, fromTime:fromTime, toDate:toDate, toTime:toTime, attendees: attendees, attendeesImages:attendeesImages, attendeesImagesTest: attendeesImagesTest, privacy:privacy, eventCreator: eventCreator, eventCreatorID: eventCreatorID)
                println("Event: \(event)")
                completion(result: true, Event: event)
            }
        }

        }) { (error) -> Void in
            println(error.description)
    }
}

我知道我的完成处理程序设置正确,因为我已经在我的程序中进行了测试。但是,我想要的是只有在 getAttendeesgetAttendeesPictures 函数都完成后,我才想存储我抓取的所有信息 snapshotgetAttendeesgetAttendeesPictures 函数并将它们存储到 event 对象中。关于如何实现这一目标的任何想法?我试图查看 dispatch_groups 以帮助我通过以下链接处理此问题:Checking for multiple asynchronous responses from Alamofire and Swift但是我的程序似乎只执行getAttendees 函数而不执行getAttendeesPictures 函数。下面还有 getAttendeesgetAttendeesPictures 函数:

func getAttendees(child: String, completion: (result: Bool, name: String?, objectID: String?) -> Void){
    //Get event attendees of particular event
    var attendeesReference = self.eventAttendeesRef.childByAppendingPath(child)
    println("Loading event attendees")
    //Get all event attendees
    attendeesReference.observeEventType(FEventType.ChildAdded, withBlock: { (snapshot) -> Void in
        let name = snapshot.value.objectForKey("name") as? String
        let objectID = snapshot.value.objectForKey("objectID") as? String
        println("Name: \(name) Object ID: \(objectID)")
        completion(result: true, name: name, objectID: objectID)
        }) { (error) -> Void in
            println(error.description)
    }

 func getAttendeesPictures(attendees: NSMutableDictionary, completion: (result: Bool, image: UIImage?)-> Void){
    println("Attendees Count: \(attendees.count)")
    for (key, value) in attendees{
        let url = NSURL(string: "https://graph.facebook.com/\(key)/picture?type=large")
        println("URL: \(url)")
        let urlRequest = NSURLRequest(URL: url!)
        //Asynchronous request to display image
        NSURLConnection.sendAsynchronousRequest(urlRequest, queue: NSOperationQueue.mainQueue()) { (response:NSURLResponse!, data:NSData!, error:NSError!) -> Void in
            if error != nil{
                println("Error: \(error)")
            }
            // Display the image
            let image = UIImage(data: data)
            if(image != nil){
                completion(result: true, image: image)
            }
        }
    }
}

最佳答案

对于在标题中寻求问题答案的用户,请使用此处概述的 dispatch_group 和 GCD:即将一个组嵌入到另一个 dispatch_group 的通知方法中是有效的。另一种进入更高级别的方法是 NSOperations 和依赖项,这也将提供进一步的控制,例如取消操作。

大纲:

func doStuffonObjectsProcessAndComplete(arrayOfObjectsToProcess: Array) -> Void){

    let firstGroup = dispatch_group_create()

    for object in arrayOfObjectsToProcess {

        dispatch_group_enter(firstGroup)

        doStuffToObject(object, completion:{ (success) in
            if(success){
                // doing stuff success
            }
            else {
                // doing stuff fail
            }
            // regardless, we leave the group letting GCD know we finished this bit of work
            dispatch_group_leave(firstGroup)
        })
    }

    // called once all code blocks entered into group have left
    dispatch_group_notify(firstGroup, dispatch_get_main_queue()) {

        let processGroup = dispatch_group_create()

        for object in arrayOfObjectsToProcess {

            dispatch_group_enter(processGroup)

            processObject(object, completion:{ (success) in
                if(success){
                    // processing stuff success
                }
                else {
                    // processing stuff fail
                }
                // regardless, we leave the group letting GCD know we finished this bit of work
                dispatch_group_leave(processGroup)
            })
        }

        dispatch_group_notify(processGroup, dispatch_get_main_queue()) {
            print("All Done and Processed, so load data now")
        }
    }
}

此答案的其余部分特定于此代码库。

这里似乎有几个问题: getAttendees 函数接受一个事件子对象并返回一个 objectIDName,它们都是字符串?这个方法不应该返回一组与会者吗?如果不是,那么返回的 objectID 是什么?

返回一组与会者后,您可以对他们进行分组处理以获取照片。

getAttendeesPictures 最终从 Facebook 返回 UIImages。最好将这些缓存到磁盘并传递 path ref - 保留所有这些获取的图像对内存不利,并且根据大小和数量,可能会很快导致问题。

一些例子:

func getAttendees(child: String, completion: (result: Bool, attendees: Array?) -> Void){

    let newArrayOfAttendees = []()

    // Get event attendees of particular event

    // process attendees and package into an Array (or Dictionary)

    // completion
    completion(true, attendees: newArrayOfAttendees)
}

func getAttendeesPictures(attendees: Array, completion: (result: Bool, attendees: Array)-> Void){

    println("Attendees Count: \(attendees.count)")

    let picturesGroup = dispatch_group_create()

    for attendee in attendees{

       // for each attendee enter group
       dispatch_group_enter(picturesGroup)

       let key = attendee.objectID

       let url = NSURL(string: "https://graph.facebook.com/\(key)/picture?type=large")

        let urlRequest = NSURLRequest(URL: url!)

        //Asynchronous request to display image
        NSURLConnection.sendAsynchronousRequest(urlRequest, queue: NSOperationQueue.mainQueue()) { (response:NSURLResponse!, data:NSData!, error:NSError!) -> Void in
            if error != nil{
                println("Error: \(error)")
            }

            // Display the image
            let image = UIImage(data: data)
            if(image != nil){
               attendee.image = image
            }

            dispatch_group_leave(picturesGroup)
        }
    }

    dispatch_group_notify(picturesGroup, dispatch_get_main_queue()) {
         completion(true, attendees: attendees)
    }
}

func setupEvents(completion: (result: Bool, Event: Event) -> Void){

    // get event info and then for each event...

    getAttendees(child:snapshot.key, completion: { (result, attendeesReturned) in
        if result {
            self.getAttendeesPictures(attendees: attendeesReturned,         completion: { (result, attendees) in

              // do something with completed array and attendees


            }
        }
        else {

        }
    })

}

以上代码只是一个大纲,但希望能为您指明正确的方向。

关于ios - 在加载数据之前完成所有异步请求?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31261500/

相关文章:

ios - 数据不会保存到桌面

ios - Swift,UILongPressGestureRecognizer 在 UIlabel 中不起作用

ios - 如何将照片(或视频)从相机转换为 NSData?

ios - 使用 Firebase 和 Swift 保存多个表部分的数据

javascript - Cloud Functions for Firebase - 创建新用户时写入数据库

ios - 使用 Parse 制作社交媒体应用程序或开发我自己的后端?

ios - 单击后 UITextView 占位符没有消失

ios - RPScreenRecorder stopRecording block 未被调用

ios - 具有图像背景和模糊效果的 TableView - swift

javascript - 数据表和 Firebase Web