解析器 SwiftUI 导致崩溃 - 调用的顺序是啥?
Posted
技术标签:
【中文标题】解析器 SwiftUI 导致崩溃 - 调用的顺序是啥?【英文标题】:Resolver SwiftUI Causing Crash - What Order Do Things Get Called?解析器 SwiftUI 导致崩溃 - 调用的顺序是什么? 【发布时间】:2022-01-08 13:32:59 【问题描述】:我正在开发 SwiftUI 并使用 Resolver 进行依赖注入。作为后端,我使用的是 Firebase。我创建了一个 AuthSession 文件来处理我的所有用户身份验证内容。在该项目中,我还有许多其他存储库,它们在整个应用程序中填充数据。在 AuthSession 中,我为每个存储库创建属性,以便我可以在登录和注销时启动和停止 Firestore 侦听器。在其中几个存储库中,我想通过@InjectedObject 访问 AuthSession,这样当用户登录时,我可以收到通知并可以通过 Combine 获取更新。我的问题是,当我启动应用程序时,它会因奇怪的 Firebase 错误而崩溃。
AuthSession.swift
class AuthSession: ObservableObject
let db = Firestore.firestore()
var offerRepository: OfferRepository = Resolver.resolve()
var handle: AuthStateDidChangeListenerHandle?
@Published var currentUser: User?
@Published var loggedIn = false
@Published var currentUserUid = ""
// Combine Cancellable
private var cancellables = Set<AnyCancellable>()
// Intitalizer
init()
func listen()
print("AuthSession - listen called")
// Monitor Authentication chagnes using Firebase Auth.
handle = Auth.auth().addStateDidChangeListener (auth, user) in
// Check to see if a user is returned from a sign in or sign up event.
if let user = user
// Set loggedIn to true. This will also be set when a new User is created in SignUpView.
print("User Exists.")
self.loggedIn = true
self.currentUserUid = user.uid
self.currentUser = user
else
print("Not logged in")
下面是 OfferRepository。添加下面的行时,它会崩溃。如果删除该行,它不会崩溃。我不确定为什么。不包括组合代码。
导致崩溃的线路。
@InjectedObject var authSession: AuthSession
OfferRepository.swift
class OfferRepository: ObservableObject
let db = Firestore.firestore()
private var snapshotListener: ListenerRegistration?
@InjectedObject var authSession: AuthSession
@Published var offers = [Offer]()
private var cancellables = Set<AnyCancellable>()
init()
startSnapshotListener()
func startSnapshotListener()
if snapshotListener == nil
self.snapshotListener = db.collection(FirestoreCollection.offers).order(by: "created", descending: true).addSnapshotListener (querySnapshot, error) in
if let error = error
print("Error getting documents: \(error)")
else
guard let documents = querySnapshot?.documents else
print("No Offers.")
return
self.offers = documents.compactMap offer in
do
return try offer.data(as: Offer.self)
catch
print(error)
return nil
这里是我的 AppDelegate+Registering 文件供参考。
extension Resolver: ResolverRegistering
public static func registerAllServices()
register AuthSession() .scope(.application)
register OfferRepository() as OfferRepository .scope(.application)
应用程序在 Firestore 包中的以下行崩溃。
- (NSString *)keyForDatabase:(NSString *)database
return [NSString stringWithFormat:@"%@|%@", self.app.name, database];
线程 1:EXC_BAD_ACCESS(代码=2,地址=0x16d317ff8)
虽然我可以在登录和注销视图中启动和停止侦听器,但我更愿意将其保留在 AuthSession 文件中。有没有办法解决这个问题?
【问题讨论】:
【参考方案1】:@InjectedObject
旨在用于将ObservableObject
s 注入 SwiftUI 视图 - 请参阅文档:https://github.com/hmlongco/Resolver#property-wrappers
由于您想在存储库中引用AuthenticationService
(即ObservableObject
s,您应该改用@Injected
。
这是来自我的一个应用程序的 sn-p:
public class ArtifactRepository: ObservableObject
// MARK: - Dependencies
@Injected var db: Firestore
@Injected var authenticationService: AuthenticationService
// MARK: - Publishers
@Published public var artifacts = [Artifact]()
// MARK: - Private attributes
private var statusFilter: Status
private var userId: String = "unknown"
private var listenerRegistration: ListenerRegistration?
private var cancellables = Set<AnyCancellable>()
let logger = Logger(subsystem: "dev.peterfriese.App", category: "persistence")
public init(statusFilter: Status = .inbox, liveSync: Bool = true)
// filtering
self.statusFilter = statusFilter
// observe user ID
authenticationService.$user
.compactMap user in
user?.uid
.assign(to: \.userId, on: self)
.store(in: &cancellables)
// if live sync is on, (re)load data when user changes
if liveSync
authenticationService.$user
.receive(on: DispatchQueue.main)
.sink [weak self] user in
if self?.listenerRegistration != nil
self?.unsubscribe()
self?.subscribe()
.store(in: &cancellables)
deinit
unsubscribe()
public func unsubscribe()
if listenerRegistration != nil
listenerRegistration?.remove()
listenerRegistration = nil
public func subscribe()
if listenerRegistration == nil
var query = db.collection("artifacts")
.whereField("userId", isEqualTo: self.userId)
if (statusFilter != .all)
query = query.whereField("status", isEqualTo: statusFilter.rawValue)
listenerRegistration = query.order(by: "dateAdded", descending: true)
.addSnapshotListener [weak self] (querySnapshot, error) in
guard let documents = querySnapshot?.documents else
self?.logger.debug("No documents")
return
self?.logger.debug("Mapping \(documents.count) documents")
self?.artifacts = documents.compactMap queryDocumentSnapshot in
try? queryDocumentSnapshot.data(as: Artifact.self)
public class AuthenticationService: ObservableObject
private let logger = Logger(subsystem: "dev.peterfriese.App", category: "authentication")
@Published public var user: User?
private var handle: AuthStateDidChangeListenerHandle?
public init()
setupKeychainSharing()
registerStateListener()
public func signIn()
if Auth.auth().currentUser == nil
Auth.auth().signInAnonymously()
public func signOut()
do
try Auth.auth().signOut()
catch
print("error when trying to sign out: \(error.localizedDescription)")
private let accessGroup = "XXXXXXX.dev.peterfriese.App"
private func setupKeychainSharing()
do
let auth = Auth.auth()
auth.shareAuthStateAcrossDevices = true
try auth.useUserAccessGroup(accessGroup)
catch let error as NSError
print("Error changing user access group: %@", error)
private func registerStateListener()
if handle == nil
handle = Auth.auth().addStateDidChangeListener( (auth, user) in
self.user = user
if let user = user
if user.isAnonymous
self.logger.debug("User signed in anonymously with user ID \(user.uid).")
else
self.logger.debug("User signed in with user ID \(user.uid). Email: \(user.email ?? "(empty)"), display name: [\(user.displayName ?? "(empty)")]")
else
self.logger.debug("User signed out.")
self.signIn()
)
【讨论】:
一读到这篇文章,我就意识到 InjectedObject 是为视图设计的。感谢您的帮助。 我认为这是答案,但崩溃仍然发生。这似乎很奇怪。在您的示例中,您没有尝试在 AuthenticationService 中解析 ArtifactRepository。我相信这是问题所在。 我刚刚注意到你的代码中似乎有一个依赖循环:AuthSession 依赖于 OfferRepository(见var offerRepository: OfferRepository = Resolver.resolve()
行),而 OfferRepository 依赖于 AuthSession(见@InjectedObject var authSession: AuthSession
行)。我敢打赌,如果您查看调用堆栈,您会注意到堆栈溢出。您的 AuthSession 永远不应该依赖于您的存储库 - 反之亦然。
太棒了。谢谢。以上是关于解析器 SwiftUI 导致崩溃 - 调用的顺序是啥?的主要内容,如果未能解决你的问题,请参考以下文章
为啥@FocusState 会导致 SwiftUI 预览崩溃
SwiftUI 应用程序在两个选择器周围出现 VStack 崩溃