2016-06-07 29 views
1

在Swift中添加ErrorType後,現在可以用更簡潔,更簡潔的方式表示錯誤和失敗事件。我們不再像iOS開發者那樣受到NSError的老方法的束縛,很難使用。Swift - 錯誤傳播,同時保持api清晰度

  • 函數通用參數
  • 任何enum能夠符合並實施
  • 與閉鎖/扔範式

工作更好:

ErrorType的幾個原因是偉大的然而,最近我遇到了一些問題和特別的問題,我很好奇看到其他人如何解決這個問題。

舉例來說,您將構建一個類似於Facebook的社交網絡應用程序,並且您有Group模型。當用戶第一次加載你的應用程序時,你想做兩三件事:

  1. 從你的服務器獲取相關組。
  2. 獲取磁盤上已存在的組(Realm,CoreData等)
  3. 使用剛剛獲取的遠程副本更新本地副本。

在這個過程中,你可以打破的錯誤類型分爲兩個不同的類別:PersistenceErrorNetworkError其中ErrorType符合枚舉可能看起來像

enum PersistenceError: ErrorType { 
    case CreateFailed([String: AnyObject) 
    case FetchFailed(NSPredicate?) 
} 

enum NetworkError: ErrorType { 
    case GetFailed(AnyObject.Type) // where AnyObject is your Group model class 
} 

有幾種方法/設計模式,你可以用於傳遞錯誤。當然最常見的是try/catch。

func someFunc() throws { 
    throw .GetFailed(Group.self) 
} 

這裏,因爲拋出還不能指定他們拋出什麼類型的錯誤,但我懷疑,這將改變功能,你可以很容易地拋出一個NetworkErrorPersistenceError

使用更通用或功能性方法(例如ReactiveCocoa或Result)時會遇到麻煩。

然後包裹在兩個電話:

func fetch() -> SignalProducer<Group, ???> { 
    let remoteProducer = self.fetchGroupsFromRemote() 
    .flatMap(.Concat)) { self.saveGroupsToLocal($0) // returns SignalProducer<[Group], PersistenceError> } 
    let localProducer = self.fetchGroupsFromLocal() 
    return SignalProducer(values: [localProducer, remoteProducer]).flatten(.Merge) 
} 

在現場中有哪些錯誤類型標誌着????是NetworkError還是PersistenceError?您不能使用ErrorType,因爲它不能用作具體類型,如果您嘗試將其用作通用約束,<E: ErrorType>,編譯器仍會抱怨說它需要E類型的參數列表。

所以這個問題變得不那麼嚴重,對於try/catch更是如此,對於函數式方法更是如此,如何維護一個錯誤層次結構,以便錯誤信息可以保留在不同的ErrorType一致性中,同時仍然存在描述性錯誤api。

我能想出迄今最好的是:

enum Error: ErrorType { 
    // Network Errors 
    case .GetFailed 

    // Persistence Errors 
    case .FetchFailed 

    // More error types 
} 

這基本上是一個長的錯誤枚舉使任何和所有的錯誤都是同一類型的,甚至最深的錯誤可以傳播了鏈。

其他人如何處理這個問題?我喜歡有一個通用錯誤枚舉的好處,但可讀性和api澄清受到影響。我寧願讓每個函數描述它們返回的具體錯誤集羣,而不是每個函數都返回Error,但是我再也看不到如何在不丟失錯誤信息的情況下如何做到這一點。

回答

1

只是嘗試解決您的問題,可能不是一個完美的。使用協議來輕鬆傳遞錯誤對象:

//1. 
protocol Error { 
    func errorDescription() 
} 

//2. 
enum PersistenceError: ErrorType, Error { 
    case CreateFailed([String: AnyObject) 
    case FetchFailed(NSPredicate?) 

    func errorDescription() { 

    } 
} 

//3. 
enum NetworkError: ErrorType, Error { 
    case GetFailed(AnyObject.Type) // where AnyObject is your Group model class 
    func errorDescription() { 

    } 
} 

//5. 
func fetch() -> SignalProducer<Group, Error> { 
    ... 
} 
+0

這當然是一個很好的解決方案。不幸的是,它仍然存在同樣的問題,即任何返回錯誤類型的函數或方法都將是'Error'類型。 – barndog

+0

正確,但即使類型是Error,它實際上是具有baseclass指針的子類對象。您可以根據協議方法的子類型實現獲取動態行爲。可能是我沒有準確地獲取上下文,但是您還可以通過向ErrorType添加擴展方法來改善行爲,並將其與任何自定義子類類型無縫地使用。 – Tushar