【问题标题】:Swift function works in app but not within override func viewDidLoad()Swift 函数在应用程序中有效,但在覆盖函数 viewDidLoad() 中无效
【发布时间】:2016-08-08 02:24:07
【问题描述】:

在我的 iOS 应用程序中,我有两个与 Firebase 相关的函数,我想在 viewDidLoad() 中调用它们。第一个选择一个带有.queryOrderedByKey() 的随机孩子并将孩子的密钥作为字符串输出。第二个使用该键和observeEventType 检索子值并将其存储在字典中。当我使用 UI 中的按钮触发这些功能时,它们会按预期工作。

但是,当我将这两个函数都放在 viewDidLoad() 中时,我得到了这个错误:

由于未捕获的异常“InvalidPathValidation”而终止应用程序,原因:“(子:)必须是非空字符串且不包含“。” '#' '$' '[' 或 ']''

有问题的代码行在我的 AppDelegate.swift 中,以红色突出显示:

class AppDelegate: UIResponder, UIApplicationDelegate, UITextFieldDelegate

当我注释掉第二个函数并将第一个函数留在 viewDidLoad 中时,应用程序加载正常,随后对两个函数的调用(由按钮操作触发)按预期工作。

我在第一个函数的末尾添加了一行打印出 URL 字符串,它没有任何违规字符:https://mydomain.firebaseio.com/myStuff/-KO_iaQNa-bIZpqe5xlg

我还在 viewDidLoad 中的函数之间添加了一条线来对字符串进行硬编码,但我遇到了同样的 InvalidPathException 问题。

这是我的 viewDidLoad() 函数:

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

这是第一个函数:

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
}

这是第二个功能:

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
)

为了更好地衡量,这是我的 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
}

我猜 Swift 执行的代码不是我期望的顺序。 Swift 在运行第二个函数之前不会自动等待第一个函数完成吗?如果是这样,为什么这种配对在应用程序的其他地方有效,但在 viewDidLoad 中无效?

【问题讨论】:

    标签: ios swift firebase firebase-realtime-database


    【解决方案1】:

    编辑:问题是没有按顺序调用闭包。

    我不确定您的 pickRandomChild() 和 getChildValues() 方法是什么,所以也请发布它们,但我解决此类型问题的方法是通过可以在 ViewController 中调用的闭包发送数据.

    例如,当我想获取全名和行业的数据时,我使用了这个。此方法接受一个 Firebase 用户,并包含一个将在完成时调用的闭包。这是在一个专门用于提取数据的类中定义的。

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

    然后我在 Viewcontroller 中定义了一个空字符串数组并调用该方法,将变量设置为闭包内的数据。

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

    如果您发布您的方法,但我可以专门为您提供帮助。

    编辑:固定方法

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

    在某个模型类中定义这些方法,当您在视图控制器中调用它们时,您应该能够在每个闭包内将视图控制器变量设置为movieDict 和movieToGuess。我是在操场上做的,所以如果你有任何错误,请告诉我。

    【讨论】:

    • 谢谢。我已经用函数本身更新了我的问题。
    • 我使用了您修改后的函数,唯一的变化是 Swift 建议在 completion(movieToGuess)completion(movieDict) 的末尾添加一个缺少的参数标签。但是,当我调用 viewDidLoad() 中的函数时,我收到错误 无法将“String”类型的值转换为预期的参数类型“(movieToGuess: String) -> ()”。 我什么都没有已尝试消除此错误,因此无法构建应用程序。
    • 调用我的方法。并双击视图控制器中所有可能的参数。您将获得一个类似于 Firebase 中的闭包,其中包含您的数据。所以你会想做一些类似 self.myDict (这是你在视图控制器中创建的变量)= myDict (它在闭包内。) - 复制我在帖子开头的做法。
    • 谢谢!再加上this thread,我得到了我需要的东西。
    【解决方案2】:

    您的函数pickRandomChild()getChildValues() 是异步的,因此它们只会在稍后阶段执行,因此如果getChildValues() 需要pickRandomChild() 的结果,则应在pickRandomChild() 的完成处理程序中调用它/ 委托回调,因为当其中一个被调用时,可以保证函数已经完成。

    当您注释掉第二个功能并且仅通过按下按钮触发它时它起作用,因为在应用程序加载和您按下按钮之间有足够的时间让异步pickRandomChild() 完全执行它的操作,允许@987654327 @ 将其返回值用于其请求。

    【讨论】:

    • 老实说,我不熟悉 firebase,但这是异步事物的典型症状,我不确定 observeEventType 到底做了什么,似乎应该让它等待 pickRandomChild() 完成,但我假设它没有这样做
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多