["A", "B", "C", "D", "E"].publisher.sink {
print($0)
}
["A", "B", "C", "D", "E"].publisher.collect().sink {
print($0)
}
// 출력값
A
B
C
D
E
["A", "B", "C", "D", "E"]
원래 publisher를 sink 하면 하나의 value 씩 받아오지만 collect를 이용하면 한 번에 받아 올 수 있다.
collect(3)
과 같은 형태로 각 어레이에 최대 몇 개의 값이 들어갈 것인지도 지정해 줄 수 있다.
let formatter = NumberFormatter()
formatter.numberStyle = .spellOut
[1999, 12, 06].publisher.map {
formatter.string(from: NSNumber(integerLiteral: $0)) ?? ""
}.sink {
print($0)
}
// 출력값
one thousand nine hundred ninety-nine
twelve
six
map을 사용하면 publisher로 부터 받아온 값을 우리가 원하는 형태로 가공할 수 있다.
struct Point {
let x: Int
let y: Int
}
let publisher = PassthroughSubject<Point, Never>()
publisher.map(\.x, \.y).sink { x, y in
print("X is \(x) and Y is \(y)")
}
publisher.send(Point(x: 10, y: 6))
// 출력값
// X is 10 and Y is 6
keyPath를 이용하여 map 함수를 더 유용하게 사용할 수 있다.
\.이름
형태로 사용하며 sink 에서 해당 값을 파라미터로 받아와 바로 사용할 수 있다.
struct School {
let name: String
let noOfStudents: CurrentValueSubject<Int, Never>
init(name: String, noOfStudents: Int) {
self.name = name
self.noOfStudents = CurrentValueSubject(noOfStudents)
}
}
let firtSchool = School(name: "First School", noOfStudents: 100)
let school = CurrentValueSubject<School, Never>(firtSchool)
school
.flatMap {
$0.noOfStudents
}
.sink {
print($0)
}
let secondSchool = School(name: "Second School", noOfStudents: 45)
school.value = secondSchool
firtSchool.noOfStudents.value += 1
secondSchool.noOfStudents.value += 1
// 출력값
100
45
101
46
School 구조체를 만들고 단일 값을 publish 하는 CurrentValueSubject를 이용하여 각 학교의 학생 수를 표현했다.
이때 School 타입을 publish하는 CurrentValueSubject인 school을 곧바로 sink하면
// 출력값
School(name: "First School", noOfStudents: Combine.CurrentValueSubject<Swift.Int, Swift.Never>)
School(name: "Second School", noOfStudents: Combine.CurrentValueSubject<Swift.Int, Swift.Never>)
위와 같이 School 전체가 출력된다.
school.map { $0 }.sink { print($0) }
처럼 map을 사용하면
// 출력값
Combine.CurrentValueSubject<Swift.Int, Swift.Never>
Combine.CurrentValueSubject<Swift.Int, Swift.Never>
School의 noOfStudents가 타입 그대로 출력된다.
하지만, flatMap을 사용하여 flatten하게 만든다면
school.faltMap { $0 }.sink { print($0) }
// 출력값
100
45
101
46
noOfStudents가 CurrentValueSubject 즉 publisher이기 때문에 이를 sink한 값이 출력된다.
["A", "B", nil, "C"].publisher.replaceNil(with: "*").sink {
print($0)
}
// 출력값
Optional("A")
Optional("B")
Optional("*")
Optional("C")
nil이 포함된 값을 publish 하고 이를 다른 값으로 수정하여 사용하고 싶다면 replaceNil을 이용하면 된다.
이때, 주의할 점은 sink한 값들이 Optional이 된다는 점이다.
Optional로 변하는 것을 원치 않다면
["A", "B", nil, "C"].publisher.replaceNil(with: "*").map {
$0!
}.sink {
print($0)
}
map을 통해 언래핑을 해주거나
["A", "B", nil, "C"].publisher.replaceNil(with: "*").compactMap {
$0
}.sink {
print($0)
}
// 출력값
A
B
*
C
compactMap을 사용하는 방법이 있다.
let empty = Empty<Int, Never>()
empty.sink(receiveCompletion: { print($0) } , receiveValue: { print($0) })
// 출력값
finished
값 전달 없이 단순히 작업이 완료되었거나 어떠한 시점을 전달하기 위해서 Empty publisher를 사용할 수 있다.
출력값을 확인해보면 receiveCompletion 에서 출력된 finished만 출력되고 receiveValue는 아무것도 출력되지 않은 것을 확인할 수 있다.
하지만 Empty publisher에서도 특정한 값을 원한다면 replaceEmpty(with:)
을 사용할 수 있다.
let empty = Empty<Int, Never>()
empty
.replaceEmpty(with: 1)
.sink(receiveCompletion: { print($0) }, receiveValue: { print($0) })
// 출력값
1
finished
let publisher = (1...10).publisher
publisher.scan([]) { numbers, value in
numbers + [value]
}.sink {
print($0)
}
// 출력값
[1]
[1, 2]
[1, 2, 3]
[1, 2, 3, 4]
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5, 6]
[1, 2, 3, 4, 5, 6, 7]
[1, 2, 3, 4, 5, 6, 7, 8]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
scan을 활용하면 원하는 포맷으로 publish 된 값을 순서대로 가공할 수 있다.
scan() 소괄호 내부에는 초기값이 들어간다. 이번 예시에서는 빈 어레이를 넣었다.
클로저 안에는 초기값을 명시하는 이름(여기서는 numbers)와 publish된 값을 지칭할 이름(value)가 파라미터로 필요하다.
그리고 해당 파리미터들로 연산을 하여 return 하고자 하는 값을 적어준다. 여기서는 numbers 어레이에 value를 append 시켰다.
출력을 해보면 1부터 10까지 값들이 계속 한 개씩 더해져서 어레이가 출력되는 것을 확인할 수 있다.