如何在 Swift Combine 中创建自定义链?
How to create a custom chain in Swift Combine?
我正在尝试为 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()
您的 requestAuthorization
应该 return 发布者:
func requestAuthorization(for type: LocationAPI.AuthorizationType = .whenInUse) -> AnyPublisher<Bool, Never> {
// start authorization flow
return authorizationPublisher
}
我还建议您使用 CurrentValueSubject
而不是 PassthroughSubject
,这样您在订阅 authorizationPublisher
时总能获得 "current" 状态。
我正在尝试为 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()
您的 requestAuthorization
应该 return 发布者:
func requestAuthorization(for type: LocationAPI.AuthorizationType = .whenInUse) -> AnyPublisher<Bool, Never> {
// start authorization flow
return authorizationPublisher
}
我还建议您使用 CurrentValueSubject
而不是 PassthroughSubject
,这样您在订阅 authorizationPublisher
时总能获得 "current" 状态。