Skip to content

Combine之基本的订阅者和发布者代码示例

just

代码示例, Just:

使用 Subscribers.Sink

先声明再组装的方式:

swift
let mySubjuect = Just(4)
let subscriber = Subscribers.Sink<Int, Never /*never表示没有数据类型*/ > { finish in
    if finish == .finished {
        print("success")
    } else { // 不会执行,just无错误        
        print("error")
    }
} receiveValue: { value in
    print(value)
}
let subscriber1 = Subscribers.Sink<Int, Never /*never表示没有数据类型*/ > { finish in
} receiveValue: { value in
    print(value)
}
mySubjuect.receive(subscriber: subscriber)
mySubjuect.receive(subscriber: subscriber1)
let mySubjuect = Just(4)
let subscriber = Subscribers.Sink<Int, Never /*never表示没有数据类型*/ > { finish in
    if finish == .finished {
        print("success")
    } else { // 不会执行,just无错误        
        print("error")
    }
} receiveValue: { value in
    print(value)
}
let subscriber1 = Subscribers.Sink<Int, Never /*never表示没有数据类型*/ > { finish in
} receiveValue: { value in
    print(value)
}
mySubjuect.receive(subscriber: subscriber)
mySubjuect.receive(subscriber: subscriber1)

使用 .publisher 与 .sink

也可以将数组、区间等通过.publisher语法作为一个发布者。
sink相关方法有:

  • .sink(receiveValue: closure)
    • 仅关注结果输出
  • sink(receiveCompletion:closure, receiveValue:closure)
    • 可以获取结束状态
swift
let range: ClosedRange<Int> = 0 ... 3
    // 专注于结果输出
_ = range.publisher
    .sink { value in
        print(value) // 0 1 2 3
    }
    // 可以获取结束状态
_ = range.publisher.sink { completion in
    if completion == .finished {
        print("completion")
    }
} receiveValue: { element in
    print("element", element)
}
let range: ClosedRange<Int> = 0 ... 3
    // 专注于结果输出
_ = range.publisher
    .sink { value in
        print(value) // 0 1 2 3
    }
    // 可以获取结束状态
_ = range.publisher.sink { completion in
    if completion == .finished {
        print("completion")
    }
} receiveValue: { element in
    print("element", element)
}

assign

keyPath

assign可以通过keyPath的方式给模型赋值:

swift
class User {
    var name: String = ""
}
let user = User()
let cancelable = ["吴彦祖"].publisher.assign(to: \.name, on: user)
print(user.name) // 吴彦祖
class User {
    var name: String = ""
}
let user = User()
let cancelable = ["吴彦祖"].publisher.assign(to: \.name, on: user)
print(user.name) // 吴彦祖

assign 与 @Published

swift
class MyModel: ObservableObject {
    @Published var id: Int = 0
}

let model = MyModel()
Just(100).assign(to: &model.$id)
print(model.id)

_ = Just(200).assign(to: \.id, on: model)
print(200)
class MyModel: ObservableObject {
    @Published var id: Int = 0
}

let model = MyModel()
Just(100).assign(to: &model.$id)
print(model.id)

_ = Just(200).assign(to: \.id, on: model)
print(200)

上述写法在SwiftUI里面随处可见

https://devv.ai/zh/search?threadId=d98yqg2heiv4

  • @Published 修饰后,可以简化assign的操作
  • 上述示例代码中ObservableObject也可以去掉,在 SwiftUI 中,@ObservableObject 用于标记一个类,该类可以存储数据并在更改时触发视图更新。而 @Published 则用于标记可观察对象中应该在更改时触发视图更新的属性。

[iOS][Swift]CombineでUIKitのUI更新を実装してみる #Swift - Qiita

future

