2016-08-08 15 views
0

In meiner iOS-App habe ich zwei Firebase-bezogene Funktionen, die ich innerhalb von viewDidLoad() aufrufen möchte. Der erste wählt ein zufälliges Kind mit .queryOrderedByKey() aus und gibt den Schlüssel des Kindes als Zeichenfolge aus. Der zweite verwendet diesen Schlüssel und observeEventType, um untergeordnete Werte abzurufen und in einem dict zu speichern. Wenn ich diese Funktionen mit einer Schaltfläche in meiner Benutzeroberfläche auslösen, funktionieren sie wie erwartet.Die Swift-Funktion funktioniert in der App, aber nicht in der Override-Funktion viewDidLoad()

Allerdings, wenn ich beiden Funktionen innerhalb viewDidLoad() setzen, bekomme ich diesen Fehler:

Terminating app due to uncaught exception 'InvalidPathValidation', reason: '(child:) Must be a non-empty string and not contain '.' '#' '$' '[' or ']''

Die beanstandeten Codezeilen in meinem AppDelegate.swift sind, rot markiert:

class AppDelegate: UIResponder, UIApplicationDelegate, UITextFieldDelegate

Wenn ich die zweite Funktion auskommentieren und die erste innere viewDidLoad verlassen, wird die App geladen, und nachfolgende Aufrufe beider Funktionen (ausgelöst durch die Schaltfläche Aktion) funktionieren wie erwartet.

Ich habe eine Zeile am Ende der ersten Funktion des URL-Zeichenfolge zu drucken, und es hat keine anstößigen Zeichen: https://mydomain.firebaseio.com/myStuff/-KO_iaQNa-bIZpqe5xlg

ich auch eine Linie zwischen den Funktionen in viewDidLoad hinzugefügt Hard- Code die Zeichenfolge, und ich lief in der gleichen InvalidPathException Problem.

Hier mein viewDidLoad() Func ist:

override func viewDidLoad() { 
    super.viewDidLoad() 
    let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(ViewController.dismissKeyboard)) 
    view.addGestureRecognizer(tap) 
    pickRandomChild() 
    getChildValues() 
} 

Dies ist die erste Funktion:

func pickRandomChild() -> String { 
    var movieCount = 0 
    movieRef.queryOrderedByKey().observeEventType(.Value, withBlock: { (snapshot) in 
     for movie in snapshot.children { 
      let movies = movie as! FIRDataSnapshot 
      movieCount = Int(movies.childrenCount) 
      movieIDArray.append(movies.key) 
     } 
     repeat { 
      randomIndex = Int(arc4random_uniform(UInt32(movieCount))) 
     } while excludeIndex.contains(randomIndex) 
     movieToGuess = movieIDArray[randomIndex] 
     excludeIndex.append(randomIndex) 
     if excludeIndex.count == movieIDArray.count { 
      excludeIndex = [Int]() 
     } 
     let arrayLength = movieIDArray.count 

    }) 
    return movieToGuess 
} 

Hier ist die zweite Funktion:

func getChildValues() -> [String : AnyObject] { 
    let movieToGuessRef = movieRef.ref.child(movieToGuess) 
    movieToGuessRef.observeEventType(.Value, withBlock: { (snapshot) in 
     movieDict = snapshot.value as! [String : AnyObject] 
     var plot = movieDict["plot"] as! String 
     self.moviePlot.text = plot 
     movieValue = movieDict["points"] as! Int 
     }) 
    return movieDict 
) 

Und für eine gute Maßnahme, Hier ist der relevante Teil meiner AppDelegate.swift:

import UIKit 
import Firebase 

@UIApplicationMain 
class AppDelegate: UIResponder, UIApplicationDelegate, UITextFieldDelegate { 
    var window: UIWindow? 

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 
    FIRApp.configure() 
    return true 
} 

Ich rate Swift führt den Code nicht in der Reihenfolge, die ich erwarte. Wird Swift nicht automatisch auf die erste Funktion warten, bevor die zweite ausgeführt wird? Wenn dies der Fall ist, warum funktioniert dieses Pairing an anderer Stelle in der App, aber nicht in viewDidLoad?

Antwort

1

Edit: Das Problem ist, dass Schließungen nicht in Reihenfolge aufgerufen werden.

Ich bin nicht sicher, was Ihre pickRandomChild() - und getChildValues ​​() -Methoden sind, also bitte posten sie auch, aber die Art, wie ich dieses Problem behoben wurde, indem Sie die Daten durch eine Schließung, die in Ihrem aufgerufen werden kann ViewController.

Zum Beispiel, wenn ich Daten für einen vollen Namen und Industrie greifen wollte, benutzte ich dies. Diese Methode verwendet einen Firebase-Benutzer und enthält eine Schließung, die nach Abschluss aufgerufen wird. Dies wurde in einer Klasse speziell zum Ziehen von Daten definiert.

