2016-06-07 8 views
1

Mit dem Zusatz von ErrorType zu Swift beibehalten wird, ist es nun möglich, Fehler und Fehlerereignisse in einem sauberen und präziser Art und Weise zum Ausdruck bringen. Wir sind nicht mehr als iOS-Entwickler an den alten Weg von NSError gebunden, klobig und schwer zu bedienen.Swift - Fehlerfortpflanzung während api Klarheit

ErrorType ist ideal für ein paar Gründe:

  • Allgemeine Parameter in Funktionen
  • Jede Enum anzupassen und umzusetzen
  • Arbeitet besser mit dem Fang/Wurf Paradigma

Es gibt jedoch einige Probleme und insbesondere ein Problem, mit dem ich in letzter Zeit konfrontiert bin, und ich bin gespannt, wie andere dies lösen.

Sagen Sie zum Beispiel, sind Sie verwandt mit Facebook eine Social-Networking-Anwendung erstellen, und Sie haben Group Modell. Wenn der Benutzer zuerst Ihre Anwendung lädt Sie zwei/drei Dinge tun:

  1. die entsprechenden Gruppen von Ihrem Server abrufen.
  2. Die bereits vorhandenen Gruppen auf dem Datenträger abrufen (Realm, CoreData usw.)
  3. Die lokale Kopie mit der gerade abgerufenen Remote-Kopie aktualisieren.

Während dieses Prozesses können Sie die Arten von Fehlern brechen in zwei unterschiedliche Kategorien: PersistenceError und NetworkError wo die ErrorType konformen Aufzählungen wie

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

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

Es gibt mehrere Möglichkeiten/Design Patterns können Sie Verwenden Sie für die Übermittlung von Fehlern. Die häufigste ist natürlich versuchen/fangen.

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

Da hier Funktionen, die sie werfen noch nicht angeben, welche Art von Fehler zu werfen, obwohl ich das wird sich ändern vermuten, Sie leicht eine NetworkError oder einen PersistenceError werfen.

Das Problem kommt, wenn eine allgemeinere oder funktionalen Ansatz, wie ReactiveCocoa oder belegt wird.

Dann wickeln die beiden Anrufe:

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) 
} 

Welche Fehlertyp in der Stelle geht ??? markiert? Ist es NetworkError oder PersistenceError? Sie können nicht ErrorType verwenden, da es nicht als konkreter verwendet werden kann, und wenn Sie es als generische Einschränkung versuchen verwenden, <E: ErrorType>, wird der Compiler noch beschweren sagen, es ist ein Argument Liste vom Typ erwartet E.

So wird das Problem, weniger mit try/catch und mehr mit funktionellen Ansätzen, wie eine Fehlerhierarchie Struktur zu pflegen, so dass Fehlerinformationen in verschiedenen ErrorType Conformances beibehalten werden können, immer noch mit diesem beschreibenden Fehler api.

Das Beste, was ich mit so weit kommen kann, ist:

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

    // Persistence Errors 
    case .FetchFailed 

    // More error types 
} 

die Enum im Wesentlichen ein langer Fehler ist so, dass sämtliche Fehler der gleichen Art sind und sogar der tiefste Fehler propagiert werden kann Kette.

Wie gehen andere Leute damit um? Ich genieße die Vorteile von einem universellen Fehler enum, aber die Lesbarkeit und api Klärung leiden. Ich würde lieber jede Funktion beschreiben, welche spezifischen Fehlercluster sie zurückgeben, anstatt jede Rückkehr Error, aber wieder sehe ich nicht, wie man das macht, ohne Fehlerinformationen auf dem Weg zu verlieren.

Antwort

1

Wenn Sie nur versuchen, eine Lösung für Ihr Problem, möglicherweise nicht perfekt. Verwenden Sie das Protokoll, um die Fehlerobjekte einfach zu umgehen:

//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

Das ist sicherlich eine gute Lösung. Leider leidet es immer noch unter dem gleichen Problem, dass jede Funktion oder Methode, die einen Fehlertyp zurückgibt, immer vom Typ "Error" ist. – barndog

+0

Richtig, aber der Typ ist Error, der eigentlich ein Unterklassenobjekt mit Basisklassenzeiger ist. Und Sie können dynamisches Verhalten basierend auf der Unterklassen-Implementierung der Protokollmethode erhalten. Möglicherweise bekomme ich den Kontext nicht genau, aber Sie können das Verhalten auch verbessern, indem Sie dem ErrorType eine Erweiterungsmethode hinzufügen und diese nahtlos mit einem der benutzerdefinierten Unterklasse-Typen verwenden. – Tushar

Verwandte Themen