Notice
Recent Posts
Recent Comments
Link
ยซ   2024/07   ยป
์ผ ์›” ํ™” ์ˆ˜ ๋ชฉ ๊ธˆ ํ† 
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
Archives
Today
Total
๊ด€๋ฆฌ ๋ฉ”๋‰ด

lgvv98

[Combine] Networking(feat. RestAPI) ๋ณธ๋ฌธ

apple/๐Ÿš SwiftUI & Combine

[Combine] Networking(feat. RestAPI)

๐Ÿฅ• ์บ๋Ÿฟ๋งจ 2022. 6. 11. 16:23

Networking(feat. RestAPI)

Combine์„ ํ™œ์šฉํ•ด์„œ ์‰ฝ๊ฒŒ ์„œ๋ฒ„์™€ ํ†ต์‹ ์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๊ธ€์˜ ์ œ์ผ ์•„๋ž˜ ๋ถ€๋ถ„์—์„œ๋Š” MVVM์— ๋Œ€ํ•œ ๊ณ ์ฐฐ๋„ ๋“ค์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

 

๋‚œ์ด๋„๋Š” ์ด 3๊ฐ€์ง€๋กœ ๋‚˜๋‰˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ํŒŒ์ผ์„ ํ™•์ธํ•ด์ฃผ์„ธ์š”!

์ด ํฌ์ŠคํŒ…์—์„œ๋Š” Refactoring(์ƒ)๊ณผ Advanced Model์— ๋Œ€ํ•ด์„œ๋งŒ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

์ž์„ธํ•œ ์ฝ”๋“œ๋Š” ์•„๋ž˜ ์ฒจ๋ถ€ ํŒŒ์ผ์„ ํ™•์ธํ•ด์ฃผ์„ธ์š”!!!

CombineNetworking.zip
0.07MB

๐Ÿฅ• ๋ชฉ์ฐจ ๐Ÿฅ•

1. Refactoring (์ƒ)

2. MVVM์— ๋Œ€ํ•œ ๊ณ ์ฐฐ(Combine ํ•œ๋‹ฌ์ฐจ,, 2022.06.11)

 

๐ŸŸ  ๋ชจ๋ธ

//
//  UserModel.swift
//  CombineNetworking
//
//  Created by Hamlit Jason on 2022/06/10.
//

//   let user = try? newJSONDecoder().decode(User.self, from: jsonData)
import Foundation

// MARK: - UserElement
struct UserElement: Codable, Hashable {
    let id: Int
    let name, username, email: String
    let address: Address
    let phone, website: String
    let company: Company
    
    static func == (lhs: UserElement, rhs: UserElement) -> Bool {
        return lhs.name == rhs.name && lhs.username == rhs.username
    }
}

// MARK: - Address
struct Address: Codable, Hashable {
    let street, suite, city, zipcode: String
    let geo: Geo
    
    static func == (lhs: Address, rhs: Address) -> Bool {
        return lhs.street == rhs.street
    }
}

// MARK: - Geo
struct Geo: Codable, Hashable {
    let lat, lng: String
    
}

// MARK: - Company
struct Company: Codable, Hashable {
    let name, catchPhrase, bs: String
    
}

typealias User = [UserElement]

๋ชจ๋ธ์—์„œ ์ฃผ๋ชฉํ•ด์•ผ ํ•˜๋Š” ๋ถ€๋ถ„์€ Hashable์ž…๋‹ˆ๋‹ค. ์ด ๋ถ€๋ถ„์„ ์ฑ„ํƒํ•ด์•ผ์ง€ ๋น„๊ต๊ฐ€ ๊ฐ€๋Šฅํ•˜๊ธฐ ๋•Œ๋ฌธ์— Hashable์„ ์ƒ์†๋ฐ›๊ณ  ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค. == ๋ฉ”์†Œ๋“œ ์•ˆ์—์„œ ๊ตฌํ˜„ํ•˜๋Š”๋ฐ, ๋‹จ์ˆœํ•œ ์˜ˆ์ œ๋ผ์„œ ์ •๋ง ํ•˜๋‚˜์˜ ์†์„ฑ๋งŒ ๋งž์œผ๋ฉด ๊ฐ™๋‹ค๊ณ  ํŒ๋‹จํ•˜๊ฒŒ ์„ค์ •ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

 

 

