Swift 3中的完成处理程序:从函数返回变量

4

我对Swift 3中完成处理程序的语法感到困惑。

在下面的函数中,从Web服务调用解析xml文件后,它应该返回一个变量(一个array [String:String])。
我的尝试如下,但显然是错误的。

  enum HistoryKey {
  case success([String:String])
  case failure(String)
 }

 private func getHistoryKeys(searchterm: String, completion: @escaping () -> HistoryKey) {
    let url = PubmedAPI.createEsearchURL(searchString: searchterm)
    let request = URLRequest.init(url: url as URL)
    let task = session.dataTask(with: request) { (data, response, error) in

        if let theData = data{
            let myParser = XMLParser.init(data: theData)
            myParser.delegate = self
            myParser.parse()
        }
    }
    task.resume()

    if keys.isEmpty {
        return .failure("no historyKeyDictionary")
    }else{
        return .success(keys)
    }

}// End of func

我希望按照以下方式使用此功能。
 let result = self.getHistoryKeys(searchTerm)

@EricAya 他并不是试图从函数中返回值,而是将其传递给另一个完成处理程序。 - Tristan Beaton
你说得对。我没有注意到那个。@EricAya - Tristan Beaton
请接受答案,以便其他人更快地找到它。谢谢。按下jackdaw。 - Vlad Pulichev
6个回答

14

存在两个问题:

  • 完成处理程序传递了一个HistoryKey实例并且没有返回值,因此签名必须颠倒顺序。
  • 调用完成处理程序必须在数据任务的完成块内部

为了能够在完成块外解析接收到的数据,在成功时返回data

enum ConnectionResult {
   case success(Data)
   case failure(Error)
}

private func getHistoryKeys(searchterm: String, completion: @escaping (ConnectionResult) -> ()) {
   let url = PubmedAPI.createEsearchURL(searchString: searchterm)
   let task = session.dataTask(with: url) { (data, response, error) in
       if let error = error {
          completion(.failure(error))
       } else {
          completion(.success(data!))
       }
  }
  task.resume()
}

并将其称为

getHistoryKeys(searchterm: String) { connectionResult in 
    switch connectionResult {
       case .success(let data): 
           let myParser = XMLParser(data: data)
           myParser.delegate = self
           myParser.parse()
           // get the parsed data from the delegate methods

       case .failure(let error): print(error)
    }
}

抱歉,我刚刚添加了一个编辑来说明我想如何使用该函数。您的解决方案是否适用于上述情况? - tim
不是这样的。你必须返回接收到的“Data”,因为解析器是异步工作的,需要在函数外部解析XML;或者你可以将完成处理程序分配给一个变量以获取强引用,并从XMLParser委托方法中调用完成处理程序。另外:你枚举的array是一个字典。 - vadian
PS:我更新了答案,以便在完成处理程序中传递接收到的数据。将处理XML解析器结果的代码放入解析器委托方法中。 - vadian
我理解你的解决方案 - 从委托方法(例如parserDidEndDocument)获取变量。但是,你能否向我展示如何从parserDidEndDocument调用完成处理程序呢? - tim

3

您没有使用completion块。
请像这样使用:

private func getHistoryKeys(searchterm: String, completion: @escaping (_ keys: Array) -> Void) {
    //do the magic
    completion(keys)
}

那么你可以这样调用该函数:

getHistoryKeys(searchterm: "str") { (keys) in 
    print("\(keys)")
}

2

Swift 4.2

enum HistoryKey {
    case success([String:String])
    case failure(String)
}

func myFunction(str: String, completionHandler: @escaping (HistoryKey) -> ()){
     completion(.success([String:String]))
     //OR
     completion(.failure(""))
}

myFunction(str: String) { result in 
    switch result {
       case .success(let data): break;
       case .failure(let error): break;
    }
}

或者

func myFunction(str: String, completionHandler: @escaping (String) -> ()){
     completionHandler("")
} 

myFunction(str: "someThing", completionHandler: {(str) in

})

1
将结果作为完成处理程序的参数返回:
private func getHistoryKeys(searchterm: String, completion: @escaping (result: HistoryKey) -> Void) {
    let url = PubmedAPI.createEsearchURL(searchString: searchterm)
    let request = URLRequest.init(url: url as URL)
    let task = session.dataTask(with: request) { (data, response, error) in

        if let theData = data{
            let myParser = XMLParser.init(data: theData)
            myParser.delegate = self
            myParser.parse()
        }

        //DispatchQueue.main.async { // if you want to do UI stuff dispatch calls to completion() on the main queue
        if keys.isEmpty {
            completion(.failure("no historyKeyDictionary"))
        } else{
            completion(.success(keys))
        }
        //}
    }
    task.resume()   
}

并像这样调用它:

getHistoryKeys("searchMe") { (result: HistoryKey) in
    print(result)
}

抱歉,我刚刚添加了一个编辑来说明我想如何使用该函数。你的解决方案是否适用于上述情况?因为你的返回值是void。 - tim
不要着急,self.getHistoryKeys(searchTerm) 会立即返回,但是你的下载需要时间,请耐心等待。 - shallowThought

0
从我的角度来看,应该是这样的

private func getHistoryKeys(searchterm: String, completion: @escaping (HistoryKey) -> ())

在编程中,应该使用completion(.failure("no historyKeyDictionary"))completion(.success(keys))代替return


0
 enum HistoryKey {
    case success([String: String])
    case failure(String)
 }

 private func getHistoryKeys(searchterm: String, completion: @escaping (_ result: HistoryKey) -> Void) {
    let url = PubmedAPI.createEsearchURL(searchString: searchterm)
    let request = URLRequest.init(url: url as URL)
    let task = session.dataTask(with: request) { (data, response, error) in

        if let theData = data{
            let myParser = XMLParser.init(data: theData)
            myParser.delegate = self
            myParser.parse()
        }

        if keys.isEmpty {
            completion(.failure("no historyKeyDictionary"))
        } else {
            completion(.success(keys))
        }
    }
    task.resume()    
} // End of func

就像这样。更改@escaping声明并执行完成代替返回。

希望能有所帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接