ios realtime Firebase Swift 3 Database stürzt bei setValue withCompletionBlock ab




swift firebase database (2)

tl; dr: Firebase stellt einen setValue(_ value: Any?, andPriority priority: Any?) der bei Verwendung einer setValue(_ value: Any?, withCompletionBlock: (Error?, FIRDatabaseReference) -> Void) Closure mit setValue(_ value: Any?, withCompletionBlock: (Error?, FIRDatabaseReference) -> Void) setValue(_ value: Any?, andPriority priority: Any?) korrekt übereinstimmt setValue(_ value: Any?, withCompletionBlock: (Error?, FIRDatabaseReference) -> Void) .

Lösung : Vermeiden Sie bei Verwendung einer API mit vielen Varianten die Verwendung von abschließenden Schließungen. In diesem Fall bevorzugen Sie setValue(myValue, withCompletionBlock: { (error, dbref) in /* ... */ }) ; Verwenden setValue(myValue) { (error, dbref) in /* ... */ } nicht setValue(myValue) { (error, dbref) in /* ... */ } .

Erläuterung

Dies scheint ein Swift-Bug zu sein. Wie in anderen Sprachen, wie Java, wählt Swift im Allgemeinen die spezifischste Überladung. Z.B,

class Alpha {}
class Beta : Alpha {}

class Charlie {
    func charlie(a: Alpha) {
        print("\(#function)Alpha")
    }
    func charlie(a: Beta) {
        print("\(#function)Beta")
    }
}

Charlie().charlie(a: Alpha()) // outputs: charlie(a:)Alpha
Charlie().charlie(a: Beta() as Alpha) // outputs: charlie(a:)Alpha
Charlie().charlie(a: Beta()) // outputs: charlie(a:)Beta

Wenn jedoch überladene Funktionen einem abschließenden Abschluss entsprechen, wählt Swift (zumindest manchmal) den allgemeineren Typ. Z.B,

class Foo {
    func foo(completion: () -> Void) {
        print(#function)
    }
    func foo(any: Any?) {
        print(#function)
    }
}

func bar() {}
Foo().foo(completion: bar) // outputs: foo(completion:)
Foo().foo(any: bar) // outputs: foo(any:)
Foo().foo() { () in } // outputs: foo(any:)
// ^---- Here lies the problem
// Foo().foo(bar) will not compile; can't choose between overrides.

Any? ist ein allgemeinerer Typ als () -> Void - dh "alles, sogar null" ist breiter als "eine Funktion, die 0 Parameter empfängt und etwas vom Typ Void ". Der abschließende Abschluss entspricht Any? ; Dies ist das Gegenteil von dem, was Sie von einer Sprache erwarten würden, die dem spezifischsten Typ entspricht.

Ich verwende Firebase auf iOS mit Swift 3.

Wenn ich benutze

FIRDatabase.database().reference().child("child").setValue("value") { 
  (error: Error?, databaseReference: FIRDatabaseReference) in
    print("Error while setting value \(error)")
}   

Die App stürzt zur Laufzeit mit dem folgenden Protokoll ab:

*** Beenden der App aufgrund der nicht abgefangenen Ausnahme 'InvalidFirebaseData', Grund: '(nodeFrom: priority :) Objekt des Typs _SwiftValue kann nicht unter. Kann nur Objekte vom Typ NSNumber, NSString, NSDictionary und NSArray speichern. '

Ich habe versucht, die gleiche Funktion zu verwenden, aber ohne die abschließende Schließung und aus irgendeinem Grund funktioniert es!

FIRDatabase.database().reference().child("child").setValue("value", 
  withCompletionBlock: { 
    (error: Error?, databaseReference: FIRDatabaseReference) in
      print("Error while setting value \(error)")
})

Gibt es etwas Besonderes an Trailing Closures und Swift 3?


Während es eine akzeptierte Antwort gibt, die einige Klarheit liefert, erklärt es, dass es ein Swift Bug ist, ist nicht wirklich genau. Abgesehen davon ist die Erklärung zutreffend, aber nicht für dieses Problem.

Es ist der wahre Fehler, die Schließung zu setValue hinzufügen zu lassen.

Eine genauere Antwort ist, dass es für die setValue-Funktion keinen Abschlussblock / Abschluss gibt, weshalb dies fehlschlägt.

Die spezifische Funktion -setValue: hat KEINE Schließung, weshalb sie abstürzt. dh es ist eine falsche Implementierung in Ihrem Code. Pro die Dokumente:

func setValue(_ value: Any?)

Beachten Sie, dass die setValue-Funktion KEINE Schließung hat und wenn Sie eine hinzufügen, wird die Funktion keine Ahnung haben, was mit diesen Daten zu tun ist.

Um einen Abschlussblock / Abschluss zu verwenden, müssen Sie die richtige Funktion aufrufen, die lautet

-setValue:withCompletionBlock:

Die untere Zeile ist, dass Sie einen Parameter oder Aufruf nicht zufällig zu einer Funktion hinzufügen können, die nicht dafür ausgelegt ist.

Dies ist offensichtlich nicht gültig, aber konzeptionell ist es der gleiche Fehler.

    let a = "myString"
    a.append("x") { (error: Error?) }

In diesem Fall weiß der Compiler, dass die Funktion string.append keine Closure-Option hat und sie vor dem Kompilieren abfängt.

Also geh ein bisschen weiter, dieser Code stimmt überein & läuft aber erzeugt auch einen Fehler

ref.child("child").setValue("value") { }

Auch hier hat setValue keine Schließung, daher ist dieser Code nicht korrekt implementiert.

Um zu klären, eine Klasse gegeben

class MyClass {
    var s = ""
    var compHandler = {}

    func setValue(aString: String) {
        self.s = aString
    }

    func setValue(aString: String, someCompletionHandler: completionHandler) {
        self.s = aString
        self.compHandler = someCompletionHandler
    }
}

Beachten Sie, dass setValue: aString eine völlig andere Funktion als setValue ist: aString: someCompletionHandler

Der einzige Parameter, der auf setValue basieren kann: aString ist ein String als erster und einziger Parameter.

Der setValue: aString: someCompletionHandler wird zwei Parameter übergeben, ein String an der ersten Position und ein completionHandler an der zweiten Position.

Der tatsächliche Vervollständigungsblock ist der zweite übergebene Parameter.

param1, param2 ------, --------------- Zeichenfolge, completionBlock

Deshalb

setValue(value) {}

ist falsch formatiert, während

setValue(value, withBlock: {})

ist richtig formatiert.





firebase-database