728x90
반응형
Publisher+Utils.swift
import Foundation
import Combine
extension Publisher {
static func empty() -> AnyPublisher<Output, Failure> {
return Empty().eraseToAnyPublisher()
}
static func just(_ output: Output) -> AnyPublisher<Output, Failure> {
return Just(output)
.catch { _ in AnyPublisher<Output, Failure>.empty() }
.eraseToAnyPublisher()
}
static func fail(_ error: Failure) -> AnyPublisher<Output, Failure> {
return Fail(error: error).eraseToAnyPublisher()
}
}
NetworkService.swift
import Foundation
import Combine
///// Defines the Network service errors.
enum NetworkError: Error {
case invalidRequest
case invalidResponse
case responseError(statusCode: Int)
case jsonDecodingError(error: Error)
}
final class NetworkService {
let session: URLSession
init(configuration: URLSessionConfiguration) {
session = URLSession(configuration: configuration)
}
func load<T>(_ resource: Resource<T>) -> AnyPublisher<T, Error> {
guard let request = resource.urlRequest else {
return .fail(NetworkError.invalidRequest)
}
return session
.dataTaskPublisher(for: request)
.tryMap { result -> Data in
guard let response = result.response as? HTTPURLResponse,
(200..<300).contains(response.statusCode)
else {
let response = result.response as? HTTPURLResponse
let statusCode = response?.statusCode ?? -1
throw NetworkError.responseError(statusCode: statusCode)
}
return result.data
}
.decode(type: T.self, decoder: JSONDecoder())
.eraseToAnyPublisher()
}
}
Resource.swift
import Foundation
struct Resource<T: Decodable> {
var base: String
var path: String
var params: [String: String]
var header: [String: String]
var urlRequest: URLRequest? {
var urlComponents = URLComponents(string: base + path)!
let queryItems = params.map { (key: String, value: String) in
URLQueryItem(name: key, value: value)
}
urlComponents.queryItems = queryItems
var request = URLRequest(url: urlComponents.url!)
header.forEach { (key: String, value: String) in
request.addValue(value, forHTTPHeaderField: key)
}
return request
}
init(base: String, path: String, params: [String: String] = [:], header: [String: String] = [:]) {
self.base = base
self.path = path
self.params = params
self.header = header
}
}
송수신 Json 형식
Resouce 에서 설정한 URL 의 반환 Json 데이터 목록을 정의 해야합니다
API에 따라 다르기 때문애 확인 후 다르게 작성해줘야 합니다
import Foundation
struct SearchResult: Hashable, Identifiable, Decodable {
var id: Int64
var login: String
var avatarUrl: URL
var htmlUrl: String
enum CodingKeys: String, CodingKey {
case id
case login
case avatarUrl = "avatar_url"
case htmlUrl = "html_url"
}
}
네트워크 선언
let network = NetworkService(configuration: .default)
API 호출 리소스 설정
- base : 기본 주소 작성
- path : base 주소의 하위 주소 작성
- params : 파라미터 작성
- header : Json 데이터 송수신을 위한 Content-Type 등 작성
let resource = Resource<SearchUserResponse>(
base: "https://api.github.com/",
path: "search/users",
params: ["q": keyword],
header: ["Content-Type": "application/json"])
리소스 설정 후 네트워크 로드
❗️ (load) 수신된 데이터에 따라 다르게 작성 해야합니다.
network.load(resource)
.map { value in value.items }
.replaceError(with: [])
.receive(on: RunLoop.main)
.assign(to: \.users, on: self)
.store(in: &subscription)
728x90
반응형
'Swift' 카테고리의 다른 글
[ Swift ] Network - 개념 (0) | 2023.03.15 |
---|---|
[ Swift ] Combine - 실습 (0) | 2023.03.13 |
[ Swift ] Combine - Operator (0) | 2023.03.08 |
[ Swift ] Combine - Subscriber (0) | 2023.03.06 |
[ Swift ] Combine - Publisher (0) | 2023.03.03 |