๐ŸŸ  ๋ทฐ

//
//  RefactoringView.swift
//  CombineNetworking
//
//  Created by Hamlit Jason on 2022/06/11.
//

import SwiftUI

struct RefactoringView: View {
    @EnvironmentObject var engine: RefactoringEngine
    
    var body: some View {
        NavigationView {
            VStack {
                HStack {
                    Image(systemName: "magnifyingglass")
                        .foregroundColor(Color.green)
                    
                    TextField("", text: $engine.typing, onCommit: {
                        engine.input = engine.typing
                    })
                        
                }
                .padding(.horizontal, 20)
                .frame(height: 40)
                .background(
                    RoundedRectangle(cornerRadius: 20)
                        .stroke(Color.green, lineWidth: 1)
                )
                .padding([.horizontal, .top], 16)
                .padding(.bottom, 10)
                
                Spacer()
                
                List {
                    ForEach(engine.users, id: \.self) { user in
                        Text("idํ•„ํ„ฐ๋ง ์—์‹œ!: \(user.id)")
                    }
                    .listStyle(.plain)
                }
                .navigationBarTitleDisplayMode(.inline)
                .toolbar {
                    ToolbarItem(placement: .principal) {
                        Text("๋ฆฌํŒฉํ† ๋ง์—”์ง„ ์žฅ์ฐฉ!")
                    }
                }
            }
            
            
        }
    }
}

view ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค. ํŠน๋ณ„ํ•œ ์ ์€ ์—†์Šต๋‹ˆ๋‹ค. 

๋‹ค๋งŒ ๋„ค๋น„๊ฒŒ์ด์…˜๋ทฐ๋ฅผ ์„ค์ •ํ•  ๋•Œ navigationBarTitleDisplayMode๋ฅผ ์ง€์ •ํ•ด์ฃผ์ง€ ์•Š์œผ๋ฉด, ์šฐ๋ฆฌ๊ฐ€ ์›ํ•œ ๊ฐ’๋ณด๋‹ค ํฐ ์˜์—ญ์œผ๋กœ ๋„ค๋น„๊ฒŒ์ด์…˜ ๋ทฐ๋ฅผ ์ฐจ์ง€ํ•ด์„œ ์›ํ•˜๋Š” ๊ฒฐ๊ณผ๊ฐ€ ์•„๋‹ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

๐ŸŸ  ์—”์ง„

//
//  RefactoringEngine.swift
//  CombineNetworking
//
//  Created by Hamlit Jason on 2022/06/11.
//
// ๊ณ ๊ธ‰ json: https://jsonplaceholder.typicode.com/users
import Combine
import SwiftUI

class RefactoringEngine: ObservableObject {
    static let urlString = "https://jsonplaceholder.typicode.com/users"
    
    @Published var users: User = []
    var cancleables = Set<AnyCancellable>()
    
    @Published var typing: String = ""
    @Published var input: String = "" {
        didSet {
            send(filterId: Int(input))
        }
    }
    
    init() {
        send()
    }
    
    func send(filterId: Int? = nil) {
        guard let url = URL(string: RefactoringEngine.urlString) else { return }
        
        URLSession.shared.dataTaskPublisher(for: url)
            .subscribe(on: DispatchQueue.global(qos: .background))
            .receive(on: DispatchQueue.main)
            .tryMap(handleOutput)
            .decode(type: User.self, decoder: JSONDecoder())
            .map { users in
                return users.filter { user in
                    user.id != filterId
                }
            }
            .sink { completion in
                print("โœ… completion \(completion)")
            } receiveValue: { [weak self] responseElements in
                print("โœ… responseElements \(responseElements)")
                self?.users = responseElements
            }
            .store(in: &cancleables)
        
    }
    
    func handleOutput(output: URLSession.DataTaskPublisher.Output) throws -> Data {
        print("โœ… \(#function)")
        guard
            let response = output.response as? HTTPURLResponse,
            (200..<300) ~= response.statusCode else {
                print("โœ… \(#function) guard์— ๊ฑธ๋ ธ์Šต๋‹ˆ๋‹ค.")
                throw URLError(.badServerResponse)
            }
        
        print("โœ… \(#function) guard๋ฅผ ํƒˆ์ถœํ–ˆ์Šต๋‹ˆ๋‹ค.")
        return output.data
    }
}