swift
let future = Future<Int, Never> { promise in
    // 此处可执行耗时操作
    promise(.success(200))
}
let subscribe = Subscribers.Sink<Int, Never> { statue in
    if statue == .finished {
        print("finished")
    }
} receiveValue: { value in
    print(value)
}
future.subscribe(subscribe)
let future = Future<Int, Never> { promise in
    // 此处可执行耗时操作
    promise(.success(200))
}
let subscribe = Subscribers.Sink<Int, Never> { statue in
    if statue == .finished {
        print("finished")
    }
} receiveValue: { value in
    print(value)
}
future.subscribe(subscribe)

operator

swift
let publisher = Just(5)
let tranform = Publishers.Map<Just<Int>, String>.init(upstream: publisher) { value in
    "字符串值:\(value)"
}

let subscribe = Subscribers.Sink<String, Never>.init { _ in
} receiveValue: { value in
    print(value) // 字符串值:5
}
tranform.subscribe(subscribe)
// 不声明`subscribe`,直接使用也可以
// tranform.sink { value in
//    print(value)
// }
let publisher = Just(5)
let tranform = Publishers.Map<Just<Int>, String>.init(upstream: publisher) { value in
    "字符串值:\(value)"
}

let subscribe = Subscribers.Sink<String, Never>.init { _ in
} receiveValue: { value in
    print(value) // 字符串值:5
}
tranform.subscribe(subscribe)
// 不声明`subscribe`,直接使用也可以
// tranform.sink { value in
//    print(value)
// }

简化写法:

swift
let publisher = Just(5)
_ = publisher.map { value in
    "-> String Value: \(value)"
}.sink { value in
    print(value)
}

// 示例2: 
let publisher = Just(5)
_ = publisher
	.filter { item in
		item >= 5
	}
	.map { value in
		"-> String Value: \(value)"
	}.sink { value in
		print(value)
	}
let publisher = Just(5)
_ = publisher.map { value in
    "-> String Value: \(value)"
}.sink { value in
    print(value)
}

// 示例2: 
let publisher = Just(5)
_ = publisher
	.filter { item in
		item >= 5
	}
	.map { value in
		"-> String Value: \(value)"
	}.sink { value in
		print(value)
	}

CurrentValueSubject

CurrentValueSubject(value) 可以保存和发布最后的数据(来自send方法和其他发布者),可设置默认数据。 可以类比RxSwift中的BehaviorRelay

swift
let publisher = CurrentValueSubject<Int, Never>(20)
let subscribe = Subscribers.Sink<Int, Never>.init { _ in
} receiveValue: { value in
    print("here: \(value)")
}

publisher.subscribe(subscribe)

var cancellables = Set<AnyCancellable>()
publisher.sink { value in
    print("there", value)
}.store(in: &cancellables)
// 此处必须加 `.store(in: )` , 否则只会执行一次。 因为执行一次后就被释放,不会持续接收事件

publisher.send(21)
publisher.send(22)
//输出:
//here: 20
//there 20
//there 21
//heare: 21
//there 22
//here: 22
let publisher = CurrentValueSubject<Int, Never>(20)
let subscribe = Subscribers.Sink<Int, Never>.init { _ in
} receiveValue: { value in
    print("here: \(value)")
}

publisher.subscribe(subscribe)

var cancellables = Set<AnyCancellable>()
publisher.sink { value in
    print("there", value)
}.store(in: &cancellables)
// 此处必须加 `.store(in: )` , 否则只会执行一次。 因为执行一次后就被释放,不会持续接收事件

publisher.send(21)
publisher.send(22)
//输出:
//here: 20
//there 20
//there 21
//heare: 21
//there 22
//here: 22

swiftui中使用的例子:

swift
import SwiftUI
import Combine

// 创建一个CurrentValueSubject对象
class DataModel: ObservableObject {
    var currentValueSubject = CurrentValueSubject<Int, Never>(0)    
    func updateValue(newValue: Int) {
        currentValueSubject.send(newValue)
    }
}

// SwiftUI视图
struct ContentView: View {
    @StateObject var dataModel = DataModel()
    
    var body: some View {
        VStack {
            Text("Current Value: \(dataModel.currentValueSubject.value)")
                .padding()
            
            Button("Update Value") {
                dataModel.updateValue(newValue: Int.random(in: 1...100))
            }
        }
    }
}

