# Publisher - Overview
νΌλΈλ¦¬μ λ μκ°μ λ°λΌ λ°©μΆνλ μΌλ ¨μ κ°λ€μ μ μΈνλ€. μ΄λ κ² λ°©μΆλ κ°λ€μ νλ μ΄μμ ꡬλ μμκ² μ λ¬ν μ μλ€.
ꡬλ
μμ Input
κ³Ό Failure
νμ
μ νΌλΈλ¦¬μ
μ μ μΈλ Output
, Failure
νμ
κ³Ό μΌμΉν΄μΌ νλ€. νΌλΈλ¦¬μ
λ ꡬλ
μμ ꡬλ
μ λ°μλ€μ΄κΈ° μν΄ receive(subscriber:)
λ©μλλ₯Ό λ΄λΆμ μΌλ‘ ꡬννλ€. Publisher
μ€μ²© ꡬ쑰체λ₯Ό μ΄ν΄λ³΄λ©΄ λ΄λΆμ receive
ν¨μ νμ
μ΄ μ μλ κ²μ λ³Ό μ μλ€.
νΌλΈλ¦¬μ λ μλμ λ©μλλ€μ ꡬλ μμκ² νΈμΆν μ μλ€.
receive(subscription:)
: ꡬλ μμ²μ μΉμΈνκ³Subscription
μΈμ€ν΄μ€λ₯Ό λ°ννλ€. ꡬλ μλSubscription
μΈμ€ν΄μ€λ₯Ό ν΅ν΄ νΌλΈλ¦¬μ μκ² κ°μ μꡬνκ³ κ°μ λ°©μΆμ μ·¨μν μ μλ€.receive(_:)
: νΌλΈλ¦¬μ λ‘λΆν° νλμ κ°μ ꡬλ μμκ² μ λ¬νλ€.receive(completion:)
: ꡬλ μμκ² κ°μ λ°©μΆμ΄ λλ¬μμ μλ¦°λ€.
λͺ¨λ νΌλΈλ¦¬μ
λ Publisher
νλ‘ν μ½ μ±νμ λ°λΌ receive
λ©μλλ₯Ό λ°λμ ꡬνν΄μΌ νλ€.
Publishers
μ΄κ±°ν λ΄λΆ ꡬνμ μ΄ν΄λ³΄λ©΄ μ λ§μ νΌλΈλ¦¬μ
λ€μ΄ μ μλμ΄ μλ κ²μ λ³Ό μ μλ€. μ΄λ νΌλΈλ¦¬μ
체μ΄λμ μν΄ μ€νΌλ μ΄ν°λ‘ μ¬μ©λ λͺ©μ μΌλ‘ λ§λ€μ΄μ§ κ²μ΄λ€. μλ₯Ό λ€μ΄ map
μ€νΌλ μ΄ν°μ κ²½μ° Publishers.Map
ν΄λμ€μ μΈμ€ν΄μ€λ₯Ό 리ν΄νλλ‘ κ΅¬νλμ΄ μλ€.
Publisher vs AsyncSequence
νΌλΈλ¦¬μ
μ μ€μννΈ νμ€ λΌμ΄λΈλ¬λ¦¬μ AsyncSequence
λ μκ°μ λ°λΌ κ°μ λ°©μΆνλ€λ μ μμ λμΌνμ§λ§ νΌλΈλ¦¬μ
λ ꡬλ
μλ₯Ό ν΅ν΄ νΌλΈλ¦¬μ
λ‘λΆν° κ°μ μμ²νλ λ°λ©΄ AsyncSequence
λ for-await-in
μ ν΅ν΄ λ°©μΆλλ κ°λ€μ μννλ€.
λλ€ λͺ¨λ νν°λ§ λ° λ§€ν κ΄λ ¨ μ°μ°μλ₯Ό μ 곡νλ€.
let endpointURL = URL(string: "https://zeddios.tistory.com")!
let lineCount = endpointURL.lines.map { $0.count } β
// AsyncSequence μΈμ€ν΄μ€μ map μ°μ°μ
do {
for try await line in lineCount {
print(line)
}
} catch {
}
// μΆμ²: https://zeddios.tistory.com/1339 [ZeddiOS:ν°μ€ν 리]
μ»΄λ°μΈμμλ AsyncSequence
μμ μ 곡νμ§ μλ μκ° κΈ°λ°μ μ€νΌλ μ΄ν° debounce
, throttle
μ μΆκ°λ‘ μ 곡νλ©° νΌλΈλ¦¬μ
κ° κ²°ν© μ€νΌλ μ΄ν°μΈ merge(with:)
, combineLatest(_: _:)
μ°μ°μλ€λ μ 곡νλ€.
AsyncSequence
μ μ»΄λ°μΈμ νΌλΈλ¦¬μ
λ₯Ό λΈλ¦Ώμ§νκΈ° μν΄μλ νΌλΈλ¦¬μ
μμ λ°©μΆνλ κ°λ€μ AsyncSequence
λ‘ λ
ΈμΆμμΌ κ΅¬λ
μλ₯Ό ν΅ν κ°μ μ²λ¦¬λ°©λ² λμ for-await-in
μΌλ‘ μνν μ μλ€.
νΌλΈλ¦¬μ
νλ‘ν μ½μ μ±ννμ¬ μ§μ μ μνλ λμ μ»΄λ°μΈμμ μ 곡νλ λͺκ°μ§ νμ
μ νμ©νλ©΄ 컀μ€ν
νΌλΈλ¦¬μ
λ₯Ό μ½κ² μ μν μ μλ€. PassThroughSubject
μ κ°μ Subject
νμν΄λμ€ νμ
μ μ¬μ©νλ©΄ λλ€. (PassThroughSubject
λ RxSwiftμμ PublishSubject
, CurrentValueSubject
λ BehaviorSubject
μ λμΌνλ€.)
@Published
νλ‘νΌν° λνΌλ₯Ό μ¬μ©νλ©΄ μμ±μ κ°μ΄ λ³ν λλ§λ€ κ°μ λ°©μΆνλ νΌλΈλ¦¬μ
λ₯Ό μ½κ² μ μν μλ μλ€.
# Publisher - Output, Failure
Outputμ νΌλΈλ¦¬μ
λ‘λΆν° λ°©μΆλλ κ°μ νμ
μ μλ―Ένλ€. Failure
λ νΌλΈλ¦¬μ
κ° λ°©μΆν μ μλ μλ¬μ νμ
μ μλ―Ένλ€. νΌλΈλ¦¬μ
κ° μλ¬λ₯Ό λ°©μΆνμ§ μμ κ²½μ° Never
λ₯Ό μ¬μ©νλ€.
# Operators
RxSwift
μμ λ€λ€λ³΄μ§ μμ μ€νΌλ μ΄ν°λ₯Ό μΆκ°λ‘ μ 리νλ€.
# map, tryMap, mapError
RxSwift map
μ€νΌλ μ΄ν°μ λμΌνλ€. μ
μ€νΈλ¦Ό νΌλΈλ¦¬μ
λ‘λΆν° λ°μ μ리먼νΈλ₯Ό ν΄λ‘μ μ λ°λΌ λ³νμν¨ λ€ μλ‘μ΄ μ΅μ λ²λΈλ‘ λ°ννλ€. tryMap
μ ν΄λ‘μ μμ μλ¬λ₯Ό throw
νλ κ²½μ° sink completion
ν΄λ‘μ μμ μ²λ¦¬νκΈ° μν΄ μ¬μ©νλ€.
struct ParseError: Error {}
func romanNumeral(from:Int) throws -> String {
let romanNumeralDict: [Int : String] =
[1:"I", 2:"II", 3:"III", 4:"IV", 5:"V"]
guard let numeral = romanNumeralDict[from] else {
throw ParseError()
}
return numeral
}
let numbers = [5, 4, 3, 2, 1, 0]
// 0μμ μλ¬ λ°©μΆ
let cancellable = numbers.publisher
.tryMap { try romanNumeral(from: $0) }
.sink(
receiveCompletion: { print ("completion: \($0)") },
receiveValue: { print ("\($0)", terminator: " ") }
)
mapError
μ€νΌλ μ΄ν°λ λ°©μΆλ μλ¬ νμ
μ mapμ²λΌ λ€λ₯Έ ννλ‘ λ³ννκ³ μΆμλ μ¬μ©νλ€.
struct MyGenericError: Error { var wrappedError: Error }
divisors.publisher
.tryMap { try myDivide(1, $0) }
.mapError { MyGenericError(wrappedError: $0) }
// ....
# replaceNil(with:)
replaceNil
μ€νΌλ μ΄ν°λ μ
μ€νΈλ¦Ό νΌλΈλ¦¬μ
μμ μ λ¬λ μμ μ€ nilκ°μ with νλΌλ―Έν°μ μ λ¬λ κ°μΌλ‘ λ체ν΄μ€λ€.
let numbers: [Double?] = [1.0, 2.0, nil, 3.0]
numbers.publisher
.replaceNil(with: 0.0)
.sink { print("\(String(describing: $0))", terminator: " ") }
.cancel()
# removeDuplicates
Output
νμ
μ΄ Equatable νλ‘ν μ½μ ꡬνν κ²½μ° λ°©μΆλλ μμλ€μ μ€λ³΅μ μ κ±°νμ¬ λ°ννλ€.
let numbers: [Int] = [1,1,2,2,3,4]
numbers.publisher
.removeDuplicates()
.sink(receiveValue: {print($0)})
by ν΄λ‘μ λ₯Ό ν΅ν΄ μ§μ λμΌμ± μ¬λΆ λ‘μ§μ ꡬμ±ν μλ μλ€.
let points = [Point(x: 0, y: 0), Point(x: 0, y: 1),
Point(x: 1, y: 1), Point(x: 2, y: 1)]
cancellable = points.publisher
.removeDuplicates { prev, current in
prev.x == current.x
}
.sink { print("\($0)", terminator: " ") }
# replaceEmpty
μνμ€μμ λ°©μΆνλ μμκ° empty
μνμΌλ μλ‘μ΄ κ°μΌλ‘ λ체νλ€.
let numbers: [String] = []
numbers.publisher
.replaceEmpty(with: "ABC")
.sink(receiveValue: { print($0) })
# collect
λ°©μΆλλ μμλ€μ μ’
ν©νμ¬ νλμ λ°°μ΄λ‘ ν©μΉλ€. count
νλΌλ―Έν°λ λ΄μ λ²νΌκ°μ μ§μ νκ³ λͺ¨λ λ΄μΌλ©΄ λ°©μΆνλ€. λͺ¨λ μμλ₯Ό μννλ©° λ²νΌμ κ³μν΄μ μ±μ΄λ€.
let numbers = 1..<10
numbers.publisher
.collect()
// collect(3) -> 3κ°μ© λͺ¨μμ λ€ λͺ¨μμ§λ©΄ λ°©μΆ
.sink(receiveValue: {print($0)})
# ignoreOutput
λ°©μΆλλ κ°λ€μ 무μνκ³ completion
μ failure
λ§ λ°©μΆνλ€.
# reduce vs scan
scan
μ λμ κ³μ°λλ λͺ¨λ κ°μ λ§€λ² λ°©μΆνκ³ , reduce
λ μ΅μ’
μ°μ°λ λ§μ§λ§ κ²°κ³Όλ§ λ°©μΆνλ€.
let numbers = 1..<4
numbers.publisher
.scan(0) { $0 + $1 }
.sink(receiveValue: {print($0)}) // 1 3 6
numbers.publisher
.reduce(0, { $0 + $1 })
.sink(receiveValue: {print($0)}) // 6
# allSatisfy
μ
μ€νΈλ¦ΌμΌλ‘λΆν° μ λ¬λ μμ μ μ²΄κ° allSatisfy
μ€νΌλ μ΄ν°μ μ μλ ν΄λ‘μ 쑰건μμ λ§μ‘±λλμ§ μ¬λΆλ₯Ό Bool
κ°μΌλ‘ 리ν΄νλ€.
let targetRange = (-1...100)
let numbers = [-1, 0, 10, 5]
numbers.publisher
.allSatisfy { targetRange.contains($0) }
.sink { print("\($0)") } // true
# drop(untilOutputFrom:), drop(while:), tryDrop(while:), dropFirst(_ count: Int)
drop(untilOutputFrom:)
: νλΌλ―Έν°μ μ λ¬λλ νΌλΈλ¦¬μ μμ μμκ° λ°©μΆλκΈ° μ κΉμ§ μ μ€νΈλ¦Όμμ λ°©μΆλλ μμλ€μ 무μνλ€.drop(while:)
:while
ν΄λ‘μ μ μ λ¬λ 쑰건μμ΄ trueμΈ λμμ μ μ€νΈλ¦Όμμ λ°©μΆλλ μμλ€μ 무μνλ€.tryDrop(while:)
: μλ¬λ₯Ό λμ§ μ μλdrop(while:)
μ€νΌλ μ΄ν°dropFirst(_ count: Int)
: 0λ²μ§ΈλΆν° countκΉμ§μ μμλ€μ λ²λ¦Ό
# append(_ elements: Self.Output...), append(_ elements: S), append
(_ publisher: P) & prepend
append(_ elements: Self.Output...)
: Outputνμ κ³Ό λμΌν λ°μ΄ν°λ€μ κ°λ³ νλΌλ―Έν° ννλ‘ μ λ¬νλ©΄ μ μ€νΈλ¦Όμμ λ°©μΆλλ μμμ μ΄μ΄λΆμ¬μ§λ€.append<S>(_ elements: S)
: νλΌλ―Έν°μ μνμ€λ₯Ό μ λ¬νλ©΄ μ μ€νΈλ¦Όμμ λ°©μΆλ μνμ€μ μ΄μ΄λΆμ¬μ§λ€.append<P>(_ publisher: P)
: μ μ€νΈλ¦Όμμ λ°©μΆλλ μμμ μ΄μ΄ νλΌλ―Έν°μ μ λ¬λ λ€μ΄μ€νΈλ¦Ό νΌλΈλ¦¬μ μμ λ°©μΆλλ μμλ₯Ό μ΄μ΄λΆμΈλ€.prepend(_ elements: Self.Output...)
: appendμ λμΌνκ² λμνμ§λ§, μ½μ λλ μμΉλ§ 맨 μ
# prefix(_: Int), prefix(while: ), tryPrefix(while:), prefix
(untilOutputFrom publisher: P)
prefix(Int)
: νλΌλ―Έν°μ μ λ¬λ κ°―μ κ° λ§νΌ μμλ₯Ό λ°©μΆνλ€.prefix(while: )
: 쑰건μμ΄ trueμΈ λμμλ§ λ°©μΆνκ³ falseκ° λ μ΄νμλ λ°©μΆνμ§ μλλ€.tryPrefix
: μλ¬λ₯Ό λμ§ μ μλprefix(while:)
prefix(untilOutputFrom)
: λ€μ΄μ€νΈλ¦Ό νΌλΈλ¦¬μ μμ μμλ₯Ό λ°©μΆνκΈ° μ κΉμ§ κ³μν΄μ μ μ€νΈλ¦Όμ μμ λ°©μΆ
let upstream = PassthroughSubject<Int,Never>()
let second = PassthroughSubject<String,Never>()
let cancellable = upstream
.prefix(untilOutputFrom: second)
.sink(receiveValue: { print($0) })
upstream.send(0)
upstream.send(1)
second.send("A")
upstream.send(2)
// 0 1
# first(), first(where: (Self.Output) -> Bool), last
first
: ν΄λΉ μ€νΌλ μ΄ν° νΈμΆ ν μ΅μ νλμ μμκ° μ μ€νΈλ¦ΌμΌλ‘λΆν° λ°©μΆλ λκΉμ§unlimited
μμ²μ μ€ννλ€.first(where: )
: 쑰건μμ λ§λ 첫 λ²μ§Έ μμλ₯Ό λ°©μΆνλ€last
: firstμ λ°λμ΄λ©°last(where:)
λ λμΌ
# output(at:Int), output(in range: R)
output(at:)
:at
μμΉμ ν΄λΉνλ μμ λ°©μΆoutput(in: range)
: rangeμ ν΄λΉνλ μμλ€ λ°©μΆ, λ²μ λ²μ΄λλ©΄ μ무κ²λ λ°©μΆ μν¨
let a = 1...10
a.publisher
.output(in: 3...4)
.sink(receiveValue: {print($0)}) // 4 5
.cancel()
# combineLatest
let pub1 = PassthroughSubject<Int, Never>()
let pub2 = PassthroughSubject<Int, Never>()
cancellable = pub1
.combineLatest(pub2) { (first, second) in
return first * second
}
.sink { print("Result: \($0).") }
pub1.send(1)
pub1.send(2)
pub2.send(2)
pub1.send(9)
pub1.send(3)
pub2.send(12)
pub1.send(13)
combineLatest
λ μ
μ€νΈλ¦Όκ³Ό λ€μ΄μ€νΈλ¦Ό νΌλΈλ¦¬μ
μμ λͺ¨λ νλ μ΄μμ μμλ₯Ό λ°©μΆνκΈ° μ κΉμ§λ μ무 μμλ λ°©μΆνμ§ μλλ€.
# merge, zip
merge(with: publisher)
: RxSwift mergeμ λμΌzip(_ other: publisher)
: μΈλ±μ€λΌλ¦¬ λ¬Άμ΄μ ννλ°ν
# flatMap, switchToLatest
flatMap
: μ μ€νΈλ¦Όμμ λ°©μΆλ μμλ₯Ό μλ‘μ΄ νΌλΈλ¦¬μ λ‘ λ¦¬ν΄νμ¬ κ΅¬λ μμκ² μ λ¬νλ€.send
λ₯Ό ν΅ν΄ μ λ¬λ ν¬λ리μ€νΈ ν¬μ€νΈ idκ°μ κΈ°λ°μΌλ‘dataTaskPublisher
νΌλΈλ¦¬μ λ₯Ό λ§λ€μ΄ ꡬλ μμκ² μ λ¬νλ€.- μ λ¬κ³Ό λμμ μ
μ€νΈλ¦Ό νΌλΈλ¦¬μ
μ λμμ΄
complete
λλ κ²μ΄ μλλΌ, μλ‘ λ¦¬ν΄λ νΌλΈλ¦¬μ λμμ΄ λλ λκΉμ§ κΈ°λ€λ¦¬κ² λλ€. (μλμ μΌλ‘ λΉλκΈ°μ²λ¦¬)
switchToLatest
: flatMapμ λ°©μΆλλ λͺ¨λ μμμ λ°λΌ μλ‘μ΄ νΌλΈλ¦¬μ λ₯Ό 맀λ²λ§λ€ μμ± λ° λ°©μΆνμ§λ§switchToLatest
λ μλμΌλ‘ 맨 λ§μ§λ§ νΌλΈλ¦¬μ μ λν΄μλ§ λ°©μΆνλ€.
// flatMap μμμ½λ
struct Post: Codable {
let userId: Int
let id: Int
let title: String
let body: String
}
var postPublisher = PassthroughSubject<Int, URLError>()
let cancellable = postPublisher.flatMap { id -> URLSession.DataTaskPublisher in
let url = URL(string:"https://jsonplaceholder.typicode.com/posts/\(id)")!
return URLSession.shared.dataTaskPublisher(for: url)
}
.sink(
receiveCompletion: { completion in
// Handle publisher completion (normal or error).
},
receiveValue: {
print(try? JSONDecoder().decode(Post.self, from: $0.data))
// Process the received data.
}
)
postPublisher.send(1)
postPublisher.send(2)
postPublisher.send(3)
sleep(5)
// switchToLatest μμμ½λ
struct User {
let name: CurrentValueSubject<String, Never>
}
let userSubject = PassthroughSubject<User, Never>()
let cancellable = userSubject
.map(\.name)
// .flatMap(\.name)
.switchToLatest()
.sink { print($0) }
let user = User(name: .init("User 1"))
let anotherUser = User(name: .init("AnotherUser 1"))
userSubject.send(user)
userSubject.send(anotherUser)
anotherUser.name.send("AnotherUser 2")
user.name.send("User 2")
flatMap
μ μ¬μ©νλ κ²½μ° User μΈμ€ν΄μ€μ name νΌλΈλ¦¬μ
μμ μλ‘μ΄ μμ λ°©μΆλλ§λ€ μλ‘μ΄ λ¬Έμμ΄μ λ°©μΆνμ¬ κ΅¬λ
μμκ² μ λ¬νλ€. λ°λΌμ μΆλ ₯κ²°κ³Όκ° User 1
, AnotherUser 1
, AnotherUser 2
, User 2
κ° λλ€.
μ μ½λμμμ userSubject
μ μ λ¬λλ νΌλΈλ¦¬μ
λ₯Ό map
μΌλ‘ 리ν΄νκ³ , switchToLatest
λ₯Ό νΈμΆνλ©΄ λ§μ§λ§μ send(anotherUser)
ννλ‘ μ λ¬λ νΌλΈλ¦¬μ
κ° μμ ν λ체νκ² λλ€. λ°λΌμ μΆλ ₯κ²°κ³Όλ AnotherUser
μ κ΄ν΄μλ§ λνλλ€.
# encode, decode
struct Article: Codable {
let title: String
let author: String
let pubDate: Date
}
let dataProvider = PassthroughSubject<Article, Never>()
let cancellable = dataProvider
.encode(encoder: JSONEncoder()) // μΈμ½λ© ..
.decode(type: Article.self, decoder: JSONDecoder()) // λμ½λ©..
.sink(receiveCompletion: { print ("Completion: \($0)") },
receiveValue: { data in
print(data) // Article μΈμ€ν΄μ€ μΆλ ₯
})
dataProvider.send(Article(title: "My First Article", author: "Gita Kumar", pubDate: Date()))
# multicast, share
multicast
: μ¬λ¬κ°μ λ€μ΄μ€νΈλ¦Ό νΌλΈλ¦¬μ κ° νλμ μ μ€νΈλ¦Όκ³Ό μ°κ²°λμ΄ μμλ, μ μ€νΈλ¦ΌμΌλ‘λΆν° μ λ¬λ μ΄λ²€νΈ λΉ νλμreceive
λ§ νΈμΆλκΈ° μν λ μ¬μ©νλ€. μ μ€νΈλ¦Ό νΌλΈλ¦¬μ μ λμμ΄ λ¬΄κ±°μΈλ μ¬μ©νλ©΄ μ’λ€. ν΄λ‘μ μλ ꡬλ μ 곡μ νκ² λ νΌλΈλ¦¬μ νλλ₯Ό μΆκ°λ‘ μ μνμ¬ λ¦¬ν΄νλ€.multicast
νΈμΆμConnectablePublisher
λ₯Ό μ±ννλMulticast
νμ νΌλΈλ¦¬μ λ‘ λ³νλκΈ° λλ¬Έμconnect
μ€νΌλ μ΄ν°λ₯Ό νΈμΆν΄μ€μΌ μμλ€μ μ μμ μΌλ‘ λ°©μΆνκ² λλ€.
share
:share
λ μ§μ connect
λ₯Ό νΈμΆν νμ μμ΄ λ΄λΆμ μΌλ‘autoconnect
λ₯Ό νΈμΆνλ€. λ°λΌμ 첫 ꡬλ μμ sinkλλΆν° μμλ€μ΄ λ°λ‘ λ°©μΆλλ€. ꡬλ μ νΌλΈλ¦¬μ λ€μ΄ κ°μΆ°μ§κ³ μ μ€νΈλ¦ΌμΌλ‘λΆν° μμλ€μ λ°©μΆν΄μΌ νκΈ° λλ¬Έμ 보ν΅delay
μ€νΌλ μ΄ν°μ ν¨κ» μ¬μ©νλ€.
let pub = ["First", "Second", "Third"].publisher
.map( { return ($0, Int.random(in: 0...100)) } )
.print("Random")
let cancellable1 = pub
.sink { print ("Stream 1 received: \($0)")}
let cancellable2 = pub
.sink { print ("Stream 2 received: \($0)")}
μ μ½λλ λ©ν°μΊμ€νΈλ₯Ό μ¬μ©νμ§ μκΈ° λλ¬Έμ μ
μ€νΈλ¦Ό pub
νΌλΈλ¦¬μ
μμ λ°©μΆλλ κ° μμμ λ
립μ μΈ κ΅¬λ
μκ° μμ±λλ€.
let pub = ["First", "Second", "Third"].publisher
.map( { return ($0, Int.random(in: 0...100)) } )
.print("Random")
.multicast { PassthroughSubject<(String, Int), Never>() } // μΆκ°
let cancellable1 = pub
.sink { print ("Stream 1 received: \($0)")}
let cancellable2 = pub
.sink { print ("Stream 2 received: \($0)")}
pub.connect() // μΆκ°
let pub = (1...3).publisher
.delay(for: 1, scheduler: DispatchQueue.global())
.map( { _ in return Int.random(in: 0...100) } )
.print("Random")
.share()
let cancellable1 = pub
.sink { print ("Stream 1 received: \($0)")}
let cancellable2 = pub
.sink { print ("Stream 2 received: \($0)")}
μ μ½λμμ delay
λ₯Ό μ£Όμμ²λ¦¬νλ©΄ Stream 1 received
λ§ μ¬λ¬λ² νΈμΆλλ€.
# eraseToAnyPublisher
RxSwiftμμμ asObservable
κ³Ό λμΌνλ€. ν΄λΉ μ€νΌλ μ΄ν°λ₯Ό μ¬μ©νλ μ€μ μ΄μ λ TypeEraser
λ‘μμ μν μ μ£ΌκΈ° μν¨μ΄λ€. νΌλΈλ¦¬μ
μΈλΆ νμ
μ κ°μΆκ³ AnyPublisher
μ ννλ‘λ§ μΆμννκΈ° μν΄ μ¬μ©νλ€.
let x = PassthroughSubject<String, Never>()
.flatMap { name in
return Future<String, Error> { promise in
promise(.success(""))
}.catch { _ in
Just("No user found")
}.map { result in
return "\(result) foo"
}
}
// .eraseToAnyPublisher()
// .sink(receiveValue: {print($0)})
μ μ½λμμ flatMap
μ€νΌλ μ΄ν°λ‘ λ°νλλ νΌλΈλ¦¬μ
λ ꡬ체 νμ
μ κ°μ§κ³ μμ΄μ λ°μ΄ν° νμ΄νλΌμΈμ νλ¦μ΄ μΈλΆλ‘ λ
ΈμΆλλ€. μ΄μ°¨νΌ ꡬλ
μμκ² μ λ¬λλ λ°μ΄ν°λ₯Ό λ¨μ νμ©λ§ ν κ±°λΌλ©΄ νΌλΈλ¦¬μ
μ ꡬ체μ μΈ νμ
μ λΆνμνκΈ° λλ¬Έμ eraseToAnyPublisher
λ₯Ό νΈμΆνμ¬ AnyPublisher
μ€νΌλ μ΄ν°λ₯Ό μΆκ°νλ κ²μ΄λ€.
# assign(toπ)
assign(ReferenceWritableKeyPath<Root, Self.Output>, on object: Root)
: μ μ€νΈλ¦Όμμ λ°©μΆλλ μμλ€μ κ°μ§κ³ ν€ν¨μ€λ₯Ό ν΅ν΄ μΈμ€ν΄μ€μ μμ± ν λΉμ μ§ννλ€.assign(to published: inout Published<Self.Output>.Publisher)
:Published
μμ±μ μ§μ μμ±μ ν λΉνλ€.
class MyClass {
var anInt: Int = 0 {
didSet {
print("anInt was set to: \(anInt)", terminator: "; ")
}
}
}
var myObject = MyClass()
let myRange = (0...2)
cancellable = myRange.publisher
.assign(to: \.anInt, on: myObject)
class MyModel2: ObservableObject {
@Published var id: Int = 0
}
let model2 = MyModel2()
Just(100).assign(to: &model2.$id)
# AsyncPublisher
νΌλΈλ¦¬μ
μλ values
μμ±μ΄ μλλ°, ν΄λΉ μμ±μ ν΅ν΄ λ°©μΆλλ κ°λ€μ Swiftμ async-await ννλ‘ λΉλκΈ°μ μΈ μνκ° κ°λ₯νλ€. AsyncPublisher
κ° AsyncSequence
λ₯Ό μ±ννκΈ° λλ¬Έμ κ°λ₯ν κ²
let numbers: [Int] = [1, 2, 3, 4, 5]
let filtered = numbers.publisher
.filter { $0 % 2 == 0 }
for await number in filtered.values
{
print("\(number)", terminator: " ")
}
printλ₯Ό μ¬μ©νλ©΄ λλ²κΉ μ΄ κ°λ₯νλ€.
let numbers: [Int] = [1, 2, 3, 4, 5]
let filtered = numbers.publisher
.filter { $0 % 2 == 0 }
.print("DEBUG: ")
.sink(receiveValue: {print($0)})
# AnyPublisher
AnyPublisher
λ eraseToAnyPublisher
μ€νΌλ μ΄ν°μ λν μ€λͺ
μμ μΈκΈλμλ― μ체μ μΈ μμ±μ κ°κ³ μμ§ μκ³ , λ°©μΆλ μμ λ° μλ£ κ°μ μ λ¬νκΈ° μν λͺ©μ μΌλ‘λ§ μ¬μ©λλ€.
λ€λ₯Έ λͺ¨λμμ APIλ€μ λ
ΈμΆνκ³ μΆμ§ μμλ μ¬μ©νλ€. λν μλΈμ νΈλ₯Ό AnyPublisher
λ‘ λννλ©΄ μΈλΆμμ send
λ©μλλ₯Ό νΈμΆνμ¬ λΆνμν μμ λ°©μΆμ λ°©μ§ν μλ μλ€.
# Published
@Published
νλ‘νΌν° λνΌλ‘ μ μΈλ μμ±μ ν΄λΉ νμ
μ λ§κ² νΌλΈλ¦¬μ
λ₯Ό μμ±νκ² λλ€. μμ±κ°μ΄ λ³κ²½λλ©΄ willSet
λΈλ‘μμ λ°©μΆμ΄ μ΄λ£¨μ΄μ§λ€. willSet
λΈλ‘μ΄κΈ° λλ¬Έμ νλ‘νΌν°μ μ€μ λ³κ²½λ κ°μ΄ λ°μλκΈ° μ μ ꡬλ
μκ° κ°μ λ°κ² λλ€.
class Weather {
@Published var temperature: Double
init(temperature: Double) {
self.temperature = temperature
}
}
let weather = Weather(temperature: 20)
let cancellable = weather.$temperature
.sink() {
print ("Temperature now: \($0)")
}
print(weather.temperature)
weather.temperature = 25
print(weather.temperature)
temperature
μμ±κ°μ΄ 25λ‘ λ³κ²½λμμ λ ꡬλ
μ ν΄λ‘μ κ° λ¨Όμ μ€νλμ΄ received
κ° μ€νλκ³ μ΄ν printκ° μ€νλλ€.
# Reference
- Zeddios - AsyncSequence (opens new window)
- Apple Document - Publisher (opens new window)
- try code by Marin Todorov - subscribe(on:) vs receive(on:) (opens new window)
- Velog - subscribe(on:) VS. receive(on:) (opens new window)
- Transforming Operators in Swift Combine Framework: Map vs FlatMap vs SwitchToLatest (opens new window)
- Medium - Swift Combine: TypeEraser, things you might have never known of (opens new window)
β Combine - κ°μ PS λͺ¨μμ§ β