[Combine] append(_:) 의 3가지 사용방법
분류
Combine Framework | Publishers |
Protocol | Publisher |
Operator | Applying Sequence Operations to Elements |
Append(_:)
[ 첫번째 ]: 가변인자로 여러개의 요소를 인자로 받아 append() 처리 후 발행 하는 경우
func append<T>(_ elements: Self.Output...) -> Publishers.Concatenate<Self,Publishers.Sequence<[Self.Ouput], Self.Failure>>
설명
[코드]
func append(numbers:Int...){
cancellable = numbers.publisher
.append(100, 200, 300)
.sink { print("\($0)") }
}
//실행
append(numbers: 1,2,3,4,5)
// 결과: "1 2 3 4 5 100 200 300"
가변 인자로 여러개의 요소를 발행받아 append() 할 수 있다.
가변인자로 타입을 지정하는 방법은 type... 으로 ...을 붙여주면 된다.
예를 들어 Int형 가변인자를 받겠다고 한다면 Int... 으로 타입을 지정해주면 된다.
가변인자는 Swift에서 내부적으로 시퀀스 타입으로 처리된다.
Apple Combine 공식문서에서 해당 경우의 Append() 코드도 살펴보자
let dataElements = (0...10)
cancellable = dataElements.publisher
.append(0, 1, 255)
.sink { print("\($0)", terminator: " ") }
// Prints: "0 1 2 3 4 5 6 7 8 9 10 0 1 255"
여기서 dataElements 는 ClosedRange 타입이다.
ClosedRange 특정 범위의 값을 표현하기 위한 타입이며 (시작 값...끝 값)의 범위를 포함한다.
Sequence Protocol을 준수하며, Sequence 의 기능을 제공할 수 있다.
[Apple Combine append(_:) Documentation]
[ 두번째 ]: Sequence protocol 을 준수하는 값을 인자로 받아 append() 처리 후 발행 하는 경우
func append<S>(_ elements: S) -> Publishers.Concatenate<Self,Publishers.Sequence<S, Self.Failure>> where S : Sequence, Self.Output == S.Element
설명
[코드]
Apple Combine 공식문서의 코드로 확인해보자.
let groundTransport = ["car", "bus", "truck", "subway", "bicycle"]
let airTransport = ["parasail", "jet", "helicopter", "rocket"]
cancellable = groundTransport.publisher
.append(airTransport)
.sink { print("\($0)", terminator: " ") }
// Prints: "car bus truck subway bicycle parasail jet helicopter rocket"
변수인 groundTransport 와 airTransport 는 배열(array)로 Sequence Protocol을 준수한다.
코드를 보면 groundTransport를 발행하였고, Sequence Protocol를 준수하는 변수인 airTransport 를 append 하고 발행하고 있다.
해당 방식으로 사용하기 위한 규칙은 아래와 같다.
Publisher(Self)의 출력 타입(Output)과 동일한 요소 타입을 가진 시퀀스를 인자(_ element)로 받을 수 있다.
[참고]
[Apple Combine append(_:) Documentation]
[ 세번째 ]: publisher protocol 을 준수하는 값을 인자로 받아 append() 처리 후 발행 하는 경우
func append<P>(_ Publisher: P) -> Publishers.Concatenate<Self,P> where P : Publisher, Self.Failure == P.Failure, Self.Output == P.Output
설명
[코드]
Apple Combine 공식문서의 코드로 확인해보자.
let numbers = (0...10)
let otherNumbers = (25...35)
cancellable = numbers.publisher //요소 발행 생성
.append(otherNumbers.publisher)
.sink { print("\($0)", terminator: " ") }
// Prints: "0 1 2 3 4 5 6 7 8 9 10 25 26 27 28 29 30 31 32 33 34 35 "
sequence protocol 을 준수하는 closedRange 타입 의 numbers 와 otherNumbers 라는 변수가 있다.
변수로 할당된 요소들을 발행하는 Publisher 를 생성하였고, 발행된 타입을 따르는 Publisher(otherNumbers.publisher) 를 생성하여 append(otherNumbers.publisher) 해주고 있다.
해당 방식으로 사용하기 위한 규칙은 아래와 같다.
sequence protocol을 준수하는 값을 Publisher 로 생성 후 append() method를 사용할 수 있다.
Publisher로 생성하여 append() 를 작동하므로, 추가할 값 또한 sequence protocol을 준수하면서 Publisher로 발행된 것이어야 가능하다.
[참고]
[Apple Combine append(_:) Documentation]
[ 구현 예시 영상 및 코드 ]
[영상]
combine append() 를 활용하여 상품 추가
[구현 코드]
ApplyingSequenceOperationsToElements Class 구현
import Foundation
import Combine
final class ApplyingSequenceOperationsToElements{
private var cancellable: AnyCancellable?
func append<T>(arr: [T], ele: T, completion: @escaping (_ results: [T]) -> Void) {
let arrPublisher = Publishers.Sequence<[T], Never>(sequence: arr)
cancellable = arrPublisher
.append([ele])
.collect()
.sink { results in
completion(results)
}
}
}
ProductViewModel 구현
import Foundation
final class ProductViewModel:ObservableObject{
static let shared = ProductViewModel()
let applySequenceOperator = ApplyingSequenceOperationsToElements()
@Published var products:[Product] = []
func appendProduct(){
var newProduct:Product
if let lastProduct = products.last{
newProduct = Product(id: lastProduct.id + 1)
}else{
newProduct = Product(id: 0)
}
applySequenceOperator.append(arr: products, ele: newProduct) { [weak self] results in
self?.products = results
}
}
}
ProductView 구현
import SwiftUI
struct ProductView: View {
@StateObject var vm = ProductViewModel.shared
var body: some View {
VStack{
HStack{
HStack{}.frame(width:24,height:24)
Spacer()
Text("Product")
.font(.system(size: 18))
Spacer()
HStack(spacing:8){
Button(action:{
vm.appendProduct()
}){
Image(systemName: "plus")
.frame(width:24, height:24)
}
Button(action:{}){
Image(systemName: "line.horizontal.3")
.frame(width:24, height:24)
}
}
}
.padding(.horizontal, 16)
.frame(height:50)
VStack{
ForEach(vm.products, id: \.id){ product in
HStack{
Text("ID: \(product.id)")
}
.frame(height: 48)
}
}
}
.frame(maxHeight: .infinity, alignment: .top)
}
}