为什么 PassthroughSubject 以 .finished 完成?

Why is PassthroughSubject completing with .finished?

我正在探索Combine,这个问题一直困扰着我。我的理解是,如果对 PassthroughSubject 的引用存储为 AnyCancellable.

,则只要它所处的对象存在,它就会存在

在这个 loginPublisher: PassthroughSubject<User, Error> 等待更新 User 对象的示例中,Subject.Completion.finished 块在收到第一个值后立即被调用。

import SwiftUI
import Combine

class AppDelegate: NSObject, UIApplicationDelegate {
    let appState = AppState()

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        return true
    }
}

@main
struct SPMDepsTestApp: SwiftUI.App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

    var body: some Scene {
        WindowGroup {
            ContentView().environmentObject(appDelegate.appState)
        }
    }
}

struct User {
    var id: Int
    var name: String
}

class App {
    var currentUser: User? = nil

    func login (user: User) -> Future<User, Error> {
        Future { promise in
            DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
                self.currentUser = user
                promise(.success(user))
            }
        }
    }
}

class AppState: ObservableObject {
    var app = App()
    var loginPublisher = PassthroughSubject<User, Error>()

    var cancellables = Set<AnyCancellable>()

    init () {

        loginPublisher
                .receive(on: DispatchQueue.main)
                .sink(receiveCompletion: { completion in
                    switch completion {
                        case .failure(let err):
                            dump(err)
                        case .finished:
                            print("ISSUE: loginPublisher is finished") // << Why should this happen immediately?
                    }
                }, receiveValue: { user in
                    dump(user)
                })
                .store(in: &cancellables)

        if let user = app.currentUser {
            loginPublisher.send(user)
        } else {
            app.login(user: User(id: 0, name: "AAA"))
                    .receive(on: DispatchQueue.main)
                    .subscribe(loginPublisher)
                    .store(in: &cancellables)
        }
    }
}

不同的是,如果我像这样初始化 App 对象

var App = App(user: user)

所以 app.currentUser != nil 那么行

 if let user = app.currentUser {
      loginPublisher.send(user) // << loginPublisher completion (finished) block is never called
 }
 

如果(看似)loginPublisher.send() 未同步调用,为什么 PassthroughSubject 完成?

PassthroughSubject 如果订阅到完成的发布者则完成。

这是一个独立的例子:

import Combine

let subject = PassthroughSubject<Int, Never>()
let ticket = subject.sink(
    receiveCompletion: { print("completion: \([=10=])") },
    receiveValue: { print("value: \([=10=])") })

let t2 = Just(123)
    .subscribe(subject)

输出:

value: 123
completion: finished

防止完成的一种方法是附加一个永远不会完成的发布者,如下所示:

let t2 = Just(123)
    .append(Empty(completeImmediately: false))
    .subscribe(subject)

所以在你的代码中,它看起来像这样:

            app.login(user: User(id: 0, name: "AAA"))
                    .receive(on: DispatchQueue.main)
                    .append(Empty(completeImmediately: false))
                    .subscribe(loginPublisher)
                    .store(in: &cancellables)