// configuration -> urlsession -> urlsessionTask
let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration)
let url = URL(string: "https://api.github.com/users/woozoobro")!
let task = session.dataTask(with: url) { data, response, error in
guard let httpResponse = response as? HTTPURLResponse, (200..<300).contains(httpResponse.statusCode) else {
print(" --> response \(response)")
return
}
guard let data = data else { return }
let result = String(data: data, encoding: .utf8)
print(result)
}
task.resume()
configuration을 만들고 session을 만든다
url을 만들어주고
session의 dataTask를 만들어준 url로 수행하게되면
completionHandler에 dat, response, error 를 받아서
뭔가를 실행해줄 수 있다!
struct GithubProfile: Codable {
let login: String
let avatarURL: String
let htmlURL: String
let followers: Int
let following: Int
enum CodingKeys: String, CodingKey {
case login
case avatarURL = "avatar_url"
case htmlURL = "html_url"
case followers
case following
}
}
// App Model <-> JSON
// 모델을 JSON으로 바꾸는게 인코딩, 그 반대는 디코딩
let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration)
let url = URL(string: "https://api.github.com/users/woozoobro")!
let task = session.dataTask(with: url) { data, response, error in
guard let httpResponse = response as? HTTPURLResponse, (200..<300).contains(httpResponse.statusCode) else {
print("--->response: \(response!)")
return
}
guard let safeData = data else { return }
// data -> GithubProfile
do {
let decoder = JSONDecoder()
let profile = try decoder.decode(GithubProfile.self, from: safeData)
print("profile: \(profile)")
} catch let error as NSError {
print("error: \(error)")
}
}
task.resume()
enum NetworkError: Error {
case invalidRequest
case transportError(Error)
case responseError(statusCode: Int)
case noData
case decodingError(Error)
}
struct GithubProfile: Codable {
let login: String
let avatarURL: String
let htmlURL: String
let followers: Int
let following: Int
enum CodingKeys: String, CodingKey {
case login
case avatarURL = "avatar_url"
case htmlURL = "html_url"
case followers
case following
}
}
final class NetworkService {
let session: URLSession
init(configuration: URLSessionConfiguration) {
session = URLSession(configuration: configuration)
}
func fetchProfile(userName: String, completion: @escaping (Result<GithubProfile, Error>) -> Void) {
let url = URL(string: "https://api.github.com/users/\(userName)")!
let task = session.dataTask(with: url) { data, response, error in
if let error = error {
completion(.failure(NetworkError.transportError(error)))
return
}
if let httpResponse = response as? HTTPURLResponse, !(200..<300).contains(httpResponse.statusCode) {
completion(.failure(NetworkError.responseError(statusCode: httpResponse.statusCode)))
return
}
guard let safeData = data else {
completion(.failure(NetworkError.noData))
return
}
// data -> GithubProfile
do {
let decoder = JSONDecoder()
let profile = try decoder.decode(GithubProfile.self, from: safeData)
completion(.success(profile))
} catch let error as NSError {
completion(.failure(NetworkError.decodingError(error)))
}
}
task.resume()
}
}
// network 담당 NetworkService
// NetworkService 이용한 네트워크 작업
let networkService = NetworkService(configuration: .default)
networkService.fetchProfile(userName: "woozoobro") { result in
switch result {
case .success(let profile):
print("Profile: \(profile)")
case .failure(let error):
print("Error: \(error)")
}
}
1-1 열거형이란?
같은 주제로 연관된 데이터들을 멤버로 구성해서 나타내는 자료형
enum Position {
case top
case mid
case jug
case adc
case sup
}
1-2. 원시값이 있는 열거형
enum Position: Int {
case top
case mid
case jug
case adc
case sup
}
요렇게 써주면 가장 먼저 선언된 case부터 0부터 1씩 증가된 값
이 들어간다
enum Position: String {
case top
case mid
case jug = "slave"
case adc
case sup
}
rawValue를 지정하지 않으면 case 이름과 동일한 Raw Value가 자동으로 만들어짐!!
지금 같은 경우엔 jug에는 slave 말고는 다 case의 이름으로 rawValue가 들어가게 된다
var user1: Position = .top
var user2: Position = .mid
var user3: Position = .jug
user1.rawValue
user2.rawValue
user3.rawValue
enum AppleProduct: String {
case iPad = "5, 128GB"
case iPhone = "6, 64GB"
case macbook = "Pro, 256GB"
}
rawValue를 사용할 경우,
모든 case가 동일한 형식(위에선 String)으로 Raw Value를 가져야 하고,
case 별 값은 미리 지정된 한 가지 값만 가질 수 있다.
이를 보완해서 사용하는 것이 바로!!
associated Value 즉, 연관값!!
enum TypeName {
case caseName(Type)
case caseName(Type, Type, ...)
}
이렇게 case 옆에 튜플 형태로 원하는 type을 명시하면 됨
아까 rawValue로 한계가 있던 예시를 바꿔보자
enum AppleProduct {
case iPad(model: String)
case iPhone(model: String, storage: Int)
case macbook(model: String, storage: Int, size: Int)
}
import Foundation
import Combine
enum NetworkError: Error {
case invalidRequest
case transportError(Error)
case responseError(statusCode: Int)
case noData
case decodingError(Error)
}
struct GithubProfile: Codable {
let login: String
let avatarURL: String
let htmlURL: String
let followers: Int
let following: Int
enum CodingKeys: String, CodingKey {
case login
case avatarURL = "avatar_url"
case htmlURL = "html_url"
case followers
case following
}
}
final class NetworkService {
let session: URLSession
init(configuration: URLSessionConfiguration) {
session = URLSession(configuration: configuration)
}
func fetchProfile(userName: String) -> AnyPublisher<GithubProfile, Error> {
let url = URL(string: "https://api.github.com/users/\(userName)")!
let publisher = session
.dataTaskPublisher(for: url)
// 서버에서 받은 response 확인
.tryMap { result -> Data in
guard let httpResponse = result.response as? HTTPURLResponse, (200..<300).contains(httpResponse.statusCode) else {
let response = result.response as? HTTPURLResponse
let statusCode = response?.statusCode ?? -1
throw NetworkError.responseError(statusCode: statusCode)
}
return result.data
} // 받은 Data 디코딩 잘하기
.decode(type: GithubProfile.self, decoder: JSONDecoder())
.eraseToAnyPublisher()
return publisher
}
}
let networkService = NetworkService(configuration: .default)
let subscription = networkService.fetchProfile(userName: "woozoobro")
.receive(on: RunLoop.main)
.print()
.sink { completion in
print("completion: \(completion)")
} receiveValue: { profile in
print("profile: \(profile)")
}