iOS 14小部件+ SwiftUI + Firebase? [英] iOS 14 Widgets + SwiftUI + Firebase?

查看:208
本文介绍了iOS 14小部件+ SwiftUI + Firebase?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我还是SwiftUI和Firebase的新手.最近,作为一种爱好,我一直在为我的学校开发一个应用程序. Xcode 12发布后,我决定尝试使用诸如Widgets的新功能.但是,由于我的应用程序是从Firebase获取数据的,所以我一直遇到一些问题.我最近的问题是此线程1"无法获取FirebaseApp实例.使用Firestore之前,请先致电FirebaseApp.configure().我不完全确定将"FirebaseApp.configure()"放在哪里.因为该小部件没有AppDelegate.swift.我的代码在下面.

I'm still pretty new to SwiftUI and Firebase. Recently, as a hobby, I have been developing an app for my school. After the launch of Xcode 12, I decided to experiment with the new features such as Widgets. However, since my app gets its data from Firebase, I've been having some problems. My most recent problem is this "Thread 1: "Failed to get FirebaseApp instance. Please call FirebaseApp.configure() before using Firestore". I'm not entirely sure where to put "FirebaseApp.configure()" as there is no AppDelegate.swift for the widget. My code is below.

我已经重新排列了代码,以便现在可以从原始iOS应用程序数据模型中获取数据.因此,我没有在小部件Swift文件中导入Firebase.但是,我仍然遇到相同的错误("SendProcessControlEvent:toPid:遇到错误:Error Domain = com.apple.dt.deviceprocesscontrolservice Code = 8"和-> 0x7fff5bb6933a< + 10> ;: jae 0x7fff5bb69344;< ; + 20>-线程1:无法获取FirebaseApp实例.请在使用Firestore之前调用FirebaseApp.configure()".我还包含了@Wendy Liga的代码,但仍然出现相同的错误.我的新代码如下:

I've rearranged my code so that I am now getting the data from the original iOS app data model. I am therefore not importing Firebase within the widgets Swift file. However, I still get the same error ("SendProcessControlEvent:toPid: encountered an error: Error Domain=com.apple.dt.deviceprocesscontrolservice Code=8" and "-> 0x7fff5bb6933a <+10>: jae 0x7fff5bb69344 ; <+20> - Thread 1: "Failed to get FirebaseApp instance. Please call FirebaseApp.configure() before using Firestore""). I've also included @Wendy Liga's code, but I still got the same error. My newer code is below :

iOS App数据模型

iOS App Data Model

import Foundation
import SwiftUI
import Firebase
import FirebaseFirestore

struct Assessment: Identifiable {
    var id:String = UUID().uuidString
    var Subject:String
    var Class:Array<String>
    var Day:Int
    var Month:String
    var Title:String
    var Description:String
    var Link:String
    var Crit:Array<String>
}

class AssessmentsViewModel:ObservableObject {
    @Published var books = [Assessment]()
    
    private var db = Firestore.firestore()
    
    // Add assessment variables
    @Published var AssessmentSubject:String = ""
    //@Published var AssessmentClass:Array<String> = [""]
    @Published var AssessmentDay:Int = 1
    @Published var AssessmentMonth:String = "Jan"
    @Published var AssessmentTitle:String = ""
    @Published var AssessmentDescription:String = ""
    @Published var AssessmentLink:String = ""
    @Published var AssessmentCrit:Array<String> = [""]
    @Published var AssessmentDate:Date = Date()
    
    func fetchData() {
        db.collection("AssessmentsTest").order(by: "date").addSnapshotListener { (QuerySnapshot, error) in
            guard let documents = QuerySnapshot?.documents else {
                print("No documents")
                return
            }
            
            self.books = documents.map { (QueryDocumentSnapshot) -> Assessment in
                let data = QueryDocumentSnapshot.data()
                
                let Subject = data["subject"] as? String ?? ""
                let Class = data["class"] as? Array<String> ?? [""]
                let Day = data["day"] as? Int ?? 0
                let Month = data["month"] as? String ?? ""
                let Title = data["title"] as? String ?? ""
                let Description = data["description"] as? String ?? ""
                let Link = data["link"] as? String ?? ""
                let Crit = data["crit"] as? Array<String> ?? [""]
                
                return Assessment(Subject: Subject, Class: Class, Day: Day, Month: Month, Title: Title, Description: Description, Link: Link, Crit: Crit)
            }
        }
    }
    
    func writeData() {
        let DateConversion = DateFormatter()
        DateConversion.dateFormat = "DD MMMM YYYY"
        let Timestamp = DateConversion.date(from: "20 June 2020")
        
        db.collection("AssessmentsTest").document(UUID().uuidString).setData([
            "subject": AssessmentSubject,
            "month": AssessmentMonth,
            "day": AssessmentDay,
            "title": AssessmentTitle,
            "description": AssessmentDescription,
            "link": AssessmentLink,
            "crit": AssessmentCrit,
            "date": AssessmentDate
        ]) { err in
            if let err = err {
                print("Error writing document: \(err)")
            } else {
                print("Document successfully written!")
            }
        }
    }
}

小部件视图

struct WidgetsMainView: View {
    
    @ObservedObject private var viewModel = AssessmentsViewModel()
    
    var body: some View {
        HStack {
            Spacer().frame(width: 10)
            VStack(alignment: .leading) {
                Spacer().frame(height: 10)
                
                ForEach(self.viewModel.books) { Data in
                    HStack {
                        VStack {
                            Text(String(Data.Day))
                                .bold()
                                .font(.system(size: 25))
                            Text(Data.Month)
                        }
                        .padding(EdgeInsets(top: 16, leading: 17, bottom: 16, trailing: 17))
                        .background(Color(red: 114/255, green: 112/255, blue: 110/255))
                        .foregroundColor(Color.white)
                        .cornerRadius(10)
                        
                        VStack(alignment: .leading, spacing: 0) {
                            Text("\(Data.Subject) Crit \(Data.Crit.joined(separator: " + "))")
                                .bold()
                            if Data.Title != "" {
                                Text(Data.Title)
                            } else {
                                Text(Data.Class.joined(separator: ", "))
                            }
                        }
                        .padding(.leading, 10)
                    }
                }
                .onAppear {
                    viewModel.books.prefix(2)
                }
                
                Spacer()
            }
            Spacer()
        }
    }
}

小部件@main

class AppDelegate: NSObject, UIApplicationDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        FirebaseApp.configure()
        return true
    }
}

