标签: combine

是否有替代 Combine 的 @Published 的方法,在它发生之后而不是之前发出值变化的信号?

我想使用Combine 的@Published属性来响应属性的变化,但它似乎在属性发生变化之前发出信号,就像willSet观察者一样。以下代码:

import Combine

class A {
    @Published var foo = false
}

let a = A()
let fooSink = a.$foo.dropFirst().sink { _ in // `dropFirst()` is to ignore the initial value
    print("foo is now \(a.foo)")
}

a.foo = true
Run Code Online (Sandbox Code Playgroud)

输出:

foo 现在是假的

我希望接收器像didSet观察者一样在属性发生变化后运行,这样foo就可以了。有没有其他出版商发出信号,或者有一种制作这样的@Published作品的方式?

swift combine

15
推荐指数
4
解决办法
2522
查看次数

结合:如何在不完成原始发布者的情况下替换/捕获错误?

鉴于以下代码:

    enum MyError: Error {
        case someError
    }

    myButton.publisher(for: .touchUpInside).tryMap({ _ in
        if Bool.random() {
            throw MyError.someError
        } else {
            return "we're in the else case"
        }
    })
        .replaceError(with: "replaced Error")
        .sink(receiveCompletion: { (completed) in
            print(completed)
        }, receiveValue: { (sadf) in
            print(sadf)
        }).store(in: &cancellables)
Run Code Online (Sandbox Code Playgroud)

每当我点击按钮时,我都会得到we're in the else case直到Bool.random()为真 - 现在抛出一个错误。我尝试了不同的方法,但我无法捕获/替换/忽略错误并在点击按钮后继续。

在代码示例中,我希望有以下输出

we're in the else case
we're in the else case
replaced Error
we're in the else case
...
Run Code Online (Sandbox Code Playgroud)

相反,我得到finishedreplaced error并且没有发出任何事件。 …

ios swift combine

15
推荐指数
3
解决办法
1万
查看次数

如何正确管理`AnyCancellable`的集合

我希望所有发布者都执行,除非明确取消。我不介意AnyCancellable基于文档,它会自动调用的范围走出去,但是canceldeinit这是不希望。

我曾尝试使用可取消的袋子,但AnyCancelable即使在出版商解雇了一个完成后仍然堆积如山。

我应该手动管理行李吗?我的印象store(in: inout Set)是为了方便管理可取消的实例,但它所做的只是推AnyCancellable入一个集合。

var cancelableSet = Set<AnyCancellable>()

func work(value: Int) -> AnyCancellable {
    return Just(value)
        .delay(for: .seconds(1), scheduler: DispatchQueue.global(qos: .default))
        .map { $0 + 1 }
        .sink(receiveValue: { (value) in
            print("Got value: \(value)")
        })
}

work(value: 1337).store(in: &cancelableSet)

DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(5)) {
    print("\(cancelableSet)")
}
Run Code Online (Sandbox Code Playgroud)

到目前为止我想出了什么,它工作正常,但让我想知道Combine框架中是否缺少某些东西,或者它不应该以这种方式使用:

class DisposeBag {
    private let lock = NSLock()
    private var cancellableSet = Set<AnyCancellable>()

