Aar*_*ger 5 ios swift cncontact swiftui
There are many possible variants of this question, but take as an example the CNAuthorizationStatus
returned by CNContactStore.authorizationStatus(for: .contacts)
, which can be notDetermined
, restricted
, denied
, or authorized
. My goal is to always show the current authorization status in my app's UI.
To expose this to SwiftUI, I might make an ObservableObject
called ModelData
with a contacts
property:
final class ModelData: ObservableObject {
@Published var contacts = Contacts.shared
}
Run Code Online (Sandbox Code Playgroud)
Where contacts
contains my contact-specific model code, including Authorization:
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
}
}
}
Run Code Online (Sandbox Code Playgroud)
And I might add a method that a button could call to request access:
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)
}
}
}
Run Code Online (Sandbox Code Playgroud)
And for the sake of simplicity, say my view is just:
Text(String(describing: modelData.contacts.authorization))
Run Code Online (Sandbox Code Playgroud)
So my questions are:
ModelData().contacts.authorization
calls a getter function, not a property, how can I inform the SwiftUI view when I know it's changed (e.g. where the TODO is in the requestAccess()
function)?正如 @jnpdx 指出的 -与类(尤其是永不改变的单例)一起使用@Published
可能不会产生任何有用的结果
@Published
其行为类似于CurrentValueSubject
,仅当其在后台存储/观察的值发生变化时才会触发更新。由于它存储对实例的引用Contacts.shared
,因此它不会提供/触发授权状态更改的任何更新。
现在回答你的问题 -
鉴于ModelData().contacts.authorization
调用的是 getter 函数,而不是属性,当我知道它已更改时,如何通知 SwiftUI 视图
只要您直接从 getter 访问一个值ModelData().contacts.authorization
,它就只是一个Contacts.Authorization
不提供任何可观察性的类型值。
因此,即使该值随时间变化(从.notDetermined
=>.authorized
开始),也没有存储(参考点)可供我们比较它自上次以来是否发生了变化。
我们必须定义一个可以比较旧/新值并根据需要触发更新的存储。authorization
这可以通过如下标记来实现@Published
-
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()
})
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
493 次 |
最近记录: |