func grabDataDict(fromUser user: FIRUser, completion: (data: [String: String]) ->()) { 

    var myData = [String: String]() 

    let uid = user.uid 
    let ref = Constants.References.users.child(uid) 

    ref.observeEventType(.Value) { (snapshot, error) in 
     if error != nil { 
      ErrorHandling.defaultErrorHandler(NSError.init(coder: NSCoder())!) 
      return 
     } 

     let fullName = snapshot.value!["fullName"] as! String 
     let industry = snapshot.value!["industry"] as! String 

     myData["fullName"] = fullName 
     myData["industry"] = industry 

     completion(data: myData) 
    } 
} 

Dann I definierte ein leeres String-Array, in dem Viewcontroller und die Methode aufgerufen, um die Variable zu meinen Daten innerhalb des Verschlusses einstellen.

Wenn Sie Ihre Methoden veröffentlichen, jedoch kann ich Ihnen mit diesen speziell helfen.

Edit: Feste Methoden

1.

func pickRandomChild (completion: (movieToGuess: String) ->()) { 
    var movieCount = 0 
    movieRef.queryOrderedByKey().observeEventType(.Value, withBlock: { (snapshot) in 
     for movie in snapshot.children { 
      let movies = movie as! FIRDataSnapshot 
      movieCount = Int(movies.childrenCount) 
      movieIDArray.append(movies.key) 
     } 
     repeat { 
      randomIndex = Int(arc4random_uniform(UInt32(movieCount))) 
     } while excludeIndex.contains(randomIndex) 
     movieToGuess = movieIDArray[randomIndex] 
     excludeIndex.append(randomIndex) 
     if excludeIndex.count == movieIDArray.count { 
      excludeIndex = [Int]() 
     } 
     let arrayLength = movieIDArray.count 

     // Put whatever you want to return here. 
     completion(movieToGuess) 
    }) 
} 

2.

func getChildValues(completion: (movieDict: [String: AnyObject]) ->()) { 
    let movieToGuessRef = movieRef.ref.child(movieToGuess) 
    movieToGuessRef.observeEventType(.Value, withBlock: { (snapshot) in 
     movieDict = snapshot.value as! [String : AnyObject] 
     var plot = movieDict["plot"] as! String 
     self.moviePlot.text = plot 
     movieValue = movieDict["points"] as! Int 

     // Put whatever you want to return here. 
     completion(movieDict) 
    }) 
} 

diese Methoden in einigen Modellklasse definieren, und wenn Sie sie in Ihrem Viewcontroller anrufen, sollten Sie sein Sie können Ihre View-Controller-Variablen innerhalb jeder Schließung auf movieDict und movieToGuess setzen. Ich habe diese auf dem Spielplatz gemacht, also lassen Sie mich wissen, wenn Sie irgendwelche Fehler bekommen.

+0

Danke. Ich habe meine Frage mit den Funktionen selbst aktualisiert. – adsigel

+0

Ich habe Ihre geänderten Funktionen verwendet, und die einzige Änderung war ein Swift-Vorschlag zum Hinzufügen einer fehlenden Argumentbezeichnung zu "completion (movieToGuess)" und "completion (movieDict)" am Ende von beiden. Allerdings, wenn ich die Funktionen in viewDidLoad() aufrufen, bekomme ich den Fehler ** Kann nicht konvertiert Wert des Typs 'String' in den erwarteten Argumenttyp '(MovieToGuess: String) ->()'. ** Nichts, was ich versucht habe Dieser Fehler wurde behoben, daher wird die App nicht erstellt. – adsigel

+0

Rufen Sie meine Methode an. Und doppelklicken Sie auf alle möglichen Parameter im View-Controller. Sie erhalten eine Sperrung wie in Firebase, in der Ihre Daten enthalten sind. Du wirst also etwas wie self.myDict machen wollen (was eine Variable ist, die du im View-Controller erstellst) = myDict (die sich innerhalb des Closings befindet.) - Kopiere, wie ich es am Anfang meines Posts gemacht habe. – TrickSpades

1

Ihre Funktionen pickRandomChild() und getChildValues() sind asynchron, also nur sie zu einem späteren Zeitpunkt ausgeführt werden sollen, so dass, wenn getChildValues() das Ergebnis pickRandomChild() braucht, sollte es stattdessen in pickRandomChild() ‚s Abschluss-Handler/delegieren Rückruf aufgerufen werden, denn wenn einer von Diejenigen werden aufgerufen, es ist garantiert, dass die Funktion beendet ist.

Es funktioniert, wenn Sie die zweite Funktion Kommentar und es nur mit einem Tastendruck auslösen, weil es genügend Zeit zwischen der App Laden war und Sie auf die Schaltfläche für die asynchrone pickRandomChild() drückt es ganz Aktion auszuführen, getChildValues() ermöglicht die Verwendung sein zurückgegebener Wert für seine Anfrage.

+0

Ich bin nicht vertraut mit Firebase, um ehrlich zu sein, aber das ist typische Symptome von asynchronen Sachen, ich bin nicht sicher, was "observeEventType" wirklich tut, es scheint es sollte warten auf die 'pickRandomChild()' zu beenden, aber ich vorausgesetzt, dass es nicht tut – Fonix