Je suis encore assez nouveau dans SwiftUI et Firebase. Récemment, en tant que passe-temps, j'ai développé une application pour mon école. Après le lancement de Xcode 12, j'ai décidé d'expérimenter les nouvelles fonctionnalités telles que les widgets. Cependant, depuis que mon application obtient ses données de Firebase, j'ai eu quelques problèmes. Mon problème le plus récent est ce "Thread 1:" Impossible d'obtenir l'instance FirebaseApp. Veuillez appeler FirebaseApp.configure () avant d'utiliser Firestore ". Je ne sais pas trop où placer" FirebaseApp.configure () "car il n'y a pas d'AppDelegate.swift pour le widget. Mon code est ci-dessous.
Edit: J'ai réorganisé mon code pour que j'obtienne maintenant les données du modèle de données d'application iOS d'origine. Je n’importe donc pas Firebase dans le fichier Swift des widgets. Cependant, j'obtiens toujours la même erreur ("SendProcessControlEvent: toPid: a rencontré une erreur: Error Domain = com.apple.dt.deviceprocesscontrolservice Code = 8" et "-> 0x7fff5bb6933a <+10>: jae 0x7fff5bb69344; <+20> - Thread 1: "Impossible d'obtenir l'instance FirebaseApp. Veuillez appeler FirebaseApp.configure () avant d'utiliser Firestore" "). J'ai également inclus le code de @Wendy Liga, mais j'ai toujours la même erreur. Mon nouveau code est ci-dessous:
Modèle de données d'application iOS
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]) } }
Vue Widgets
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() } } }
Widgets @main
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!") } } } }
3 Réponses :
Vous pouvez ajouter l' appDelegate
à votre vue @main
SwiftUI
Créez d'abord votre appdelegate sur votre extension de widget
@main struct TestWidget: Widget { @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate private let kind: String = "ExampleWidget" public var body: some WidgetConfiguration { ... } }
regardez @main, dans l'extension de votre widget,
import Firebase class AppDelegate: NSObject, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { FirebaseApp.configure() return true } }
@main
est une nouvelle fonctionnalité swift 5.3 qui permet un point d'entrée de type valeur, ce sera donc votre principal point d'entrée pour votre extension de widget
ajoutez simplement @UIApplciationDelegateAdaptor
, dans votre @main
Salut! Merci pour votre réponse! Malheureusement, même après avoir utilisé votre code, j'ai toujours la même erreur. "-> 0x7fff5bb6933a <+10>: jae 0x7fff5bb69344; <+20>" - "Thread 1:" Impossible d'obtenir l'instance FirebaseApp. Veuillez appeler FirebaseApp.configure () avant d'utiliser Firestore "" Si cela aide, j'ai inclus 2 images. send.firefox.com/download/be18829cae081518/… , send.firefox.com/download/1c7e750ebb55b28a/...
Cela ne fonctionnera pas, pour une raison quelconque, appDelegate n'est pas appelé à partir de la portée du widget, veuillez me prouver que j'ai déjà testé.
Votre application principale doit transmettre des données à votre extension, ceci peut être réalisé en autorisant votre application à utiliser la fonctionnalité "Groupes d'applications". Ce que font les groupes d'applications, c'est qu'ils créent un conteneur dans lequel votre application peut enregistrer des données que vous pouvez partager avec vos extensions d'application. Suivez donc ces étapes pour activer les "Groupes d'applications".
1. Sélectionnez votre cible d'application principale> Signature et capacités, puis appuyez sur + Capacité et sélectionnez "Groupes d'applications"
2. Appuyez sur "+" pour ajouter un nouveau conteneur et ajoutez-lui un nom après le groupe. exemple: "group.com.widgetTest.widgetContainer"
Une fois que vous avez créé le "Groupe d'applications" sur votre application principale, vous devez suivre les mêmes étapes mais sur votre cible "Extension de widget". Cette fois, au lieu de créer un conteneur, vous devriez pouvoir sélectionner le conteneur que vous avez déjà dans l'application principale. Vous pouvez trouver une bonne vidéo sur YouTube expliquant très bien ce processus ici Comment partager les paramètres par défaut avec les extensions d'application
La prochaine étape que je recommande est de créer un package Swift ou un Framework, et d'ajouter un nouvel objet modèle, cet objet modèle est celui que vous passerez de votre application principale à votre extension de widget. J'ai choisi un package Swift.
Pour le faire, suivez ces étapes:
1. Fichier> Nouveau> Package Swift
Une bonne vidéo de la WWDC19 à ce sujet peut être vue ici
2. Dans votre package Swift, dans le dossier "Sources", créez un modèle personnalisé que vous utiliserez à la fois dans votre application principale et dans l'extension de widget.
Rendre votre objet conforme à "Codable" et qu'il est public.
Important Assurez-vous d'importer "Foundation" afin que lorsque vous décodez / encodez votre objet, il le fasse correctement.
3. Ajoutez votre package à votre application principale et à l'extension de widget
Maintenant, tout ce que vous avez à faire est «d'importer» votre module dans le fichier que vous allez créer votre objet personnalisé à la fois dans votre application principale et sur votre extension WidgetExtension, puis initialisez votre objet partagé sur votre application principale et enregistrez-le dans UserDefaults en encoder d'abord l'objet en JSON, puis l'enregistrer dans 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)") }
Ensuite, dans votre extension de widget, vous souhaitez récupérer votre objet de UserDefaults, le décoder et vous devriez être prêt à partir.
Réponse courte
Téléchargez vos données Firebase, créez un nouvel objet à partir de ces données, encodez-le en JSON, enregistrez-le sur votre conteneur à l'aide de UserDefaults, récupérez l'objet dans votre extension à partir du conteneur, décodez-le et utilisez-le pour votre entrée de widget. Bien sûr, tout cela suppose que vous suivez les étapes ci-dessus.
Excellente explication mais que se passe-t-il si l'application principale ne fonctionne plus en arrière-plan et que les données distantes de Firebase sont toujours à jour? Le widget aurait un tas de données périmées. Dans ce cas, le widget devrait-il se lier directement à Firebase et obtenir le jeton d'authentification de l'application principale pour accéder directement aux informations?
C'est une excellente question. J'ai répondu à cela en partageant les données de l'application principale vers le widget. Je suis sûr que si vous souhaitez que votre widget interagisse directement avec Firebase, vous devrez implémenter une sorte de connectivité entre le widget et Firebase. Mais gardez à l'esprit que le widget n'appellera une URL que lors de la mise à jour de la chronologie ou lorsque la chronologie met à jour le widget.
Je peux confirmer après avoir testé que la méthode suivante fonctionne pour utiliser Firebase dans la cible de widget sans incorporer un groupe d'applications, les valeurs par défaut de l'utilisateur ou autre.
@main struct FirebaseStartupSequence: Widget { init() { FirebaseApp.configure() } let kind: String = "FirebaseStartupSequence" var body: some WidgetConfiguration { IntentConfiguration(kind: kind, intent: ConfigurationIntent.self, provider: Provider()) { entry in FirebaseStartupSequenceEntryView(entry: entry) } .configurationDisplayName("My Widget") .description("This is an example widget.") } }
Utilisez simplement la méthode init
dans votre widget pour accéder à une instance de Firebase.
C'était la solution la plus simple pour moi à ce jour.
Tiré de: https://github.com/firebase/firebase-ios-sdk/issues/6683
Je ne peux pas vraiment répondre à cette question car j'en ai le même problème. Mais pour vous aider à trouver plus d'informations, vous devrez peut-être encapsuler vos modèles de données et votre logique dans un package rapide, puis importer les modules. Dans mon cas, je suis coincé dans l'ajout de Firebase et de Realm en tant que dépendance dans mon package swift. Regardez cette vidéo, où ils font de même, mais avec une extension de montre au lieu de l'extension de widget: developer.apple.com/wwdc19/410
Avez-vous trouvé une solution @ j-arango @ daniel-ho?
@lazerrouge je l'ai fait, je vais donner une réponse.