我使用一个 OAuth 框架,它异步创建经过身份验证的请求,如下所示:
OAuthSession.current.makeAuthenticatedRequest(request: myURLRequest) { (result: Result<URLRequest, OAuthError>) in
switch result {
case .success(let request):
URLSession.shared.dataTask(with: request) { (data, response, error) in
// ...
}
// ...
}
}
Run Code Online (Sandbox Code Playgroud)
我试图让我的 OAuth 框架使用Combine,所以我知道有一个发布者版本的makeAuthenticatedRequest方法,即:
public func makeAuthenticatedRequest(request: URLRequest) -> AnyPublisher<URLRequest, OAuthError>
Run Code Online (Sandbox Code Playgroud)
我正在尝试使用它来替换上面的调用站点,如下所示:
OAuthSession.current.makeAuthenticatedRequestPublisher(request)
.tryMap(URLSession.shared.dataTaskPublisher(for:))
.tryMap { (data, _) in data } // Problem is here
.decode(type: A.self, decoder: decoder)
Run Code Online (Sandbox Code Playgroud)
如上所述,问题在于将发布者的结果转换为新的发布者。我该怎么做呢?
在 iOS 13 中使用新的 Combine 框架。
假设我有一个上游发布者以非常不规则的速度发送值 - 有时几秒或几分钟可能没有任何值,然后一个值流可能会同时通过。我想创建一个自定义发布者,该发布者订阅上游值,缓冲它们并在它们进入时以已知的常规节奏发出它们,但如果它们都已耗尽则不发布任何内容。
举个具体的例子:
我订阅上游的发布者将每 1 秒产生一次值:
合并中现有的出版商或运营商似乎都没有完全按照我的意愿行事。
存在基础:在阅读之前,了解您不能通过imagekeypath将 UIImage 分配给图像视图插座的属性会有所帮助\UIImageView.image。这是财产:
@IBOutlet weak var iv: UIImageView!
Run Code Online (Sandbox Code Playgroud)
现在,这会编译吗?
let im = UIImage()
let kp = \UIImageView.image
self.iv[keyPath:kp] = im // error
Run Code Online (Sandbox Code Playgroud)
不!
可选类型“UIImage?”的值 必须解包为“UIImage”类型的值
好的,现在我们已经为实际用例做好了准备。
我真正想了解的是Combine 框架.assign订阅者如何在幕后工作。为了进行实验,我尝试使用我自己的 Assign 对象。在我的示例中,我的发布者管道生成一个 UIImage 对象,并将其分配给imageUIImageView 属性的属性self.iv。
如果我们使用该.assign方法,它会编译并运行:
URLSession.shared.dataTaskPublisher(for: url)
.map {$0.data}
.replaceError(with: Data())
.compactMap { UIImage(data:$0) }
.receive(on: DispatchQueue.main)
.assign(to: \.image, on: self.iv)
.store(in:&self.storage)
Run Code Online (Sandbox Code Playgroud)
所以,我对自己说,要看看这是如何工作的,我将删除.assign并将其替换为我自己的 Assign 对象:
let pub = URLSession.shared.dataTaskPublisher(for: url)
.map {$0.data}
.replaceError(with: Data())
.compactMap { …Run Code Online (Sandbox Code Playgroud) 我试图观察核心数据实例中对象的任何变化。这是我的代码:
class MyClass {
@objc dynamic var str: String?
}
final class Article: NSObject {
@objc dynamic var title: String?
@objc dynamic var summary: String?
var myVar: MyClass?
}
Run Code Online (Sandbox Code Playgroud)
这是我正在实现的观察者:
func update(article: Article) {
titleSubscription = article.publisher(for: \.title).sink { value in
print(value)
} receiveValue: { _ in
print("I got something")
}
summarySubscription = article.publisher(for: \.myVar?.str).sink{ _ in
} receiveValue: { _ in
}
}
Run Code Online (Sandbox Code Playgroud)
但我收到这个错误:
Thread 1: Fatal error: Could not extract a String from KeyPath Swift.KeyPath<Examples.Article, Swift.Optional<Swift.String>>
Run Code Online (Sandbox Code Playgroud)
你们中有人知道为什么我会收到此错误或者是否有任何解决方法?
我将非常感谢你的帮助。
SwiftUI 5 现在使用@Observable包装器来观察类中的更改,而不是使其符合ObservableObject. 这摆脱了@Published我在工作流程中积极使用的包装器:
旧的实现
class OldObservable: ObservableObject {
let model = SomeModel()
@Published var someValue: String?
init() {
setupSubscribers()
}
func setupSubscribers() {
// someValuePublisher could be PassthroughSubject<String?, Never>()
model.someValuePublisher.assign(to:$someValue)
}
}
Run Code Online (Sandbox Code Playgroud)
新实施
@Observable
class NewObservable {
let model = SomeModel()
var someValue: String?
init() {
setupSubscribers()
}
func setupSubscribers() {
// Not working anymore
model.someValuePublisher.assign(to:$someValue)
}
}
Run Code Online (Sandbox Code Playgroud)
由于 someValue 不是已发布的属性,因此我无法assign(to:)再使用通常的属性。
到目前为止,为了使其工作,我必须使用旧的实现 allocate(to:on) ,我听说这可能会导致保留周期问题,并且相同结果的代码更长。
var subscribers = Set<AnyCancellable>()
func setupSubscribers() { …Run Code Online (Sandbox Code Playgroud) 有没有办法将给定转换AnyPublisher<AnyType, SomeError>为AnyPublisher<AnyType, Never>?
我正在尝试在执行异步请求时显示活动指示器。我所做的是创建一个 ActivityTracker 对象来跟踪发布者的生命周期。这个 ActivityTracker 是一个 ObservableObject 并将存储在视图模型中,它也是一个 ObservableObject。
似乎这种设置并没有刷新视图。这是我的代码:
struct ContentView: View {
@ObservedObject var viewModel = ContentViewModel()
var body: some View {
VStack(spacing: 16) {
Text("Counter: \(viewModel.tracker.count)\nPerforming: \(viewModel.tracker.isPerformingActivity ? "true" : "false")")
Button(action: {
_ = request().trackActivity(self.viewModel.tracker).sink { }
}) {
Text("Request")
}
}
}
}
class ContentViewModel: ObservableObject {
@Published var tracker = Publishers.ActivityTracker()
}
private func request() -> AnyPublisher<Void, Never> {
return Just(()).delay(for: 2.0, scheduler: RunLoop.main)
.eraseToAnyPublisher()
}
extension Publishers {
final class ActivityTracker: ObservableObject {
// …Run Code Online (Sandbox Code Playgroud) 我正在尝试使用 Swift combine 运行许多具有相同结果的任务。目前,每个任务都是一个将发出结果的发布者。现在我面临一个问题,我必须等待所有发布者发出元素然后继续。有点像调度组。我发现 zip(with: : :_) 操作符需要 4 个发布者。
https://developer.apple.com/documentation/combine/passthroughsubject/3333571-zip
但是如果您有一组发布者(以防它们发出相同类型的元素)怎么办?有没有办法做到这一点?
我试图弄清楚如何使用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 …Run Code Online (Sandbox Code Playgroud) 我看怎么.retry直接使用,出错后重新订阅,像这样:
URLSession.shared.dataTaskPublisher(for:url)
.retry(3)
Run Code Online (Sandbox Code Playgroud)
但这似乎非常简单。如果我认为等待一段时间后此错误可能会消失怎么办?我可以插入一个.delay运算符,但是即使没有错误,延迟也会起作用。并且似乎没有一种方法可以有条件地应用运算符(即仅在出现错误时)。
我看到了如何通过从头开始编写 RetryWithDelay 操作符来解决这个问题,而且确实这样的操作符是由第三方编写的。但是有没有办法说“如果有错误就延迟”,纯粹使用我们给定的运算符?
我的想法是我可以使用.catch,因为它的功能仅在出现错误时运行。但是该函数需要返回一个发布者,我们将使用哪个发布者?如果我们返回somePublisher.delay(...)后跟.retry,我们就会.retry向错误的出版商申请,不是吗?