【问题标题】:JSON Completion HandlerJSON 完成处理程序
【发布时间】:2017-09-27 20:04:16
【问题描述】:

这是我目前使用的代码:

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 何时完成获取 data 中的所有信息,然后运行新函数 fetchTwo()

【问题讨论】:

  • 只需将fetchTwo() 放在注释掉print 语句的位置即可?
  • 如果你想要同步,那么你可以在 fetchOne 中调用下一个,或者你可以使用信号量。否则,我建议使用通知来广播它并在观察者中触发 fetchTwo。
  • @smarx 那么,不管username中的元素/字符串是100个还是10000个,全部完成后它还会运行吗?
  • @CanJoe 是的。为什么不试试看呢?
  • @smarx 我可以,但这是使用 Instagram API,所以它只允许我获取 10 个用户:) 也可以试试 Rashwan L 的答案。

标签: ios arrays json swift xcode


【解决方案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)
}

【讨论】:

  • 我怎么知道它是异步还是同步
  • URLSession.shared.dataTask 应该是异步的。
  • 以前从未使用过这个。有趣的。我究竟应该把 Usage: 代码放在哪里。
  • 您今天在哪里调用 fetchOne,用 usage 代码替换该调用。
  • 是否也可以使用三个不同的功能来做到这一点?在 fetchOne 之后运行 fetchTwo,在 fetchTwo 之后运行 fetchThree?
【解决方案2】:

欢迎使用 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]) -> ()) 这里我们有一个函数,它接受一个 String 类型的 userName、一个 String 类型的密码和一个 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]) -> ()) 这里我们有一个函数,它接受一个 String 类型的 userName、一个 String 类型的密码和一个 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]) -> ()) { 让 loginrequest = JsonRequests.loginRequest(用户名:用户名,密码:密码) 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
}

希望对你有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-06-02
    • 2018-05-03
    • 2015-08-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多