Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在SwiftUI中引用外部iOS系统状态更新?_Ios_Swift_Swiftui_Cncontact - Fatal编程技术网

如何在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视图

    只要您直接从getter
    ModelData().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
    测试了这一点,当视图可见时,授权状态改变时,我的视图不会更新(即,如果用户在设置应用程序中关闭联系人权限)。