์—ฌ๊ธฐ๋„์„œ๋„ ๊ทธ๋ฆฌ ์–ด๋ ต์ง€ ์•Š๊ฒŒ ์ฒ˜๋ฆฌํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ํ•ด๋‹น ์ฝ”๋“œ๋Š” ํ…์ŠคํŠธ์—์„œ ์›ํ•˜๋Š” ์กฐ๊ฑด๊ฐ’์„ ์ž…๋ ฅํ•˜๋ฉด ํ•„ํ„ฐ๋ง์ด ๊ฐ€๋Šฅํ•˜๊ฒŒ๋” ์ง€์›ํ•ด์ค๋‹ˆ๋‹ค.

๊ธฐ์กด์—๋Š” ํด๋กœ์ €๋ฅผ ํ†ตํ•ด์„œ ์ž‘์„ฑํ•ด์„œ ์œ„์˜ ์ฝ”๋“œ๋ณด๋‹ค ์กฐ๊ธˆ ๋” ๊ธด ์ฝ”๋“œ๊ฐ€ ๋ณต์žกํ•˜๊ฒŒ ๋‹ค๊ฐ€์™”์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์œ„์˜ ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด ์ •๋ง ๊น”๋”ํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

 

 

2. MVVM์— ๋Œ€ํ•œ ๊ณ ์ฐฐ(Combine ํ•œ๋‹ฌ์ฐจ,, 2022.06.11)

 

SwiftUI + Combine์„ ๊ณต๋ถ€ํ•  ๋•Œ UIKit + RxSwift๋ฅผ ์‚ฌ์šฉํ•˜๋‹ˆ๊นŒ UI์˜ ๊ตฌ์„ฑ ๋ฐฉ์‹์ด๋˜๊ฐ€, delegate์™€ ๊ฐ™์€ ๋ถ€๋ถ„์ด ์–ด๋ ค์› ๋‹ค. ์ดํ•ด๊ฐ€ ๊นŠ์–ด์งˆ์ˆ˜๋ก ์–ด๋ ค์šด ์ ์ด ๋” ์ƒ๊ฒผ๋Š”๋ฐ, 'MVVM์ด ์ž˜ ์–ด์šธ๋ฆฌ๋Š”๊ฐ€?' ์ด๋‹ค.

 

ํŒŒ์ผ์„ ์—ด์–ด๋ณด๋ฉด ์•Œ๊ฒ ์ง€๋งŒ, ์˜ˆ๋ฅผ๋“ค์–ด EasyView๊ฐ€ ์žˆ๋‹ค๊ณ  ํ•˜๋ฉด EasyViewModel๋กœ ๋„ค์ด๋ฐํ•˜๋ฉด ์ข‹์„ ํŒŒ์ผ์„ EasyEngine์œผ๋กœ ๋„ค์ด๋ฐ ํ–ˆ๋‹ค. ์ด๊ฒŒ ๋” ์ง๊ด€์ ์ด๊ณ , ์ž˜ ์–ด์šธ๋ฆฐ๋‹ฌ๊นŒ..?

 

๋‚ด๊ฐ€ ์ง„ํ–‰ํ•˜๋Š” ํ”„๋กœ์ ํŠธ์˜ app๋‹จ์„ UIKit์—์„œ SwiftUI๋กœ ์ „ํ™˜ํ•˜์˜€๋Š”๋ฐ, viewModel์ด๋ผ๋Š” ๋„ค์ด๋ฐ๋ณด๋‹ค ํ•ด๋‹น ๊ธฐ๋Šฅ์— ๋” ์•Œ๋งž๋Š” ๋„ค์ด๋ฐ์„ ์ฑ„ํƒํ•ด์„œ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค.

 

 

 

'apple > ๐Ÿš SwiftUI & Combine' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

[SwiftUI] EqutableView (feat. POD)  (0) 2023.08.08
[iOS] NavigationSplitView  (0) 2023.08.05
[Combine] Let's study Combine! (feat. SwiftUI)  (0) 2022.06.03
[SwiftUI] @StateObject  (0) 2022.06.02
[SwiftUI] State and Data Flow  (0) 2022.06.02
Comments