Schnell! Protokollorientiert

    Hallo allerseits!
    Nein, dies ist kein weiterer Beitrag im Stil von "Meet Swift and its Capabilities", sondern ein kurzer Exkurs über praktische Anwendungen und Feinheiten, bei dem die protokollorientierte Natur der neuen Sprache von Apple es Ihnen ermöglicht, nette und praktische Dinge zu tun.
    Bild


    Ein besonderer Gruß an diejenigen, die unter den habr-kat schauten. In letzter Zeit musste ich viel auf "Swift" entwickeln, gleichzeitig ist viel Gepäck auf Objekt C und einige möchten einige Dinge mit Code ausdrücken, von dem ich verstanden habe, dass es viel einfacher und eleganter ist, sie in einer neuen Sprache zu implementieren.

    Vorsicht!: Der Artikel ist kein detailliertes Schulungsmaterial, sondern stellt praktische Punkte dar, die mich persönlich beeindruckt haben, und ich habe beschlossen, sie zu teilen. Wahrscheinlich werden sie den meisten Lesern bekannt vorkommen. Wer also etwas hinzufügen möchte, wird um Kommentare gebeten.

    Was erwartet Sie in diesem Artikel?


    • Ein paar einleitende Sätze (plus eine kleine nützliche Bibliothek)
    • Dekorieren Sie zusätzliches Klassenverhalten mit Extension (etwas Code)
    • Wir erstellen ein wiederverwendbares Element unter Verwendung des Protokolls und der Standardimplementierung (viel Code).
    • Protokolle und Aufzählung - es kann bequem sein (mittlerer Code)

    Was ist die Kraft von Protokollen?


    Erstens, da jeder den Protokollmechanismus kennt, kann er die mehrfache Vererbung verschiedener Protokolltypen durch ein einzelnes Objekt implementieren. Vererbung beschränkt uns auf die Tatsache, dass es in der Kette der Erben im n-ten Schritt unmöglich ist, ein neues Verhalten, das mit einem anderen Objekt gemeinsam ist, „einzugießen“ oder „hinzuzufügen“.
    Zweitens ist es in Swift möglich, eine Standardimplementierung (Standardimplementierung) für das angegebene Protokoll hinzuzufügen. Darüber hinaus kann das Protokoll abhängig von der Klasse oder dem Typ des Objekts, das es erbt, mehrere Standardimplementierungen aufweisen.
    Drittens kann das Protokoll vom Protokoll geerbt werden.
    Viertens können Protokolle nicht nur von Klassen (Class), sondern auch von Strukturen (Struct) und Aufzählungen (Enum) geerbt werden.
    Fünftens können Protokolle Eigenschaften hinzufügen.
    Sechstens können Sie eine Standardimplementierung für Systemprotokolle hinzufügen, und wenn Sie möchten, können Sie diese bereits in einer bestimmten Klasse überschreiben.
    Abschließend möchte ich hinzufügen, dass Sie mit den Protokollen Code in verschiedenen Klassen und Strukturen wiederverwendbar machen können. Sie können in ihnen allgemeine Aufgaben implementieren und sie als Dekoratoren mit den Dateien verbinden, in denen sie benötigt werden.
    Beispielsweise muss in jedem Projekt ein Klick auf eine UIView verarbeitet werden , damit Sie nicht jedes Mal zusätzlichen Code schreiben müssen, sondern eine eigene Tappable- Klasse erstellen (der Code ist hier )
    . Ich persönlich vermisse einige Konventionen bei der Vererbung des Protokolls, sodass die vererbten Methoden und Eigenschaften deutlich sichtbar sind (das habe ich gehört in Ruby):

    protocol FCActionProtocol {
        var actionButton: UIButton! {get set}
        func showActionView()
    }
    class FCController: FCActionProtocol {
        var actionButton: UIButton! // FCActionProtocol convenience
        func showActionView() {}
    }
    

    Ich möchte, dass actionButton und showActionView () im automatisch generierten Bereich ersetzt werden.
    Ich werde mit Swift 3.0 warten

    Dekorieren Sie zusätzliches Klassenverhalten mit Extension


    Von der Theorie zur Praxis: Lebensfall Nummer 1.
    Stellen Sie sich vor, wir haben eine Logik für den Ansichtszyklus des Controllers und eine Logik für die Übertragung des Modells in die Ansicht. Plötzlich haben wir eine neue Controller-Erweiterung, bei der wir die Logik zum Anzeigen des Mail-Clients anpassen müssen. Mit Protokollen ist das einfach:

    class MyViewController: UIViewController { 
    // a lot of code here 
    }
    extension MyViewController: MFMailComposeViewControllerDelegate {
        func showMailController() {
            let mailComposeViewController = configuredMailComposeViewController()
            if MFMailComposeViewController.canSendMail() {
                self.presentViewController(mailComposeViewController, animated: true, completion: nil)
            }
        }
        func configuredMailComposeViewController() -> MFMailComposeViewController {
            let controller = MFMailComposeViewController()
            controller.mailComposeDelegate = self
            return controller // customize and set it here
        }
        // MARK: - MFMailComposeViewControllerDelegate
        func mailComposeController(controller: MFMailComposeViewController, didFinishWithResult result: MFMailComposeResult, error: NSError?) {}
    }
    

    Es ist sehr erfreulich, dass Sie im Gegensatz zu obj-c in Swift in der Erweiterung der MyViewController- Klasse neue vererbbare Protokolle angeben und deren Verhalten implementieren können.

    Erstellen Sie ein wiederverwendbares Element mit dem Protokoll und der Standardimplementierung


    Fall Nr. 2: Kürzlich gab es in der Anwendung auf zwei Bildschirmen dieselbe Schaltfläche, die zum selben Szenario führte - das Aktionsblatt mit Aktionen, von denen eine den E-Mail-Client zeigte. Die technische Aufgabe bestand darin, ein Protokoll mit der Implementierung und der gesamten darin enthaltenen Logik zu implementieren, sodass der Komplexitätsgrad der Verbindung und der Abhängigkeiten minimal war. Hier ist der Code im Projekt:

    protocol FCActionProtocol {
        var actionButton: UIButton! {get set}
        var delegateHandler: FCActionProtocolDelegateHandler! {get set}
        mutating func showActionSheet()
        func showMailController()
    }
    class FCActionProtocolDelegateHandler : NSObject, MFMailComposeViewControllerDelegate {
        var delegate: FCActionProtocol!
        init(delegate: FCActionProtocol) {
            super.init()
            self.delegate = delegate
        }
        func mailComposeController(controller: MFMailComposeViewController, didFinishWithResult result: MFMailComposeResult, error: NSError?) {
            controller.dismissViewControllerAnimated(true, completion: nil)
        }
    }
    extension FCActionProtocol {
        mutating func showActionSheet() {
            delegateHandler = FCActionProtocolDelegateHandler(delegate: self)
            let actionController = UIAlertController(title: nil, message: nil, preferredStyle: .ActionSheet)
            actionController.addAction(UIAlertAction(title: NSLocalizedString("ActionClear", comment: ""), style: .Default) { (action) in })
            actionController.addAction(UIAlertAction(title: NSLocalizedString("ActionWriteBack", comment: ""), style: .Default) { (action) in
                self.showMailController()
            })
            if let controller = self as? UIViewController {
                controller.presentViewController(actionController, animated: true) {}
            }
        }
        func showMailController() {
            if MFMailComposeViewController.canSendMail() {
                let controller = MFMailComposeViewController()
                controller.mailComposeDelegate = delegateHandler
                (self as! UIViewController).navigationController!.presentViewController(controller, animated: true, completion: nil)
            }
        }
    }
    

    Achtung! Die Idee des Codes ist, dass es ein Protokoll FCActionProtocol gibt , das eine Schaltfläche ( actionButton ) enthält, mit der das Blatt mit den Aktionen ( showActionSheet ) angezeigt wird . Wenn Sie auf das Blattelement klicken, sollte der Mail-Client ( showMailController ) angezeigt werden . Um zu verhindern, dass die Logik und die Verarbeitung dieses Aufrufs in der Klasse implementiert werden, die unser Protokoll erbt, führen wir eine Standardimplementierung mit einer abstrakten Entität delegateHandler durch , die in unserer Erweiterung erstellt wird, und die Delegatmethoden des Mail-Clients werden von einer Instanz der Klasse FCActionProtocolDelegateHandler verarbeitet .

    Infolgedessen ist das Hinzufügen dieser wiederverwendbaren Aktionsliste wie folgt schwierig:

    class FCMyController: FCActionProtocol {
        var actionButton: UIButton! // convenience FCActionProtocol
        var delegateHandler: FCActionProtocolDelegateHandler! // convenience FCActionProtocol
    }
    

    Alle Logik im Inneren. Wir müssen nur eine Schaltfläche initialisieren und hinzufügen. Meiner Meinung nach ist es sehr schön und prägnant ausgefallen.

    Protokolle und Aufzählungen - das kann praktisch sein


    Life Case Nr. 3: Unser Team hat einen Online-Flugscheinservice durchgeführt. Der mobile Client kommuniziert eng mit dem Server und es gibt verschiedene Szenarien, in denen die API aufgerufen wird. Wir unterteilen sie unter Vorbehalt in Suche, Kartenreservierung und Bezahlung. Bei jedem dieser Prozesse kann ein Fehler auftreten (auf dem Server, dem Client, dem Kommunikationsprotokoll, der Datenüberprüfung usw.). Wenn sich bei der Buchung oder Suche der 500. vom Server noch immer keine Sorgen macht, gehen die Daten beispielsweise beim Bezahlen vom internen Server möglicherweise bereits zum Zahlungsgateway, und der Kunde kann nicht nur einen Fehler anzeigen, während sein Geld von der Bank abgebucht wird Karten.
    Mit den Protokollen kann ich hier sehr eleganten Code erstellen:

    protocol Critical {
        func criticalStatus() -> (critical: Bool, message: String)
    }
    enum Error {
        case Search(code: Int)
        case Booking(code: Int)
        case Payment(code: Int)
    }
    extension Error : Critical {
        func criticalStatus() -> (critical: Bool, message: String) {
            switch self {
            case .Payment(let code) where code == 500:
                return (true, "Please contact us, because your payment could proceed")
            default:
                return (false, "Something went wrong. Please try later.")
            }
        }
    }
    

    Jetzt ziehen wir unseren Code und bewerten, wie schlimm alles ist:

    let error = Error.Payment(code: 500)
    if error.criticalStatus().critical {
        print("callcenter will solve it")
    }
    

    Es ist schade, dass das Projekt in Wirklichkeit eine riesige Ziel-C-Ebene mit einer Reihe von Hacks für die Kompatibilität mit Swift darstellte.
    Ich hoffe, dass die folgenden Projekte mit allen Funktionen der Sprache umgesetzt werden können.

    PS: Hoffe, dass sich Anfänger für Swift und die Entwicklung von Protokollen interessieren. Vielleicht bemerkt jemand aus der Mitte selbst ein paar Tricks, die er nicht angewendet hat. Und die Senioren werden nicht kritisieren und in den Kommentaren ein paar ihrer Geheimnisse und Best Practices teilen. Danke an alle.

    Jetzt auch beliebt: