简体   繁体   English

Swift函数可在应用程序中工作,但不能在覆盖功能的viewDidLoad()中使用

[英]Swift function works in app but not within override func viewDidLoad()

In my iOS app, I have two Firebase-related functions that I want to call within viewDidLoad(). 在我的iOS应用中,我想在viewDidLoad()中调用两个与Firebase相关的函数。 The first picks a random child with .queryOrderedByKey() and outputs the child's key as a string. 第一个使用.queryOrderedByKey()选择一个随机子级, .queryOrderedByKey()该子级的键输出为字符串。 The second uses that key and observeEventType to retrieve child values and store it in a dict. 第二个使用该键和observeEventType来检索子值并将其存储在字典中。 When I trigger these functions with a button in my UI, they work as expected. 当我使用UI中的按钮触发这些功能时,它们将按预期工作。

However, when I put both functions inside viewDidLoad(), I get this error: 但是,当我将两个函数都放在viewDidLoad()中时,出现此错误:

Terminating app due to uncaught exception 'InvalidPathValidation', reason: '(child:) Must be a non-empty string and not contain '.' 由于未捕获的异常'InvalidPathValidation'而终止应用程序,原因:'((child :)必须为非空字符串且不包含'。' '#' '$' '[' or ']'' '#''$''['或']''

The offending line of code is in my AppDelegate.swift, highlighted in red: 令人反感的代码行在我的AppDelegate.swift中,以红色突出显示:

class AppDelegate: UIResponder, UIApplicationDelegate, UITextFieldDelegate

When I comment out the second function and leave the first inside viewDidLoad, the app loads fine, and subsequent calls of both functions (triggered by the button action) work as expected. 当我注释掉第二个函数并将第一个函数留在viewDidLoad内时,应用程序加载正常,随后两个函数的调用(通过按钮动作触发)均按预期工作。

I added a line at the end of the first function to print out the URL string, and it doesn't have any offending characters: https://mydomain.firebaseio.com/myStuff/-KO_iaQNa-bIZpqe5xlg 我在第一个函数的末尾添加了一行以打印出URL字符串,它没有任何令人反感的字符: https://mydomain.firebaseio.com/myStuff/-KO_iaQNa-bIZpqe5xlg : https://mydomain.firebaseio.com/myStuff/-KO_iaQNa-bIZpqe5xlg

I also added a line between the functions in viewDidLoad to hard-code the string, and I ran into the same InvalidPathException issue. 我还在viewDidLoad中的函数之间添加了一条线,以对字符串进行硬编码,并且遇到了相同的InvalidPathException问题。

Here is my viewDidLoad() func: 这是我的viewDidLoad()函数:

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

Here is the first function: 这是第一个功能:

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
}

Here is the second function: 这是第二个功能:

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
)

And for good measure, here's the relevant portion of my AppDelegate.swift: 好的,这是我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
}

I'm guessing Swift is executing the code not in the order I expect. 我猜想Swift正在按照我期望的顺序执行代码。 Does Swift not automatically wait for the first function to finish before running the second? Swift是否在运行第二个函数之前不会自动等待第一个函数完成? If that's the case, why does this pairing work elsewhere in the app but not in viewDidLoad? 如果是这样,为什么这种配对在应用程序中的其他位置起作用,而在viewDidLoad中不起作用?

Your functions pickRandomChild() and getChildValues() are asynchronous, therefore they only get executed at a later stage, so if getChildValues() needs the result of pickRandomChild() , it should be called in pickRandomChild() 's completion handler / delegate callback instead, because when one of those are called it is guaranteed that the function has finished. 您的函数pickRandomChild()getChildValues()是异步的,因此它们仅在以后的阶段执行,因此,如果getChildValues()需要pickRandomChild()的结果, pickRandomChild()pickRandomChild()的完成处理程序/委托回调中调用它相反,因为当调用其中之一时,可以确保函数已完成。

It works when you comment out the second function and only trigger it with a button press because there has been enough time between the app loading and you pushing the button for the asynchronous pickRandomChild() to perform it action entirely, allowing getChildValues() to use its returned value for its request. 当您注释掉第二个函数并且仅在按下按钮时触发它,因为在应用程序加载和您按下按钮之间有足够的时间让异步pickRandomChild()完全执行它的操作时,它才pickRandomChild() ,从而允许getChildValues()使用其请求的返回值。

Edit: The issue is that closures are not called in order. 编辑:问题是没有按顺序调用闭包。

I'm not sure what your pickRandomChild() and getChildValues() methods are, so please post them as well, but the way I fixed this type issue was by sending the data through a closure that can be called in your ViewController. 我不确定您的pickRandomChild()和getChildValues()方法是什么,因此也请发布它们,但是我解决此类型问题的方法是通过可在ViewController中调用的闭包发送数据。

For example when I wanted to grab data for a Full Name and Industry I used this. 例如,当我想获取全名和行业的数据时,就使用了此名称。 This method takes a Firebase User, and contains a closure that will be called upon completion. 此方法使用Firebase用户,并包含一个在完成时将调用的闭包。 This was defined in a class specifically for pulling data. 这是在专门用于提取数据的类中定义的。

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

Then I defined an empty array of strings in the Viewcontroller and called the method, setting the variable to my data inside the closure. 然后,我在Viewcontroller中定义了一个空字符串数组,并调用了该方法,将变量设置为闭包内部的数据。

messages.grabRecentSenderIds(fromUser: currentUser!) { (userIds) in
        self.userIds = userIds
        print(self.userIds)
}

If you post your methods, however I can help you with those specifically. 如果您发布方法,不过我可以为您提供具体帮助。

Edit: Fixed Methods 编辑:固定方法

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

Define these methods in some model class, and when you call them in your viewcontroller, you should be able to set your View Controller variables to movieDict and movieToGuess inside each closure. 在某个模型类中定义这些方法,然后在viewcontroller中调用它们时,应该能够在每个闭包内将View Controller变量设置为movieDict和movieToGuess。 I made these in playground, so let me know if you get any errors. 我是在操场上制作的,所以如果您遇到任何错误,请告诉我。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM