Swift 中的 resume 用法整理
在 Swift 中,resume 主要出現在兩個場景:URLSessionTask 和 Continuation。這篇文章幫你釐清兩者的差異和正確用法。
resume 的觀念如下圖,因為過去的 fetch 沒有 async await ,所以在現在有 async await 的 function 中如果有去 call fetch 的東西,我們會需要知道說,是否 fetch 已經完成了。所以需要透過調用 resume 才會知道說完成了,這個時候 async 的 function 才會結束 task 不然, task 就會一直被掛著,這時 swift 就會有 error log 出現。
## 類似這種SWIFT TASK CONTINUATION MISUSE: sendHeartbeat(with:) leaked its continuation without resuming it. This may cause tasks waiting on it to remain suspended forever.

📋 目錄
1. URLSessionTask 的 resume()
基本用法
let url = URL(string: "https://api.example.com/data")!
let task = URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
print("收到資料: \(data)")
} else if let error = error {
print("錯誤: \(error)")
}
}
task.resume() // ⚠️ 必須呼叫才會開始請求
為什麼需要 resume()?
URLSession.dataTask()建立的是暫停狀態的任務- 只有呼叫
.resume()才會真正發送網路請求 - 忘記呼叫 = 請求永遠不會發送
進階用法:取消任務
let task = URLSession.shared.dataTask(with: url) { data, response, error in
// 處理回應
}
// 開始請求
task.resume()
// 如果需要取消
task.cancel()
2. Continuation 的 resume()
基本概念
當你要把 callback-based API 包裝成 async/await 時,會用到 withCheckedContinuation:
func fetchUserData() async throws -> User {
try await withCheckedThrowingContinuation { continuation in
// 呼叫舊的 callback API
oldAPICall { result, error in
if let result = result {
continuation.resume(returning: result) // ✅ 成功
} else if let error = error {
continuation.resume(throwing: error) // ❌ 失敗
}
}
}
}
不同類型的 Continuation
// 1. 不回傳值
func performAction() async {
await withCheckedContinuation { continuation in
someAction {
continuation.resume() // 只喚醒,不回傳值
}
}
}
// 2. 回傳值
func fetchString() async -> String {
await withCheckedContinuation { continuation in
someAPI { result in
continuation.resume(returning: result)
}
}
}
// 3. 可能拋出錯誤
func fetchData() async throws -> Data {
try await withCheckedThrowingContinuation { continuation in
someAPI { result, error in
if let error = error {
continuation.resume(throwing: error)
} else {
continuation.resume(returning: result)
}
}
}
}
3. 常見錯誤與注意事項
⚠️ 重要規則
-
Continuation 只能
resume()一次// ❌ 錯誤 :會造成 crashcontinuation.resume(returning: "first")continuation.resume(returning: "second") // 💥 SWIFT TASK CONTINUATION MISUSE -
必須呼叫
resume()// ❌ 錯誤:await 會永遠等待func badExample() async -> String {await withCheckedContinuation { continuation in// 忘記呼叫 resume() → 永遠卡住}} -
URLSessionTask 必須
resume()// ❌ 錯誤:請求不會發送let task = URLSession.shared.dataTask(with: url) { ... }// 忘記 task.resume() → 請求永遠不會發送
最佳實踐
// ✅ 正確:使用 defer 確保 resume 被呼叫
func safeFetch() async throws -> String {
try await withCheckedThrowingContinuation { continuation in
defer {
// 如果沒有其他 resume 被呼叫,這裡會處理
}
someAPI { result, error in
if let error = error {
continuation.resume(throwing: error)
} else {
continuation.resume(returning: result)
}
}
}
}
4. 完整實例:包裝 URLSession 為 async/await
// 舊的 callback 方式
func fetchDataOld(completion: @escaping (Result<Data, Error>) -> Void) {
let url = URL(string: "https://api.example.com/data")!
let task = URLSession.shared.dataTask(with: url) { data, response, error in
if let error = error {
completion(.failure(error))
} else if let data = data {
completion(.success(data))
}
}
task.resume() // URLSessionTask 的 resume
}
// 新的 async/await 包裝
func fetchDataNew() async throws -> Data {
try await withCheckedThrowingContinuation { continuation in
let url = URL(string: "https://api.example.com/data")!
let task = URLSession.shared.dataTask(with: url) { data, response, error in
if let error = error {
continuation.resume(throwing: error) // Continuation 的 resume
} else if let data = data {
continuation.resume(returning: data) // Continuation 的 resume
}
}
task.resume() // URLSessionTask 的 resume
}
}
// 使用方式
func useNewAPI() async {
do {
let data = try await fetchDataNew()
print("成功取得資料: \(data)")
} catch {
print("錯誤: \(error)")
}
}
5. 總結對比
| 類型 | 用途 | 呼叫時機 | 注意事項 |
|---|---|---|---|
URLSessionTask.resume() | 啟動網路請求 | 建立 task 後立即呼叫 | 不呼叫 = 請求不發送 |
Continuation.resume() | 喚醒 await 等待 | callback 完成時呼叫 | 只能呼叫一次 |
Task {} | Swift Concurrency | 建立即自動執行 | 不需要 resume() |
記憶口訣
- URLSessionTask:建立後要
resume()才會發送 - Continuation:callback 完成時要
resume()喚醒 await - Swift Task:建立就自動跑,不用
resume()
🎯 實用技巧
- 使用
withCheckedContinuation而不是withUnsafeContinuation(更安全) - URLSessionTask 記得
resume(),否則請求不會發送 - Continuation 只能
resume()一次,重複呼叫會 crash - 考慮使用
defer來確保 continuation 一定會被 resume