标签: combine

合并将一个发布者变成另一个

我使用一个 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 swift combine

12
推荐指数
1
解决办法
5163
查看次数

Swift Combine:缓冲上游值并以稳定的速度发出它们?

在 iOS 13 中使用新的 Combine 框架。

假设我有一个上游发布者以非常不规则的速度发送值 - 有时几秒或几分钟可能没有任何值,然后一个值流可能会同时通过。我想创建一个自定义发布者,该发布者订阅上游值,缓冲它们并在它们进入时以已知的常规节奏发出它们,但如果它们都已耗尽则不发布任何内容。

举个具体的例子:

  • t = 0 到 5000 毫秒:未发布上游值
  • t = 5001ms:上游发布“a”
  • t = 5002ms:上游发布“b”
  • t = 5003ms:上游发布“c”
  • t = 5004ms 到 10000ms:没有发布上游值
  • t = 10001ms:上游发布“d”

我订阅上游的发布者将每 1 秒产生一次值:

  • t = 0 到 5000 毫秒:未发布值
  • t = 5001ms:发布“a”
  • t = 6001ms:发布“b”
  • t = 7001ms:发布“c”
  • t = 7001ms 到 10001ms:没有发布值
  • t = 10001ms:发布“d”

合并中现有的出版商或运营商似乎都没有完全按照我的意愿行事。

  • throttle并且debounce会简单地以某个节奏采样上游值并删除丢失的值(例如,如果节奏为 1000 毫秒,则仅发布“a”)
  • delay 将为每个值添加相同的延迟,但不会将它们隔开(例如,如果我的延迟是 1000 毫秒,它将在 6001 毫秒时发布“a”,在 6002 毫秒时发布“b”,在 6003 毫秒时发布“c”)
  • buffer看起来很有希望,但我不太清楚如何使用它 …

ios swift combine

12
推荐指数
2
解决办法
4755
查看次数

Swift ReferenceWritableKeyPath 如何使用 Optional 属性?

存在基础:在阅读之前,了解您不能通过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)

optional swift swift-keypath combine

12
推荐指数
1
解决办法
1400
查看次数

Swift 组合:无法从 KeyPath Swift.KeyPath 中提取字符串

我试图观察核心数据实例中对象的任何变化。这是我的代码:

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)

在此输入图像描述

你们中有人知道为什么我会收到此错误或者是否有任何解决方法?

我将非常感谢你的帮助。

swift combine

12
推荐指数
1
解决办法
4324
查看次数

使用新的 SwiftUI Observable 框架将组合发布者分配给变量

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)

swift swiftui combine

12
推荐指数
0
解决办法
457
查看次数

将给定的 Publishers 失败类型设置为 Never in Combine

有没有办法将给定转换AnyPublisher<AnyType, SomeError>AnyPublisher<AnyType, Never>

frp ios swift combine

11
推荐指数
2
解决办法
4370
查看次数

ObservableObject 内的 ObservedObject 不刷新视图

我正在尝试在执行异步请求时显示活动指示器。我所做的是创建一个 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)

ios swift swiftui combine

11
推荐指数
2
解决办法
2589
查看次数

Swift Combine - 等待所有发布者

我正在尝试使用 Swift combine 运行许多具有相同结果的任务。目前,每个任务都是一个将发出结果的发布者。现在我面临一个问题,我必须等待所有发布者发出元素然后继续。有点像调度组。我发现 zip(with: : :_) 操作符需要 4 个发布者。

https://developer.apple.com/documentation/combine/passthroughsubject/3333571-zip

但是如果您有一组发布者(以防它们发出相同类型的元素)怎么办?有没有办法做到这一点?

swift combine

11
推荐指数
1
解决办法
6920
查看次数

使用 Combine 和 SwiftUI 的异步操作

我试图弄清楚如何使用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)

asynchronous swiftui combine

11
推荐指数
2
解决办法
1160
查看次数

延迟后结合框架重试?

我看怎么.retry直接使用,出错后重新订阅,像这样:

    URLSession.shared.dataTaskPublisher(for:url)
        .retry(3)
Run Code Online (Sandbox Code Playgroud)

但这似乎非常简单。如果我认为等待一段时间后此错误可能会消失怎么办?我可以插入一个.delay运算符,但是即使没有错误,延迟也会起作用。并且似乎没有一种方法可以有条件地应用运算符(即仅在出现错误时)。

我看到了如何通过从头开始编写 RetryWithDelay 操作符来解决这个问题,而且确实这样的操作符是由第三方编写的。但是有没有办法说“如果有错误就延迟”,纯粹使用我们给定的运算符?

我的想法是我可以使用.catch,因为它的功能仅在出现错误时运行。但是该函数需要返回一个发布者,我们将使用哪个发布者?如果我们返回somePublisher.delay(...)后跟.retry,我们就会.retry向错误的出版商申请,不是吗?

ios swift combine

11
推荐指数
4
解决办法
3209
查看次数

标签 统计

combine ×10

swift ×9

ios ×5

swiftui ×3

asynchronous ×1

frp ×1

optional ×1

swift-keypath ×1