// 在ContentView中订阅CurrentValueSubject对象
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
import SwiftUI
import Combine

// 创建一个CurrentValueSubject对象
class DataModel: ObservableObject {
    var currentValueSubject = CurrentValueSubject<Int, Never>(0)    
    func updateValue(newValue: Int) {
        currentValueSubject.send(newValue)
    }
}

// SwiftUI视图
struct ContentView: View {
    @StateObject var dataModel = DataModel()
    
    var body: some View {
        VStack {
            Text("Current Value: \(dataModel.currentValueSubject.value)")
                .padding()
            
            Button("Update Value") {
                dataModel.updateValue(newValue: Int.random(in: 1...100))
            }
        }
    }
}

// 在ContentView中订阅CurrentValueSubject对象
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

代码示例:发送失败消息以终止,再往后的消息不会收到

swift
var cancellables = Set<AnyCancellable>()
let publisher = CurrentValueSubject<Int, MyError>(20)
let subscribe = Subscribers.Sink<Int, MyError>.init { ff in
    if ff == .finished {
        print("finished")
    } else {
        print("error")
    }
    switch ff {
    case .finished:
        print("finished")
    case .failure(let err):
        print(err)
    }
} receiveValue: { value in
    print("here: \(value)")
}

publisher.subscribe(subscribe)

publisher.send(1)
publisher.send(2)
publisher.send(3)
publisher.send(completion: .failure(.uploadFailed))
publisher.send(4)
// here: 20
// here: 1
// here: 2
// here: 3
// error
// uploadFailed
var cancellables = Set<AnyCancellable>()
let publisher = CurrentValueSubject<Int, MyError>(20)
let subscribe = Subscribers.Sink<Int, MyError>.init { ff in
    if ff == .finished {
        print("finished")
    } else {
        print("error")
    }
    switch ff {
    case .finished:
        print("finished")
    case .failure(let err):
        print(err)
    }
} receiveValue: { value in
    print("here: \(value)")
}

publisher.subscribe(subscribe)

publisher.send(1)
publisher.send(2)
publisher.send(3)
publisher.send(completion: .failure(.uploadFailed))
publisher.send(4)
// here: 20
// here: 1
// here: 2
// here: 3
// error
// uploadFailed

@Published 和 CurrentValueSubject 之间还有一些区别和优势。

  1. 错误类型:@Published 的发布者的错误类型是 Never,而 CurrentValueSubject 可以指定错误类型。
  2. 使用范围:@Published 只能在类中使用,而 CurrentValueSubject 可以在任何地方使用,包括结构体和枚举。
  3. 订阅者行为:@Published 不能直接用作订阅者,而 CurrentValueSubject 可以直接订阅。
  4. 协议声明:@Published 不能在协议声明中使用,而 CurrentValueSubject 可以在协议中声明并使用。
  5. 设置初始值:CurrentValueSubject 可以在初始化时设置初始值,而 @Published 需要在声明时设置初始值。

总的来说,@Published 更适合简单的自动更新视图的场景

PassthroughSubject

和CurrentValueSubject的区别是没有默认值:

swift
let publisher = PassthroughSubject<Int, Never>()
// 写法1
let subscribe = Subscribers.Sink<Int, Never>.init { _ in
} receiveValue: { value in
    print("here: \(value)")
}

publisher.subscribe(subscribe)

// 写法2
var cancellables = Set<AnyCancellable>()
publisher.sink { value in
    print("there", value)
}.store(in: &cancellables)

publisher.send(21)
publisher.send(22)
let publisher = PassthroughSubject<Int, Never>()
// 写法1
let subscribe = Subscribers.Sink<Int, Never>.init { _ in
} receiveValue: { value in
    print("here: \(value)")
}

publisher.subscribe(subscribe)

// 写法2
var cancellables = Set<AnyCancellable>()
publisher.sink { value in
    print("there", value)
}.store(in: &cancellables)

publisher.send(21)
publisher.send(22)