Development record of developer who study hard everyday.

레이블이 swiftui인 게시물을 표시합니다. 모든 게시물 표시
레이블이 swiftui인 게시물을 표시합니다. 모든 게시물 표시
, ,

How to call the api in SwiftUI and Combine ios project.

 How to call the api in SwiftUI and Combine ios project.


1. ContentView

import SwiftUI


struct ContentView: View {
    //We need @ObservedObject to observe ViewModel class

    @ObservedObject

    var viewModel = ViewModel()

    

    var body: some View {

        VStack {
            //List is rerendered whenever ViewModel's characters property changed

            List(viewModel.characters) { character in

                CharacterRow(character: character)

            }

        }

        .padding()

        .onAppear {
            //this closure is executed before View appears

            viewModel.fetchCharacters()

        }

    }

}


2. CharacterRow

import SwiftUI


struct CharacterRow: View {

    

    var character: Character

    

    var body: some View {

        HStack {

            AsyncImage(url: character.image) { image in

                image

                    .resizable()

                    .clipShape(RoundedRectangle(cornerRadius: /*@START_MENU_TOKEN@*/25.0/*@END_MENU_TOKEN@*/, style: .continuous))

                    .frame(width: 100, height: 100)

            } placeholder: {

                ProgressView()

            }

            Text(character.name)

        }

    }

}



3. Character struct and CharacterResponse struct

import Foundation


struct CharacterResponse: Codable {

    let results: [Character]

}


struct Character: Codable, Identifiable {

    let id: Int

    let name: String

    let image: URL


}

I receive api response using these structs


4. ViewModel

import Foundation

import Combine


class ViewModel : ObservableObject {
    //@Published make the view re-render whenever characters changes

    @Published var characters: [Character] = []

    var subscriptions = Set<AnyCancellable>()

    

    func fetchCharacters() {

        guard let url = URL(string: "https://rickandmortyapi.com/api/character") else {return}

        

        URLSession.shared.dataTaskPublisher(for: url)

            .map {

                $0.data

            }.decode(type: CharacterResponse.self, decoder: JSONDecoder())

            .receive(on: DispatchQueue.main)

            .sink(receiveCompletion: { completion in

                switch completion {

                case .failure(let error):

                    print(error.localizedDescription)

                case .finished:

                    print("Data Fetched")

                }

                

            }, receiveValue: { decodedData in

                print("receiveValue, count: \(decodedData.results.count)")

                self.characters = decodedData.results

            })

            .store(in: &subscriptions)

    }

}

I call the api and store character information to characters property.

Because characters is type of @Published, ListView is rerendered.









Share:
Read More
, ,

How to implement MVVM pattern using SwiftUI and Combine

How to implement MVVM pattern using SwiftUI and Combine



1. ContentView.swift

import SwiftUI


struct ContentView: View {
    //You have to declare viewModel as @ObservedObject, so you can bind viewModel's property
    //to SwiftUI's view

    @ObservedObject private var viewModel: ViewModel

    init(viewModel: ViewModel) {

        self.viewModel = viewModel

    }

    

    var body: some View {

        VStack {

            

            Text(String(viewModel.count))

            

            Spacer().frame(height: 10)


            Button(action: {

                viewModel.plus()

            }, label: {

                Text("Plus")

            })

            

            Spacer().frame(height: 10)

            

            Button(action: {

                viewModel.minus()

            }, label: {

                Text("Minus")

            })

            

            Spacer().frame(height: 20)

            

            Text(viewModel.inputText)

            

            Spacer().frame(height: 10)

            

            Text(viewModel.descriptionText)


            Spacer().frame(height: 10)

            
            //Using $, you can store user's inputText in viewModel's property

            TextField("텍스트를 입력하세요", text: $viewModel.inputText)

                .frame(alignment: .center)

                .multilineTextAlignment(.center)

                .keyboardType(.default)

        }

        .padding()

    }

}


#Preview {

 

    ContentView(viewModel : ViewModel())

}


2. ViewModel.swift

import Foundation

import Combine


//ViewModel have to inherit ObservableObject for MVVM pattern

class ViewModel: ObservableObject {

    

    init() {

        print("ViewModel init")

        
        //Subscribe descriptionPublisher and assign value to descriptionText property

        descriptionPublisher

            .receive(on: RunLoop.main)

            .assign(to: \.descriptionText, on: self)

            .store(in: &cancellableBag)

    }

    
    //You can publish data and subscribe data using @Published

    @Published var count: Int = 0

    @Published var inputText: String = ""

    @Published var descriptionText: String = ""
    //Subscribe inputText data and transform data using map method

    var descriptionPublisher : AnyPublisher<String, Never> {

        return $inputText

            .map { "현재 텍스트의 길이는 " + String($0.count) + "입니다." }

            .eraseToAnyPublisher()

    }

    

    private var cancellableBag = Set<AnyCancellable>()

    

    func plus() {

        print("viewModel plus called")

        count += 1

    }

    

    func minus() {

        print("viewModel minus called")

        count -= 1

    }

    

    deinit {

        cancellableBag.removeAll()

    }

}


The full code is in below url:

https://github.com/antwhale/SampleCombineCounter


Share:
Read More