根据 Swift 中的用户输入取消 URLSession
Cancel a URLSession based on user input in Swift
我已将对我的应用程序的 API 调用集中在名为 APIService
的 class 中。
调用如下所示:
// 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 并启动一个新字符。我怎样才能从 UIPressesEvent
处理程序中做到这一点?
您可以使用计时器来实现,
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) 更新 pressesEnded
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)
}
}
基本思想是确保 API return 是一个以后可以取消的对象,如果需要的话,然后修改搜索例程以确保取消任何待处理的请求,如果有:
首先,API 调用 return 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
}
让您的搜索例程跟踪上一个任务,如果需要则取消它:
private weak var previousTask: URLSessionTask?
func searchConversations(searchString: String) {
previousTask?.cancel()
previousTask = apiService.getConversations(searchString: searchString) { result in
...
}
}
我们经常添加一个微小的延迟,以便在用户快速输入时避免大量不必要的网络请求:
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
...
}
}
}
唯一的另一件事是您可能想要更改网络请求错误处理程序,以便不会像错误一样处理请求的“取消”。从URLSession
的角度来看,取消是一个错误,但从我们的应用程序的角度来看,取消不是一个错误条件,而是一个预期的流程。
我已将对我的应用程序的 API 调用集中在名为 APIService
的 class 中。
调用如下所示:
// 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 并启动一个新字符。我怎样才能从 UIPressesEvent
处理程序中做到这一点?
您可以使用计时器来实现,
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) 更新 pressesEnded
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)
}
}
基本思想是确保 API return 是一个以后可以取消的对象,如果需要的话,然后修改搜索例程以确保取消任何待处理的请求,如果有:
首先,API 调用 return
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 }
让您的搜索例程跟踪上一个任务,如果需要则取消它:
private weak var previousTask: URLSessionTask? func searchConversations(searchString: String) { previousTask?.cancel() previousTask = apiService.getConversations(searchString: searchString) { result in ... } }
我们经常添加一个微小的延迟,以便在用户快速输入时避免大量不必要的网络请求:
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 ... } } }
唯一的另一件事是您可能想要更改网络请求错误处理程序,以便不会像错误一样处理请求的“取消”。从
URLSession
的角度来看,取消是一个错误,但从我们的应用程序的角度来看,取消不是一个错误条件,而是一个预期的流程。