참고 문헌/웹
Q) 아래 예제들 모두 실행안되는데 뭐가 문제지...
A) 알아냈음.
참고: 모든 Cancelable은 저장해야한다. 그렇지 않으면 현재 코드 범위를 벗어날 때 deallocated 및 취소된다.
뭔 말이냐면 지금 Playground는 해당 코드를 함수에 넣어서 호출하고 있다. 함수가 종료되면서 모두 deallocated및 취소되고 있는 거임.
- Debugging 하면서 찾아낸거라 거기에 적혀있으니 거기를 보도록 하자.
Combine에서는 URLRequest과 URL만 가지고 네트워크 통신을 할 수 있는 2가지 api를 제공한다.
그 중 URL을 이용한 기본적인 예제
guard let url = URL(string: "<https://mysite.com/mydata.json>") else{return}
let subscription = URLSession.shared
.dataTaskPublisher(for: url)
.sink { completion in
if case .failure(let err) = completion{
print("Retrieving data failed with error \\(err)")
}
} receiveValue: { data, response in
print("Retrieved data of size \\(data.count), response = \\(response)")
}
JsonDecoder, JsonEncoder를 이용해 response 받은 data를 디코딩할 수 있다.
guard let url = URL(string: "<https://mysite.com/mydata.json>") else{return}
let subscription = URLSession.shared
.dataTaskPublisher(for: url)
.tryMap{ data, _ in
try JSONDecoder().decode(MyType.self, from: data)
}
.sink { completion in
if case .failure(let err) = completion{
print("Retrieving data failed with error \\(err)")
}
} receiveValue: { object in
print("Retrieved object \\(object)")
}
위의 예제에서는 tryMap을 사용했지만 코드를 줄이기 위해 다음과 같이 사용할 수도 있다.
let subscription = URLSession.shared
.dataTaskPublisher(for: url)
.map(\\.data)
.decode(type: MyType.self, decoder: JSONDecoder())
.sink { completion in
if case .failure(let err) = completion{
print("Retrieving data failed with error \\(err)")
}
} receiveValue: { object in
print("Retrieved object \\(object)")
}
원래는 sink로 구독이 활성화되면 publisher가 작업을 바로 수행하게 됨.
그러면 API를 2번 실행하게 된다.
multicast를 사용하면 API를 한번만 실행할 수 있도록 만들어준다.
- share랑 multicast 차이???
- share 내부적으로는 PassThroughSubject를 autoconnect() 해주고 있음. 걍 share써라
let publisher = URLSession.shared
.dataTaskPublisher(for: url)
.map(\.data)
// multicast 내부 클로저에는 Subject가 들어가야함.
// 새로만들어도 되고 기존에 사용하던 subject가 들어가도 된다.
.multicast{PassthroughSubject<Data, URLError>()}
// subscription하는데 ConnectablePublisher(multicast 반환 타입) 때문에 바로 작업을 시작할 수 없다.
let subscription1 = publisher
.sink { completion in
if case .failure(let err) = completion{
print("Sink1 Retrieving data failed with error \(err)")
}
} receiveValue: { object in
print("Sink1 Retrieved object \(object)")
}
.store(in: &cancelBag)
let subscription2 = publisher
.sink { completion in
if case .failure(let err) = completion{
print("Sink2 Retrieving data failed with error \(err)")
}
} receiveValue: { object in
print("Sink2 Retrieved object \(object)")
}
.store(in: &cancelBag)
// publisher를 connect한다.
// 작동을 시작하고 모든 구독자에게 publish한다.
let subscription = publisher.connect()