如何在 Swift Combine 中创建自定义链?

Posted

技术标签:

【中文标题】如何在 Swift Combine 中创建自定义链?【英文标题】:How to create a custom chain in Swift Combine? 【发布时间】:2020-09-30 02:24:23 【问题描述】:

我正在尝试为 Combine 创建一个 LocationManager 包装器。我有一个发布者和一些触发发布者的功能。但是,我想将它们与自定义命令组合在一起。

这是我目前得到的:

@available(OSX 10.15, ios 13, tvOS 13, watchOS 6, *)
public class LocationProxy: NSObject 
    private lazy var manager = CLLocationManager()

    private static let authorizationSubject = PassthroughSubject<Bool, Never>()
    public private(set) lazy var authorizationPublisher: AnyPublisher<Bool, Never> = Self.authorizationSubject.eraseToAnyPublisher()

    var isAuthorized: Bool  CLLocationManager.isAuthorized 

    func isAuthorized(for type: LocationAPI.AuthorizationType) -> Bool 
        guard CLLocationManager.locationServicesEnabled() else  return false 

        #if os(macOS)
        return type == .always && CLLocationManager.authorizationStatus() == .authorizedAlways
        #else
        return (type == .whenInUse && CLLocationManager.authorizationStatus() == .authorizedWhenInUse)
            || (type == .always && CLLocationManager.authorizationStatus() == .authorizedAlways)
        #endif
    

    func requestAuthorization(for type: LocationAPI.AuthorizationType = .whenInUse) 
        // Handle authorized and exit
        guard !isAuthorized(for: type) else 
            Self.authorizationSubject.send(true)
            return
        

        // Request appropiate authorization before exit
        defer 
            #if os(macOS)
            if #available(OSX 10.15, *) 
                manager.requestAlwaysAuthorization()
            
            #elseif os(tvOS)
            manager.requestWhenInUseAuthorization()
            #else
            switch type 
            case .whenInUse:
                manager.requestWhenInUseAuthorization()
            case .always:
                manager.requestAlwaysAuthorization()
            
            #endif
        

        // Handle mismatched allowed and exit
        guard !isAuthorized else 
            // Process callback in case authorization dialog not launched by OS
            // since user will be notified first time only and ignored subsequently
            Self.authorizationSubject.send(false)
            return
        

        // Handle denied and exit
        guard CLLocationManager.authorizationStatus() == .notDetermined else 
            Self.authorizationSubject.send(false)
            return
        
    

    public func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) 
        guard status != .notDetermined else  return 
        Self.authorizationSubject.send(isAuthorized)
    

要使用它,我必须先订阅,然后调用请求授权函数:

cancellable = locationProxy.authorizationPublisher
    .sink  status in
        print(status)
    

locationProxy.requestAuthorization()

有没有办法在一次调用中构造代码以订阅和请求授权,如下所示:

cancellable = locationProxy.authorizationPublisher
    .sink  status in
        print(status)
    
    .requestAuthorization()

【问题讨论】:

【参考方案1】:

您的requestAuthorization 应该返回发布者:

func requestAuthorization(for type: LocationAPI.AuthorizationType = .whenInUse) -> AnyPublisher<Bool, Never> 
  // start authorization flow

  return authorizationPublisher

我还建议您使用CurrentValueSubject 而不是PassthroughSubject,这样您在订阅authorizationPublisher 时始终可以获得“当前”状态。

【讨论】:

以上是关于如何在 Swift Combine 中创建自定义链?的主要内容,如果未能解决你的问题,请参考以下文章

如何在Xcode + Swift 4中创建自定义UIBarButtonItem类?

iOS - 如何在没有第三方框架的情况下在 Swift 中创建自定义动画横幅

在 Swift 中创建自定义(八角形)UIButton 的最佳方法

swift 在Swift中创建自定义运算符

在 Swift 中创建自定义 UIView 并显示为弹出窗口

如何在 laravel 中创建自定义关系?