JSON完成处理程序

时间:2017-09-27 20:04:17

标签: ios arrays json swift xcode

这是我目前使用的代码:

func fetchOne(){
        URLSession.shared.dataTask(with:apiURL!, completionHandler: {(data, response, error) in
            guard let data = data, error == nil else { return }
            do {
                let allContactsData = try Data(contentsOf: self.apiURL!)
                let allContacts = try JSONSerialization.jsonObject(with: allContactsData, options: JSONSerialization.ReadingOptions.allowFragments) as! [String : AnyObject]
                if let arrJSON = allContacts["data"] as? [[String : Any]] {
                    for aObject in arrJSON {
                        self.followerUsername.append(aObject["username"] as! String)
                        self.followerFullName.append(aObject["full_name"] as! String)
                    }
                }
//                print(self.followerUsername)
//                print(self.followerFullName)

            } catch let error as NSError {
                print(error)
            }
        }).resume()
    }

如何检测json何时完成获取数据中的所有信息,然后运行新函数fetchTwo()

3 个答案:

答案 0 :(得分:1)

如果您的方法是异步,那么您可以为您的函数添加completionHandler,如下所示:

func fetchOne(onCompletion: @escaping (Bool) -> Void, onError: @escaping (NSError) -> Void) {
    URLSession.shared.dataTask(with:apiURL!, completionHandler: {(data, response, error) in
        guard let data = data, error == nil else { return }
        do {
            let allContactsData = try Data(contentsOf: self.apiURL!)
            let allContacts = try JSONSerialization.jsonObject(with: allContactsData, options: JSONSerialization.ReadingOptions.allowFragments) as! [String : AnyObject]
            if let arrJSON = allContacts["data"] as? [[String : Any]] {
                for aObject in arrJSON {
                    self.followerUsername.append(aObject["username"] as! String)
                    self.followerFullName.append(aObject["full_name"] as! String)
                }
                onCompletion(true)
            }
//                print(self.followerUsername)
//                print(self.followerFullName)

        } catch let error as NSError {
            print(error)
            onError(error)
        }
    }).resume()
}

用法:

fetchOne(onCompletion: { (successful) in
    print(successful)
    fetchTwo()
}) { (error) in
    print(error.domain)
}

如果同步,请执行以下操作:

func fetchOne(){
    URLSession.shared.dataTask(with:apiURL!, completionHandler: {(data, response, error) in
        guard let data = data, error == nil else { return }
        do {
            let allContactsData = try Data(contentsOf: self.apiURL!)
            let allContacts = try JSONSerialization.jsonObject(with: allContactsData, options: JSONSerialization.ReadingOptions.allowFragments) as! [String : AnyObject]
            if let arrJSON = allContacts["data"] as? [[String : Any]] {
                for aObject in arrJSON {
                    self.followerUsername.append(aObject["username"] as! String)
                    self.followerFullName.append(aObject["full_name"] as! String)
                }
            }
//                print(self.followerUsername)
//                print(self.followerFullName)
                  fetchTwo()

        } catch let error as NSError {
            print(error)
        }
    }).resume()
}

<强>更新

fetchOne(onCompletion: { (successful) in
    print(successful) // fetch one
    fetchTwo(onCompletion: { (successful) in
        print(successful) // fetch two
    }) { (error) in
        print(error.domain)
    }
}) { (error) in
    print(error.domain)
}

答案 1 :(得分:-1)

同步任务:

当您同步执行任务时,主线程将等待此任务完成,然后再继续执行其他任务

异步任务:

另一方面,当您异步执行任务时,方法将继续,而无需知道块是否成功。主线程不会被阻止,它将继续响应用户界面。完成处理程序将在您的任务完成时在后台通知您(这就是您需要使用@escaping的原因)

在您的情况下:

在您的情况下,您需要从API下载一些数据。您不知道何时会收到数据。它可能很快,但也可能很长。这是异步任务的示例。您不希望阻止UI,因此您希望仅在任务完成时收到通知。

以下是您需要重写方法的方法:

func fetchOne(_ completion: @escaping (Bool) -> ()) {
        URLSession.shared.dataTask(with:apiURL!, completionHandler: { (data, response, error) in

            guard let data = data, error == nil else { 
                 completion(false) // Something went wrong
                 return 
            }

            do {

                let allContactsData = try Data(contentsOf: self.apiURL!)
                let allContacts = try JSONSerialization.jsonObject(with: allContactsData, options: JSONSerialization.ReadingOptions.allowFragments) as! [String : AnyObject]
                if let arrJSON = allContacts["data"] as? [[String : Any]] {
                    // ...

                    completion(true) // Everything is fine
                }

            } catch let error as NSError {

                completion(false) // Something went wrong
            }

        }).resume()
    }

你完成块(完成时会被调用的内容)看起来像这样:

fetchOne { [weak self] (success) in

    guard success else {
       return
    }

    self?.fetchTwo()
}

如您所见,当您完成任务时,您将调用完成处理程序并返回true。如果下载任务失败,则调用完成处理程序并返回false。在这两种情况下(成功或失败),将调用您的完成块。这就是为什么你需要检查成功的价值。在这种情况下,保护声明就足够了,以确保success == true

当然,你可以在你有多种方法的情况下以及当你想知道它们何时完成时都这样做。

