根据Swift中的用户输入取消URLSession



我将应用程序的API调用集中在一个名为APIService的类中。来电如下:

// GET: Attempts getconversations API call. Returns Array of Conversation objects or Error
func getConversations(searchString: String = "", completion: @escaping(Result<[Conversation], APIError>) -> Void) {
{...} //setting up URLRequest
let dataTask = URLSession.shared.dataTask(with: request) { data, response, error in
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200, let _ = data
else {
print("ERROR: ", error ?? "unknown error")
completion(.failure(.responseError))
return
}
do {
{...} //define custom decoding strategy
}
let result = try decoder.decode(ResponseMultipleElements<[Conversation]>.self, from: data!)
completion(.success(result.detailresponse.element))
}catch {
completion(.failure(.decodingError))
}
}
dataTask.resume()
}

我正在从应用程序中的任何位置执行API调用,如:

func searchConversations(searchString: String) {
self.apiService.getConversations(searchString: searchString, completion: {result in
switch result {
case .success(let conversations):
DispatchQueue.main.async {
{...} // do stuff 
}
case .failure(let error):
print("An error occured (error.localizedDescription)")
}
})
}

我现在想实现的是,在输入searchString时,对用户敲击的每个字符执行func searchConversations。这将很容易,只需根据正在激发的UIPressesEvent调用func searchConversations。像这样:

override func pressesEnded(_ presses: Set<UIPress>, with event: UIPressesEvent?) {
guard let key = presses.first?.key else { return }
switch key.keyCode {
{...} // handle special cases
default:
super.pressesEnded(presses, with: event)
searchConversations(searchString: SearchText.text)
}
}

我现在的问题是:每当输入一个新角色时,我都想取消上一个URLSession并启动一个新的URLSession。如何在UIPressesEvent处理程序内部执行此操作?

基本思想是确保API返回一个稍后可以取消的对象(如果需要(,然后修改搜索例程以确保取消任何挂起的请求(如果有(:

  1. 首先,让API调用返回URLSessionTask对象:

    @discardableResult
    func getConversations(searchString: String = "", completion: @escaping(Result<[Conversation], APIError>) -> Void) -> URLSessionTask {
    ...
    let dataTask = URLSession.shared.dataTask(with: request) { data, response, error in
    ...
    }
    dataTask.resume()
    return dataTask
    }
    
  2. 让你的搜索程序跟踪最后一项任务,如果需要,可以取消它:

    private weak var previousTask: URLSessionTask?
    func searchConversations(searchString: String) {
    previousTask?.cancel()
    previousTask = apiService.getConversations(searchString: searchString) { result in
    ...
    }
    }
    
  3. 我们经常添加一个微小的延迟,这样如果用户快速输入,我们就可以避免许多不必要的网络请求:

    private weak var previousTask: URLSessionTask?
    private weak var delayTimer: Timer?
    func searchConversations(searchString: String) {
    previousTask?.cancel()
    delayTimer?.invalidate()
    delayTimer = Timer.scheduledTimer(withTimeInterval: 0.25, repeats: false) { [weak self] _ in
    guard let self = self else { return }
    self.previousTask = self.apiService.getConversations(searchString: searchString) {result in
    ...
    }
    }
    }
    
  4. 唯一的另一件事是,你可能想更改你的网络请求错误处理程序,这样请求的"取消"就不会像处理错误一样处理了。从URLSession的角度来看,取消是一个错误,但从我们的应用程序的角度来说,取消不是一个错误条件,而是一个预期的流程。

您可以通过使用计时器来实现这一点

1( 定义计时器变量

var requestTimer: Timer?

2( 更新searchConversations功能

@objc func searchConversations() {
self.apiService.getConversations(searchString: SearchText.text, completion: {result in
switch result {
case .success(let conversations):
DispatchQueue.main.async {
{...} // do stuff
}
case .failure(let error):
print("An error occured (error.localizedDescription)")
}
})
}

3( 更新按结束

override func pressesEnded(_ presses: Set<UIPress>, with event: UIPressesEvent?) {
guard let key = presses.first?.key else { return }
switch key.keyCode {
{...} // handle special cases
default:
super.pressesEnded(presses, with: event)
self.requestTimer?.invalidate()
self.requestTimer = Timer.scheduledTimer(timeInterval: 0.5, target: self, selector: #selector(searchConversations), userInfo: nil, repeats: false)
}
}

相关内容

  • 没有找到相关文章

最新更新