Ash*_*lls 11 asynchronous swiftui combine
我试图弄清楚如何使用Combine 和SwiftUI 处理异步操作。
例如,我有一个HealthKitManager类,除其他外,处理请求健康商店授权......
final class HealthKitManager {
enum Error: Swift.Error {
case notAvailable
case authorisationError(Swift.Error)
}
let healthStore = HKHealthStore()
func getHealthKitData(for objects: Set<HKObjectType>, completion: @escaping (Result<Bool, Error>) -> Void) {
guard HKHealthStore.isHealthDataAvailable() else {
completion(.failure(.notAvailable))
return
}
self.healthStore.requestAuthorization(toShare: nil, read: objects) { completed, error in
DispatchQueue.main.async {
if let error = error {
completion(.failure(.authorisationError(error)))
}
completion(.success(completed))
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
其用法如下...
struct ContentView: View {
let healthKitManager = HealthKitManager()
@State var showNextView = false
@State var showError = false
@State var hkError: Error?
let objectTypes = Set([HKObjectType.quantityType(forIdentifier: .bloodGlucose)!])
var body: some View {
NavigationView {
NavigationLink(destination: NextView(), isActive: $showNextView) {
Button("Show Next View") {
self.getHealthKitData()
}
}.navigationBarTitle("Content View")
}.alert(isPresented: $showError) {
Alert(title: Text("Error"), message: Text(hkError?.localizedDescription ?? ""), dismissButton: .cancel())
}
}
func getHealthKitData() {
self.healthKitManager.getHealthKitData(for: self.objectTypes) { result in
switch result {
case let .success(complete):
self.showNextView = complete
case let .failure(error):
self.hkError = error
self.showError = true
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
我想做的是使用Combine 而不是Result闭包。我猜是这样的……
final class HealthKitManager: ObservableObject {
enum Error: Swift.Error {
case notAvailable
case authorisationError(Swift.Error)
}
@Published var authorisationResult: Result<Bool, Error>?
let healthStore = HKHealthStore()
func getHealthKitData(for objects: Set<HKObjectType>) {
guard HKHealthStore.isHealthDataAvailable() else {
self.authorisationResult = .failure(.notAvailable)
return
}
self.healthStore.requestAuthorization(toShare: nil, read: objects) { completed, error in
DispatchQueue.main.async {
if let error = error {
self.authorisationResult = .failure(.authorisationError(error))
return
}
self.authorisationResult = .success(completed)
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
但是不清楚如何绑定到NavigationLink(isActive:)and的值alert(isPresented:),并得到错误。
struct ContentView: View {
@ObservedObject var healthKitManager = HealthKitManager()
let objectTypes = Set([HKObjectType.quantityType(forIdentifier: .bloodGlucose)!])
var body: some View {
NavigationView {
NavigationLink(destination: NextView(), isActive: ????) { // How do I get this
Button("Show Next View") {
self.healthKitManager.getHealthKitData(for: self.objectTypes)
}
}.navigationBarTitle("Content View")
}.alert(isPresented: ????) { // or this
Alert(title: Text("Error"), message: Text(????.localizedDescription ?? ""), dismissButton: .cancel()) // or this
}
}
}
Run Code Online (Sandbox Code Playgroud)
我猜这@Published var authorisationResult: Result<Bool, Error>?不正确?我应该使用Future / Promise其他东西吗?
更新
我发现还有另一种方式来呈现警报......
.alert(item: self.$error) { error in
Alert(title: Text(error.localizedDescription))
Run Code Online (Sandbox Code Playgroud)
这意味着我不需要 Bool for showError(它只需要Error对象是Identifiable)
我喜欢result你在第二个变体中所做的
@Published var authorisationResult: Result<Bool, Error>?
Run Code Online (Sandbox Code Playgroud)
所以可能的使用方法如下
NavigationLink(destination: NextView(), isActive:
Binding<Bool>.ifSuccess(self.healthKitManager.authorisationResult)) {
Button("Show Next View") {
self.healthKitManager.getHealthKitData(for: self.objectTypes)
}
}.navigationBarTitle("Content View")
Run Code Online (Sandbox Code Playgroud)
哪里有一些方便的扩展
extension Binding {
static func ifSuccess<E>(_ result: Result<Bool, E>?) -> Binding<Bool> where E: Error {
Binding<Bool>(
get: {
guard let result = result else { return false }
switch result {
case .success(true):
return true
default:
return false
}
}, set: { _ in })
}
}
Run Code Online (Sandbox Code Playgroud)
error可以用类似的方式完成变体。
extension Result {
func getFailure() -> Failure? {
switch self {
case .failure(let er):
return er
default:
return nil
}
}
func binding<B>(
success successClosure: (@escaping (Success) -> B),
failure failureClosure: @escaping (Failure) -> B) -> Binding<B> {
return Binding<B>(
get: {
switch self {
case .success(let value):
return successClosure(value)
case .failure(let failure):
return failureClosure(failure)
}
}, set: { _ in })
}
func implicitBinding(failure failureClosure: @escaping (Failure) -> Success) -> Binding<Success> {
return binding(success: { $0 }, failure: failureClosure)
}
}
class HealthKitManager: ObservableObject {
enum Error: Swift.Error {
case authorisationError(Swift.Error)
case notAvailable
}
@Published var authorisationResult = Result<Bool, Error>.failure(.notAvailable)
let healthStore = HKHealthStore()
func getHealthKitData(for objects: Set<HKObjectType>) {
guard HKHealthStore.isHealthDataAvailable() else {
self.authorisationResult = .failure(.notAvailable)
return
}
self.healthStore.requestAuthorization(toShare: nil, read: objects) { completed, error in
DispatchQueue.main.async {
if let error = error {
self.authorisationResult = .failure(.authorisationError(error))
return
}
self.authorisationResult = .success(completed)
}
}
}
}
struct ContentView: View {
@ObservedObject var healthKitManager = HealthKitManager()
let objectTypes = Set([HKObjectType.quantityType(forIdentifier: .bloodGlucose)!])
var body: some View {
NavigationView {
NavigationLink(destination: NextView(),
isActive: healthKitManager.authorisationResult.implicitBinding(failure: { _ in false })) {
Button("Show Next View") {
self.healthKitManager.getHealthKitData(for: self.objectTypes)
}
}.navigationBarTitle("Content View")
}.alert(isPresented: healthKitManager.authorisationResult.binding(success: { _ in false }, failure: { _ in true })) {
let message = healthKitManager.authorisationResult.getFailure()?.localizedDescription ?? ""
return Alert(title: Text("Error"), message: Text(message), dismissButton: .cancel()) // or this
}
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1160 次 |
| 最近记录: |