Swift Combine 二:錯誤處理

上一篇講解了 Combine 中的兩大概念:Publisher 和 Subscriber 以及其基本使用。這一篇主要講解一下缩功,當 Combine 流中發(fā)生錯誤,我們應該怎么處理。

錯誤主要是分為兩大類沛贪,一種是錯誤類型不匹配怕午,一種則是上下游操作發(fā)生異常废登。

錯誤類型不匹配

假設,我們要實現(xiàn)一個加載網(wǎng)絡圖片的一個需求郁惜。

首先鄙煤,定義一個 RequestError 的枚舉,用來表示請求過程中發(fā)生的錯誤:

enum RequestError: Error {
 case sessionError(error: Error)
}

接著圃庭,聲明兩個實例對象:

private let imageURLPublisher = PassthroughSubject<URL, RequestError>() // 圖片加載請求發(fā)布者
private var cancel: AnyCancellable?

然后蝙眶,將數(shù)據(jù)與接受者進行綁定:

cancel = imageURLPublisher.flatMap { requestURL in
 return URLSession.shared.dataTaskPublisher(for: requestURL)
}.sink { error in
 print(error)
} receiveValue: { result in
 let image = UIImage(data: result.data)
 print(image)
}

但此時你會發(fā)現(xiàn)編譯報錯:

原因在于 DataTaskPublisher 的錯誤類型為 URLError, 與 RequestError 類型是不一致恨樟。 DataTaskPublisher 源碼:

public struct DataTaskPublisher : Publisher, Sendable {
 public typealias Output = (data: Data, response: URLResponse)
 public typealias Failure = URLError
 public let request: URLRequest
 public let session: URLSession
 public init(request: URLRequest, session: URLSession)

 public func receive<S>(subscriber: S) where S : Subscriber, S.Failure == URLError, S.Input == (data: Data, response: URLResponse)
}

我們用 mapError 將錯誤類型進行一下轉(zhuǎn)換就可以了:

cancel = imageURLPublisher.flatMap { requestURL in
 return URLSession.shared.dataTaskPublisher(for: requestURL)
 .mapError { error -> RequestError in
 return RequestError.sessionError(error: error)
 }
}.sink { error in
 print(error)
} receiveValue: { result in
 let image = UIImage(data: result.data)
 print(image)
}

最后半醉,就是在 touchesBegan 中觸發(fā)圖片加載:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
 imageURLPublisher.send(URL(string: "https://httpbin.org/image/jpeg")!)

 // RequestError.sessionError(error: Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made."
    imageURLPublisher.send(URL(string: "https://unknown.url/image")!) // 這個會走 Error 的分支,打印上述錯誤信息
}

運行一下項目劝术,點擊一下屏幕就可以看到控制臺對 image 的打印了缩多。

上下游操作發(fā)生異常

當操作流發(fā)生異常的時候,比如:網(wǎng)絡加載失敗养晋、JSON 字符串解析失敗等衬吆。我們可以通過下述的幾個函數(shù)來進行錯誤處理。

assertNoFailure(_:file:line:)

當上游發(fā)布者發(fā)生錯誤時绳泉,拋出異常逊抡。主要用在測試版本中,用來提前發(fā)現(xiàn)某些場景下的錯誤零酪。

代碼示例:

public enum SubjectError: Error {
 case genericSubjectError
}

let subject = PassthroughSubject<String, Error>()
cancel = subject
 .assertNoFailure()
 .sink(receiveCompletion: { print ("completion: \($0)") },
 receiveValue: { print ("value: \($0).") }
 )

subject.send("數(shù)據(jù)")
subject.send(completion: Subscribers.Completion<Error>.failure(SubjectError.genericSubjectError))

當 subject 第一次調(diào)用 send 會發(fā)送一個 "數(shù)據(jù)" 的字符串冒嫡,第二次調(diào)用則是發(fā)送了一個 SubjectError 類型的錯誤。

因為 subject 調(diào)用了 assertNoFailure() 函數(shù)四苇,所以孝凌,當?shù)诙握{(diào)用 send 的時候會直接拋出異常。

catch 和 replaceError

當在 Combine 流中發(fā)生錯誤時月腋,如果你想捕獲錯誤并且忽略它蟀架,可以使用 catch 操作符。該操作符可以在發(fā)生錯誤的時候榆骚,允許你返回一個默認值片拍。比如下面這兩種場景:

  • 當搜索結(jié)果發(fā)生錯誤時,返回一個空數(shù)組妓肢。
  • 當加載網(wǎng)絡圖片失敗時捌省,返回一個占位圖。 以下是加載網(wǎng)絡圖片失敗的示例代碼:
cancel = imageURLPublisher.flatMap { requestURL in
 ...與上文一致
}.map({ result -> UIImage? in
 return UIImage(data: result.data)
}).catch({ error -> Just<UIImage?> in
 return Just(notFoundImage)
}).sink(receiveValue: { image in
 print(image)
})

當 Combine 發(fā)生錯誤的時候职恳,也可以使用 replaceError(with:) 來進行處理所禀。它的作用是將錯誤替換成提供的值方面。當你想要通過發(fā)送單個替換元素來處理錯誤并結(jié)束流時是非常有用的。

它和 catch 的區(qū)別就是:完全忽略錯誤色徘,不會去關心錯誤信息恭金。而 catch 我們是可以根據(jù)錯誤信息來進行邏輯處理的。它的使用就是將上述的 catch 替換為 .replaceError(with: notFoundImage) 即可褂策。

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末横腿,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子斤寂,更是在濱河造成了極大的恐慌耿焊,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件遍搞,死亡現(xiàn)場離奇詭異罗侯,居然都是意外死亡,警方通過查閱死者的電腦和手機溪猿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門钩杰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人诊县,你說我怎么就攤上這事讲弄。” “怎么了依痊?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵避除,是天一觀的道長。 經(jīng)常有香客問我胸嘁,道長瓶摆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任缴渊,我火速辦了婚禮赏壹,結(jié)果婚禮上鱼炒,老公的妹妹穿的比我還像新娘衔沼。我一直安慰自己,他們只是感情好昔瞧,可當我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布指蚁。 她就那樣靜靜地躺著,像睡著了一般自晰。 火紅的嫁衣襯著肌膚如雪凝化。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天酬荞,我揣著相機與錄音搓劫,去河邊找鬼瞧哟。 笑死,一個胖子當著我的面吹牛枪向,可吹牛的內(nèi)容都是我干的勤揩。 我是一名探鬼主播,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼秘蛔,長吁一口氣:“原來是場噩夢啊……” “哼陨亡!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起深员,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤负蠕,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后倦畅,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體遮糖,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年叠赐,在試婚紗的時候發(fā)現(xiàn)自己被綠了止吁。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡燎悍,死狀恐怖敬惦,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情谈山,我是刑警寧澤俄删,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站奏路,受9級特大地震影響畴椰,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鸽粉,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一斜脂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧触机,春花似錦帚戳、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蔬胯,卻和暖如春对供,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背氛濒。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工产场, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留鹅髓,地道東北人。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓京景,卻偏偏與公主長得像迈勋,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子醋粟,可洞房花燭夜當晚...
    茶點故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內(nèi)容