@main
struct AssessmentsWidget: Widget {
    
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    
    private let kind: String = "Assessments Widget"

    public var body: some WidgetConfiguration {
        StaticConfiguration(kind: kind, provider: Provider(), placeholder: PlaceholderView()) { entry in
            AssessmentsWidgetEntryView(entry: entry)
        }
        .configurationDisplayName("Assessments Widget")
        .description("Keep track of your upcoming assessments.")
        .supportedFamilies([.systemMedium])
    }
}

推荐答案

您的主应用需要将数据传递到您的扩展程序,这可以通过允许您的应用使用应用组"来实现.能力.应用程序组的作用是创建一个容器,您的应用程序可以在其中保存数据以供您与应用程序扩展共享.因此,请按照以下步骤启用应用组".

Your main app needs to pass data to your extension, this can be achieved by allowing your app to use "App Groups" capability. What App Groups does is, it creates a container where your app can save data for you to share with your app extensions. So follow these steps to enable "App Groups".

1.选择您的主要应用目标>签名和功能,然后点击+功能,然后选择应用组"

2.点击"+"添加一个新的容器,并在组之后添加一个名称.示例:"group.com.widgetTest.widgetContainer"

创建应用程序组"后,在您的主应用程序上,您应该执行相同的步骤,但在您的窗口小部件扩展"上目标.这次,无需创建容器,您应该能够从主应用程序中选择已经拥有的容器.您可以在YouTube 如何共享UserDefaults上找到一个不错的视频,很好地解释了此过程带有应用程序扩展程序

Once you have created the "App Group" on your main app, you should take the same steps but on your "Widget Extension" target. This time, instead of creating a container, you should be able to select the container you already have from the main app. You can find a good video on YouTube explaining this process really well on here How to Share UserDefaults with app extensions

我建议的下一步是创建一个Swift包或框架,并添加一个新的模型对象,该模型对象是您将从主应用传递到小部件扩展的对象.我选择了一个Swift软件包.

The next step I recommend is to create a Swift Package or a Framework, and add a new Model Object, this model object is the one you will be passing from your main app, to your widget extension. I chose a Swift Package.

为此,请按照以下步骤操作:

To do this follow these steps:

1. File> New> Swift程序包

此处

2.在您的Swift软件包中,在来源"文件夹中,创建一个将在您的主应用程序和窗口小部件扩展中使用的自定义模型

使您的对象符合可编码"标准,而且是公开的.

Make your object conform to "Codable" and that it is Public.

重要 请确保您导入"Foundation"这样,当您对对象进行解码/编码时,它就会正确地完成操作.

3.将您的软件包添加到您的主应用和窗口小部件扩展中

  • 选择应用程序的目标>常规>滚动到框架,库和嵌入式内容"
  • 点击"+"并搜索您的包裹

  • 在窗口小部件的扩展程序上执行相同的步骤

现在,您需要做的就是导入"您将要在主应用程序和WidgetExtension中创建自定义对象的文件中的模块,然后在主应用程序上初始化共享对象,并先将对象编码为JSON,然后将其保存到UserDefaults,然后将其保存到UserDefaults UserDefaults(suiteName:group.com.widgetTest.widgetContainer)

Now, all you need to do is "import" your module in the file that you will be creating your custom object in both your Main App, and on your WidgetExtension, then initialize your shared object on your main app and save it to UserDefaults by first encoding the object to JSON and then saving it to UserDefaults(suiteName: group.com.widgetTest.widgetContainer)

let mySharedObject = MySharedObject(name: "My Name", lastName: "My Last Name")
                   
 do {
     let data = try JSONEncoder().encode(mySharedObject)

      /// Make sure to use your "App Group" container suite name when saving and retrieving the object from UserDefaults
      let container = UserDefaults(suiteName:"group.com.widgetTest.widgetContainer")
          container?.setValue(data, forKey: "sharedObject")
                        
      /// Used to let the widget extension to reload the timeline
      WidgetCenter.shared.reloadAllTimelines()

      } catch {
        print("Unable to encode WidgetDay: \(error.localizedDescription)")
   }

然后在窗口小部件扩展中,您想要从UserDefaults中检索对象,对其进行解码,那么您应该一切顺利.

Then in your widget extension, you want to retrieve your object from UserDefaults, decode it and you should be good to go.

简短回答

下载Firebase数据,从该数据创建一个新对象,将其编码为JSON,使用UserDefaults将其保存在您的容器中,从容器中检索扩展中的对象,对其进行解码并将其用于小部件条目.当然,所有这些都是假设您按照上述步骤进行.

Download your Firebase data, create a new object from that data, encode it to JSON, save it on your container by using UserDefaults, retrieve the object in your extension from the container, decode it and use it for your widget entry. Of course, all of this is assuming you follow the steps above.

这篇关于iOS 14小部件+ SwiftUI + Firebase?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