    func store(_ cancellable: AnyCancellable) {
        print("Store cancellable: …
Run Code Online (Sandbox Code Playgroud)

swift combine

14
推荐指数
1
解决办法
4022
查看次数

使自定义发布者在 Swift Combine 上的不同 DispatchQueue 上运行

我创建了一个函数,该函数使用以下代码在 Swift Combine 中返回一个自定义发布者:

func customPubliher() -> AnyPublisher<Bool, Never> {
    return Future<Bool, Never> { promise in
        promise(.success(true))
    }.eraseToAnyPublisher()
}
Run Code Online (Sandbox Code Playgroud)

然后我使用以下代码订阅了这个发布者:

customPublisher()
    .subscribe(on: DispatchQueue.global())
    .map { _ in
        print(Thread.isMainThread)
    }
    .sink(receiveCompletion: { _ in }, receiveValue: { value in
        // Do something with the value received
    }).store(in: &disposables)
Run Code Online (Sandbox Code Playgroud)

但是,即使我说行.subscribe(on: DispatchQueue.global()),当我做了申购,代码不是在不同的队列(已执行print.map产出如此)。

但是,例如,如果我将自定义发布者替换为一个内置的 Combine 发布者Just()(见下文),则相同的代码将在不同的队列上正常执行:

Just(true)
    .subscribe(on: DispatchQueue.global())
    .map { _ in
        print(Thread.isMainThread)
    }
    .sink(receiveCompletion: { _ in }, receiveValue: { value in …
Run Code Online (Sandbox Code Playgroud)

ios swift combine

14
推荐指数
2
解决办法
3221
查看次数

结合@Published 属性:在更新期间从别处获取当前值

我的主要问题是我正在尝试解决(未记录的)事实,即@Published在订阅者收到更改通知后,属性不会更新属性的值。我似乎无法找到解决它的好方法。

考虑以下 aSubject@Published属性的人为组合。首先是一个简单的类:

class StringPager {
    @Published var page = 1
    @Published var string = ""
}
let pager = StringPager()
Run Code Online (Sandbox Code Playgroud)

然后是一个简单的传递主题:

let stringSubject = PassthroughSubject<String, Never>()
Run Code Online (Sandbox Code Playgroud)

为了调试,让我们订阅字符串属性并打印出来:

pager.$string.sink { print($0) }
Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好。接下来,让我们订阅主题并根据其值更改寻呼机:

stringSubject.sink { string in
  if pager.page == 1 {
    pager.string = string
  } else {
    pager.string = string.uppercased()
  }
}
Run Code Online (Sandbox Code Playgroud)

希望这个逻辑可以让我们在不在第一页时将寻呼机字符串设为大写。

现在让我们在页面更新时通过 stringSubject 发送值:

pager.$page.sink { 
  $0 == 1 ? stringSubject.send("lowercase") : stringSubject.send("uppercase") 
}
Run Code Online (Sandbox Code Playgroud)

如果我们正确地理解了这个逻辑,那么小写将始终为小写,而大写将始终为大写。不幸的是,事实并非如此。这是一个示例输出:

pager.page = 1 // lowercase
pager.page …
Run Code Online (Sandbox Code Playgroud)

ios swift combine

14
推荐指数
2
解决办法
6940
查看次数

如何使用Combine + Swift复制PromiseKit风格的链式异步流

我在一个项目中成功地使用了 PromiseKit,直到 Xcode 11 beta 破坏了 PK v7。为了减少外部依赖,我决定废弃 PromiseKit。处理链式异步代码的最佳替代品似乎是使用新组合框架的 Futures。

我正在努力使用 Combine 复制简单的 PK 语法

前任。简单的 PromiseKit 链式异步调用语法

getAccessCodeFromSyncProvider.then{accessCode in startSync(accessCode)}.then{popToRootViewController}.catch{handleError(error)}
Run Code Online (Sandbox Code Playgroud)

我明白:

async/await 的 Swift 标准库实现将解决这个问题(async/await 尚不存在,尽管Chris Latter 本人有很多喋喋不休和参与

我可以使用信号量进行复制(容易出错?

flatMap 可用于链接 Futures

我想要的异步代码应该能够按需调用,因为它涉及确保用户登录。我正在努力解决两个概念性问题。

  1. 如果我将 Futures 包装在一个方法中,sink以处理结果,则该方法似乎在订阅者被调用之前超出了范围sink

  2. 由于 Futures 只执行一次,我担心如果我多次调用该方法,我只会从第一次调用中得到旧的、陈旧的结果。要解决这个问题,也许我会使用 PassthroughSubject?这允许按需调用发布者。

问题:

  1. 我是否必须保留调用方法之外的每个发布者和订阅者
  2. 如何使用 Swift 标准库复制简单的链式异步,然后将其嵌入到我可以按需调用以从顶部重新启动链式异步调用的 swift 实例方法中?
//how is this done using Combine?
func startSync() {
 getAccessCodeFromSyncProvider.then{accessCode in startSync(accessCode)}.catch{\\handle error here}
}
Run Code Online (Sandbox Code Playgroud)

asynchronous swift combine

14
推荐指数
2
解决办法
6109
查看次数

有没有办法避免到处使用 AnyPublisher/eraseToAnyPublisher?

我只是在学习如何使用Combine。我有使用 Rx(RxSwift 和 RxJava)的经验,我注意到它非常相似。

然而,完全不同(有点烦人)的一件事是Publisher协议不对其OutputFailure类型使用泛型;它使用关联类型代替。

这意味着我无法指定多态Publisher类型(例如Publisher<Int, Error>)并简单地返回符合Publisher这些类型的任何类型。我需要AnyPublisher<Int, Error>改用,我被迫在eraseToAnyPublisher()所有地方都包括在内。

如果这是唯一的选择,那么我会忍受它。但是,我最近还了解了 Swift 中的不透明类型,我想知道是否可以使用它们来解决这个问题。

有没有办法,让我有,比方说,一个函数,返回some Publisher和使用的具体类型OutputFailure

这似乎是不透明类型的完美案例,但我不知道是否有办法既使用不透明类型又指定关联类型。

我正在想象这样的事情:

func createPublisher() -> some Publisher where Output = Int, Failure = Error {
    return Just(1)
}
Run Code Online (Sandbox Code Playgroud)

swift combine opaque-types

14
推荐指数
1
解决办法
4368
查看次数

如何告诉 SwiftUI 视图绑定到多个嵌套的 ObservableObject

我有两个类嵌套在另一个类中,它是 SwiftUI 视图中的一个可观察对象。即使嵌套类中的属性声明为@Published,它们的值(当它们更改时)也不会在主视图中更新。

这里有人问过一个类似的问题,我可以用它来让它为两个子类之一工作,但不能同时为两个子类工作。

如何告诉 SwiftUI 视图绑定到嵌套的 ObservableObjects

这是模型:

class Submodel1: ObservableObject {
  @Published var count = 0
}

class Submodel2: ObservableObject {
  @Published var count = 0
}

class Model: ObservableObject {
  @Published var submodel1: Submodel1 = Submodel1()
  @Published var submodel2: Submodel2 = Submodel2()
}
Run Code Online (Sandbox Code Playgroud)

这是主要观点:

struct ContentView: View {
  @ObservedObject var model: Model = Model()

  var body: some View {
    VStack {
      Text("Count: \(model.submodel1.count)")
        .onTapGesture {
          self.model.submodel1.count += 1
        }
      Text("Count: \(model.submodel2.count)")
        .onTapGesture {
          self.model.submodel2.count += 1
        } …
Run Code Online (Sandbox Code Playgroud)

ios swift swiftui combine

13
推荐指数
1
解决办法
3709
查看次数

SwiftUI 如何防止视图重新加载全身

基本上我试图弄清楚我的 viewModel 何时更新,它会通知视图并刷新整个身体。如何避免这种情况。例如,如果我的视图 GoLiveView 已经呈现另一个视图 BroadcasterView,然后我的 goLiveViewModel 得到更新,GoLiveView 将被刷新,它会再次创建 BroadcasterView,因为 showBroadcasterView = true。正因为如此,它会在未来引起很多问题。

struct GoLiveView: View {

@ObservedObject var goLiveViewModel = GoLiveViewModel()
@EnvironmentObject var sessionStore: SessionStore
@State private var showBroadcasterView = false
@State private var showLiveView = false

init() {
    goLiveViewModel.refresh()
}

var body: some View {
    NavigationView {
        List(goLiveViewModel.rooms) { room in // when goLiveViewModed get updated 
            NavigationLink(destination: LiveView(clientRole: .audience, room: room, showLiveView: $showLiveView))) {
                LiveCell(room: room)

            }
        }.background(Color.white)
        .navigationBarTitle("Live", displayMode: .inline)
        .navigationBarItems(leading:
            Button(action: {
                self.showBroadcasterView = true
        }, …
Run Code Online (Sandbox Code Playgroud)

xcode swift swiftui combine

13
推荐指数
1
解决办法
3566
查看次数

SwiftUI - NavigationView 中的内存泄漏

我正在尝试向模态呈现的视图导航栏添加一个关闭按钮。但是,在解雇后,我的视图模型deinit方法从未被调用。我发现问题在于它在navigationBarItem 中捕获self的位置。我不能只在navigationBarItem的动作中传递 a ,因为 View 是一个结构,而不是一个类。这是一个有效的问题还是只是缺乏知识?weak self

struct ModalView: View {

    @Environment(\.presentationMode) private var presentation: Binding<PresentationMode>
    @ObservedObject var viewModel: ViewModel

    var body: some View {

        NavigationView {
            Text("Modal is presented")
            .navigationBarItems(leading:
                Button(action: {
                    // works after commenting this line
                    self.presentation.wrappedValue.dismiss()
                }) {
                    Text("close")
                }

            )
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

memory-leaks ios swift swiftui combine

13
推荐指数
1
解决办法
2457
查看次数

标签 统计

combine ×10

swift ×10

ios ×5

swiftui ×3

asynchronous ×1

memory-leaks ×1

opaque-types ×1

xcode ×1