老房东
老房东的纸条箱

老房东的纸条箱

FireBase for SwiftUI小纸条(三)

Photo by Road Trip with Raj on Unsplash

FireBase for SwiftUI小纸条(三)

在SwiftUI中获取Realtime Database的数据

老房东's photo
老房东
·Aug 22, 2022·

2 min read

Subscribe to my newsletter and never miss my upcoming articles

Table of contents

  • 获取Realtime Database数据

如果你已经创建好了Firebase和Xcode项目,并且也已经将Realtime Database相关设置完成。接下来,我们开始尝试做从Realtime Database获取数据的操作。

获取Realtime Database数据

一次性读取一个值

为了进行读取操作,我们需要准备一个View和一个ViewModel。首先是我们的ViewModel:

import Foundation
import FirebaseDatabase
import FirebaseDatabaseSwift

class ReadViewModel: ObservableObject{
    var ref = Database.database().reference()

    @Published var value: String? = nil

    func readVale(){
        ref.child("game1").observeSingleEvent(of: .value) { snapshot in
            self.value = snapshot.value as? String
        }
    }
}

再准备一个View来显示它的内容:

import SwiftUI

struct ReadView: View {
    @StateObject var vm = ReadViewModel()

    var body: some View {
        VStack{
            if vm.value == nil {
                Text("The place will display value...")
                    .padding()
                    .background(.gray)
            }else{
                Text(vm.value!)
                    .padding()
                    .background(.gray)
            }

            Button{
                vm.readVale()
            }label: {
                Text("Read")
            }
            .buttonStyle(.borderedProminent)

        }
    }
}

这个view启动时会是这样的:

image.png

运行起来,会发现界面没有任何,变化,原因很简单,你需要在服务器控制台,加一个子节点,名叫game1,这就是ViewModel中ref.child("game1")会指向的子节点,给它增加任何值:

image.png

运行起我们的View,我们会发现每点击一下Read按钮,它就会去读取一次数值,当服务器更新后,再点一次Read,就会再读取一次数据:

readValue.gif

observeSingleEvent(of: .value)在做什么呢?用官方的原话,它对于只需加载一次且预计不会频繁变化或不需要主动侦听的数据能立即返回本地缓存中的值,而不必检查服务器上更新后的值。

观察数值变化

如果我们想让value的内容能实时被同步下来,Realtime Database也提供了非常方便的方法,我们只需要使用以下代码替代readValue函数的内容:

        ref.child("game1").observe(.value) { snapshot in
            self.value = snapshot.value as? String ?? "Load failed"
        }

我们就得到了一个实时同步版本:

observerValueChange.gif

这时所使用的.boserve(.value)方法会在server上数值发生变化时,使用snapshot做为参数同步执行指定的匿名函数。

读取一个对象

我们在FireBase for SwiftUI小纸条(二)- 在SwiftUI中使用Realtime Database写入数据中我们写入过一个叫User的对象,但是之前它只加入了Encodable,我们现在要为它加入遵循Decodable的协议:

class User: Encodable,Decodable{
    var username: String = ""
    var level: Int = 1
}

接下来,为ReadViewModel加入一个属性和一个方法:

    @Published var object : User? = nil

    func readObject(){
        ref.child("game2").observe(.value) { snapshot in
            do{
                self.object = try snapshot.data(as: User.self)
            }catch{
                print("Can't convert to User object")
            }
        }
    }

所以我们将会使用game2子节点的内容来还原到object中去,而这个object是User的一个实例。所以我们在数据库中加入game2子节点,并加入username和level属性:

image.png

为了显示一个Object,我们将ReadView也进行一些改进:

import SwiftUI

struct ReadView: View {
    @StateObject var vm = ReadViewModel()

    var body: some View {
        VStack{
            if vm.value == nil {
                Text("The place will display value...")
                    .padding()
                    .background(.gray)
            }else{
                Text(vm.value!)
                    .padding()
                    .background(.gray)
            }

            Button{
                vm.observerValueChange()
            }label: {
                Text("Read")
            }
            .buttonStyle(.borderedProminent)
            if vm.object == nil {
                Text("The place will display object...")
                    .padding()
                    .background(.gray)
            }else{
                Text("username:\(vm.object!.username)\nlevel:\(vm.object!.level)")
                    .padding()
                    .background(.gray)
            }

            Button{
                vm.readObject()
            }label: {
                Text("Read")
            }
            .buttonStyle(.borderedProminent)
        }
    }
}

再来试试结果:

image.png

读取一组对象

Realtime Database也提供了读取一组对象的方法,甚至你也可以用侦听的方式来读取这一组对象:

    func readListObject(){
        ref.observe(.value){parentSnapshot in
            guard let children = parentSnapshot.children.allObjects as? [DataSnapshot] else{
                return
            }

            self .listObject = children.map({ snapshot in
                return try! snapshot.data(as: User.self)
            })
        }
    }

Did you find this article valuable?

Support 老房东 by becoming a sponsor. Any amount is appreciated!

See recent sponsors Learn more about Hashnode Sponsors
 
Share this