为什么 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)
我正在探索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)