코인리스트를 받아올 때 네트워크 작업을 하는 CoinDataService.swift 를 재사용 가능하게 작성합니다.
앱이 커지게 되면서 네트워킹 코드를 반복해서 쓰게 될 일을 줄여줍니다.
업데이트 전의 CoinDataService.swift
import Foundation
import Combine
class CoinDataService {
@Published var allCoins: [CoinModel] = []
var coinSubscription: AnyCancellable?
init() {
getCoin()
}
private func getCoin() {
guard let url = URL(string: "https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=250&page=1&sparkline=true&price_change_percentage=24h") else { return }
coinSubscription = URLSession.shared.dataTaskPublisher(for: url)
.subscribe(on: DispatchQueue.global(qos: .default))
.tryMap { (output) -> Data in
guard let response = output.response as? HTTPURLResponse,
response.statusCode == 200 else {
throw URLError(.badServerResponse)
}
return output.data
}
.receive(on: DispatchQueue.main)
.decode(type: [CoinModel].self, decoder: JSONDecoder())
.sink { (completion) in
switch completion {
case .finished:
break
case .failure(let error):
print(error.localizedDescription)
}
} receiveValue: { [weak self] (returnCoins) in
self?.allCoins = returnCoins
self?.coinSubscription?.cancel()
}
}
}
업데이트 후의 CoinDataService.swift
import Foundation
import Combine
class CoinDataService {
@Published var allCoins: [CoinModel] = []
var coinSubscription: AnyCancellable?
init() {
getCoin()
}
private func getCoin() {
guard let url = URL(string: "https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=250&page=1&sparkline=true&price_change_percentage=24h") else { return }
coinSubscription = NetWorkingManager.download(url: url)
.decode(type: [CoinModel].self, decoder: JSONDecoder())
.sink(receiveCompletion: NetWorkingManager.handleCompletion, receiveValue: { [weak self] (returnCoins) in
self?.allCoins = returnCoins
self?.coinSubscription?.cancel()
})
}
}
새롭게 추가된 NetworkingManager.swift
import Foundation
import Combine
class NetWorkingManager {
enum NetworkingError: LocalizedError {
case badURLResponse(url: URL)
case unknown
var errorDescription: String? {
switch self {
case .badURLResponse(url: let url): return "[🔥] Bad response form URL: \(url)"
// \(url )로 어떤 url에서 에러가 발생했는지 알 수 있습니다.
case .unknown: return "[⚠️] Unknown error occured"
}
}
}
static func download(url: URL) -> AnyPublisher<Data, Error> {
return URLSession.shared.dataTaskPublisher(for: url)
.subscribe(on: DispatchQueue.global(qos: .default))
.tryMap({ try handleURLResponse(output: $0, url: url) })
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
}
static func handleURLResponse(output: URLSession.DataTaskPublisher.Output, url: URL) throws -> Data {
guard let response = output.response as? HTTPURLResponse,
response.statusCode == 200 else {
throw NetworkingError.badURLResponse(url: url)
}
return output.data
}
static func handleCompletion(completion: Subscribers.Completion<Error>) {
switch completion {
case .finished:
break
case .failure(let error):
print(error.localizedDescription)
}
}
}
NetworkingManager 문서를 보면 dataTaskPublisher를 통해 데이터를 다운로드 하는 코드, 에러 핸들링 코드가 함수로 정리되어 있습니다.
덕분에 앞으로 네트워킹 코드를 작성할 때 수고를 줄일 수 있겠네요!
'Project > SwiftUI 블록와이드' 카테고리의 다른 글
[SwiftUI Project] 검색화면 전환 개선하기 GeometryReader (0) | 2022.09.15 |
---|---|
[SwiftUI Project] 검색화면으로 전환하기 NavigationLink (0) | 2022.09.14 |
[SwiftUI Project] 코인 데이터 받아오기 with Combine (0) | 2022.09.13 |
[SwiftUI Project] HomeViewModel과 HomeView 연결, UI 작업 (0) | 2022.09.13 |
[SwiftUI Project] CoinRowView와 Double Formatter (0) | 2022.09.12 |
댓글