如果您有3个或更多任务,我建议您使用DispatchGroups来避免嵌套阻止。只要您需要等待一对,您就可以使用DispatchGroups继续之前的异步或同步任务。

向您展示DispatchGroups如何运作的示例。假设您有四个不同的任务:fetchOnefetchTwofetchThreefetchFour

let dispatchGroup = DispatchGroup()

dispatchGroup.enter()

fetchOne { (success) in

    guard success else {
       return
    }

    dispatchGroup.leave()
}

// ... fetchTwo, fetchThree...

dispatchGroup.enter()

fetchFour { (success) in

    guard success else {
       return
    }

    dispatchGroup.leave()
}

dispatchGroup.notify(queue: DispatchQueue.main, execute: {

    // Do something here when all your tasks are completed

})

如果您有任何疑问,请告诉我们。)

答案 2 :(得分:-1)

欢迎来到Swift:)

您正在将同步和异步代码混合在一起。

当您调用login时,您希望它立即返回[String:String]类型的答案。

但是在您的登录方法中,您会进行无法立即返回的网络呼叫...这就是为什么对Alamofire.request的调用会将完成块作为参数。

所以......你需要改变你的登录方法,所以:

不会立即返回任何内容(它不能这样做...登录需要我们进行网络通话记住) 登录成功后,将完成块调用。 这可以这样做:

public func login(userName: String, password: String, loginCompletion: @escaping ([String : String]) -> ())

欢迎来到Swift:)

您正在将同步和异步代码混合在一起。

当您调用login时,您希望它立即返回[String:String]类型的答案。

但是在您的登录方法中,您会进行无法立即返回的网络呼叫...这就是为什么对Alamofire.request的调用会将完成块作为参数。

所以......你需要改变你的登录方法,所以:

不会立即返回任何内容(它不能这样做...登录需要我们进行网络通话记住) 登录成功后,将完成块调用。 这可以这样做:

public func login(userName:String,password:String,loginCompletion:@escaping([String:String]) - &gt;()) 这里我们有一个函数,它接受一个String类型的userName,一个String类型的密码和一个类型为function的loginCompletion,它再次将[String:String]字典作为参数。请注意,该方法不会返回任何内容。

现在你可以像以前一样调用你的makeWebServiceCall:

let loginrequest = JsonRequests.loginRequest(userName: userName, password: password)
makeWebServiceCall(urlAddress: URL, requestMethod: .post, params: loginrequest, completion: { (JSON : Any) in
   //Now we are ready, the login call has returned some data to you. 

   //You have an attribute named JSON of type Any, which you need to convert to [String : String], and then you can call loginCompletion with that, like so:
   loginCompletion(yourConvertedDictionaryHere)
})

以下是完整性的新登录方法:

public func login(userName: String, password: String, loginCompletion: @escaping ([String : String]) -> ()) {
    let loginrequest = JsonRequests.loginRequest(userName: userName, password: password)
    makeWebServiceCall(urlAddress: URL, requestMethod: .post, params: loginrequest, completion: { (JSON : Any) in
       //Now we are ready, the login call has returned some data to you. 

       //You have an attribute named JSON of type Any, which you need to convert to [String : String], and then you can call loginCompletion with that, like so:
       loginCompletion(yourConvertedDictionaryHere)
    })
}

欢迎来到Swift:)

您正在将同步和异步代码混合在一起。

当您调用login时,您希望它立即返回[String:String]类型的答案。

但是在您的登录方法中,您会进行无法立即返回的网络呼叫...这就是为什么对Alamofire.request的调用会将完成块作为参数。

所以......你需要改变你的登录方法,所以:

不会立即返回任何内容(它不能这样做...登录需要我们进行网络通话记住) 登录成功后,将完成块调用。 这可以这样做:

public func login(userName:String,password:String,loginCompletion:@escaping([String:String]) - &gt;()) 这里我们有一个函数,它接受一个String类型的userName,一个String类型的密码和一个类型为function的loginCompletion,它再次将[String:String]字典作为参数。请注意,该方法不会返回任何内容。

现在你可以像以前一样调用你的makeWebServiceCall:

让loginrequest = JsonRequests.loginRequest(userName:userName,password:password) makeWebServiceCall(urlAddress:URL,requestMethod:.post,params:loginrequest,completion:{(JSON:Any)in    //现在我们准备好了,登录调用已经返回了一些数据给你。

//你有一个名为JSON的属性Any,你需要转换为[String:String],然后你可以用它来调用loginCompletion,如下所示:    loginCompletion(yourConvertedDictionaryHere) }) 以下是完整性的新登录方法:

public func login(userName:String,password:String,loginCompletion:@escaping([String:String]) - &gt;()){     let loginrequest = JsonRequests.loginRequest(userName:userName,password:password)     makeWebServiceCall(urlAddress:URL,requestMethod:.post,params:loginrequest,completion:{(JSON:Any)in        //现在我们准备好了,登录调用已经返回了一些数据给你。

   //You have an attribute named JSON of type Any, which you need to convert to [String : String], and then you can call loginCompletion with that, like so:
   loginCompletion(yourConvertedDictionaryHere)
})

} 然后你就像这样调用你的登录方法:

retur.login(userName: "root", password: "admin01") { stringDictionary: [String : String] in
    //here you have your stringDictionary which you can use as pleased
}

希望对你有所帮助。