如何在SwiftUI中引用外部iOS系统状态更新?
这个问题有许多可能的变体,但以如何在SwiftUI中引用外部iOS系统状态更新?,ios,swift,swiftui,cncontact,Ios,Swift,Swiftui,Cncontact,这个问题有许多可能的变体,但以CNContactStore.authorizationStatus(for:.contacts)返回的CNAuthorizationStatus为例,它可以是未确定的,受限的,拒绝的,或已授权的。我的目标是始终在我的应用程序的UI中显示当前授权状态 为了向SwiftUI公开此内容,我可能会创建一个名为ModelData的observeObject,并使用contacts属性: final class ModelData: ObservableObject {
CNContactStore.authorizationStatus(for:.contacts)
返回的CNAuthorizationStatus
为例,它可以是未确定的,受限的,拒绝的,或已授权的。我的目标是始终在我的应用程序的UI中显示当前授权状态
为了向SwiftUI公开此内容,我可能会创建一个名为ModelData
的observeObject
,并使用contacts
属性:
final class ModelData: ObservableObject {
@Published var contacts = Contacts.shared
}
其中联系人
包含我的联系人特定型号代码,包括授权:
class Contacts {
fileprivate let store = CNContactStore()
static let shared = Contacts()
enum Authorization {
case notDetermined
case restricted
case denied
case authorized
}
var authorization: Authorization {
switch CNContactStore.authorizationStatus(for: .contacts) {
case .notDetermined:
return .notDetermined
case .restricted:
return .restricted
case .denied:
return .denied
case .authorized:
return .authorized
@unknown default:
return .notDetermined
}
}
}
我可能会添加一个按钮可以调用以请求访问的方法:
func requestAccess(handler: @escaping (Bool, Error?) -> Void) {
store.requestAccess(for: .contacts) { (granted, error) in
// TODO: tell SwiftUI views to re-check authorization
DispatchQueue.main.async {
handler(granted, error)
}
}
}
为了简单起见,我的观点是:
Text(String(describing: modelData.contacts.authorization))
因此,我的问题是:
假设ModelData().contacts.authorization
调用的是一个getter函数,而不是一个属性,那么当我知道SwiftUI视图发生了更改(例如,TODO在requestAccess()函数中的位置)时,如何通知它
鉴于用户可以在设置应用程序中切换权限(即,该值可能会从“我”下更改),我如何确保视图状态始终处于更新状态?(我是否需要订阅NSNotification并以类似方式强制刷新?还是有更好的方法?)
正如@jnpdx所指出的-将@Published
与一个类(尤其是一个永不改变的单例)一起使用可能不会产生任何有用的结果
@Published
的行为类似于CurrentValueSubject
,只有在其存储/观察到的值发生变化时,才会触发更新。由于它正在存储对Contacts.shared
实例的引用,因此它不会提供/触发任何授权状态更改的更新
现在谈谈你的问题-
鉴于ModelData().contacts.authorization
调用的是一个getter函数,而不是一个属性,当我知道它已更改时,如何通知SwiftUI视图
只要您直接从getterModelData().contacts.authorization
中访问一个值,它就是一个不提供任何可观察性的contacts.authorization
类型的值
因此,即使值随时间变化(从.notDetermined
=>.authorized
),也没有存储(参考点),我们可以对比它自上次以来是否发生了变化
我们必须定义一个可以比较新旧值并根据需要触发更新的存储。这可以通过将授权
标记为@已发布
来实现,如下所示-
import SwiftUI
import Contacts
final class Contacts: ObservableObject {
fileprivate let store = CNContactStore()
static let shared = Contacts()
enum Authorization {
case notDetermined
case restricted
case denied
case authorized
}
/// Since we have a storage (and hence a way to compare old/new status values)
/// Anytime a new ( != old ) value is assigned to this
/// It triggers `.send()` which triggers an update
@Published var authorization: Authorization = .notDetermined
init() {
self.refreshAuthorizationStatus()
}
private func refreshAuthorizationStatus() {
authorization = self.currentAuthorization()
}
private func currentAuthorization() -> Authorization {
switch CNContactStore.authorizationStatus(for: .contacts) {
case .notDetermined:
return .notDetermined
case .restricted:
return .restricted
case .denied:
return .denied
case .authorized:
return .authorized
@unknown default:
return .notDetermined
}
}
func requestAccess() {
store.requestAccess(for: .contacts) { [weak self] (granted, error) in
DispatchQueue.main.async {
self?.refreshAuthorizationStatus()
}
}
}
}
struct ContentView: View {
@ObservedObject var contacts = Contacts.shared
var body: some View {
VStack(spacing: 16) {
Text(String(describing: contacts.authorization))
if contacts.authorization == .notDetermined {
Button("Request Access", action: {
contacts.requestAccess()
})
}
}
}
}
我想这一切都在运作。
当用户从设置应用程序更改访问级别时,将调用此行
文本(字符串(描述:modelData.contacts.authorization))
因此,您的视图总是显示当前状态。没有时间给出完整的答案,但现在我要说的是,将@Published
与类一起使用(尤其是永不更改的单例)可能不会产生任何有用的结果。您可能需要的是一个@Published
属性,用于存储上次检查授权状态的结果<事实上,code>contacts
可能会变成一个private
属性。这一点很好。但是我自己存储结果,而不是仅仅通过系统传递结果。我希望完全避免存储结果的复杂性,而只是找到一种模式来广播当我收到NSNotification或系统回调时状态发生了变化。你当然可以想出一个联合发布器,它可以让你以各种方式进行传输,但我想你仍然需要将该值存储在某个地方。由于SwiftUI完全是关于声明性状态的,所以您不能像在命令式编程中那样在传递过程中使用值来设置值——您需要一些设置,以便在后续重新呈现时可以继续引用它。@AaronBrager我下面的回答是否涵盖了您能想到的所有用例?我用.notDetermined>.authorized>.denied
测试了这一点,当视图可见时,授权状态改变时,我的视图不会更新(即,如果用户在设置应用程序中关闭联系人权限)。