MVVM

김정현·2024년 1월 8일
0

Project2: Pokemon App

목록 보기
2/2

포켓몬 데이터를 가져오기 위해 https://pokeapi.co/docs/v2 에서 정보를 가져와 사용하였다.

Pokemon App에서 api 통신을 통해서 데이터를 가져올떄 가독성 좋게 코드를 위해 MVVM구조로 작성하는게 좋다.

Model

우선 Model을 작성하였다. API에서 받아올 정보들의 틀을 만들어 놓는 것이다.

https://pokeapi.co/api/v2/pokemon/포켓몬도감번호/

이 링크를 FireFox를 통해서 실행하면 Json코드로 이루어진 데이터들을 확인할 수 있다. 이 데이터들을 API 통신을 통해서 가져오게 되는 것이고 Json코드에 적혀진 이름에 맞게 모델을 작성해야지만 올바른 데이터들을 가져올 수 있는 것이다.

원시 데이터를 클릭해보면

이런식으로 데이터들이 정렬되어있다. 데이터들을 가져오기 위해서 구조체, 배열, 딕셔너리 등등 알맞게 작성해야하는데, 수기로 작성해도 되지만 나는 https://json2kt.com/json-to-swift.php 사이트를 통해서 작업하였다.

스트링형태로 된 데이터들은 큰 용량을 차지하지 않기 때문에 교신하는 API의 데이터 들의 모든 데이터 모델들을 swift파일로 다운받아 모델에 저장하였다.
이렇게 해두면 원하는 데이터들만 원할때 사용할 수 있게 된다.
그러나 파일 그대로 사용하게 될경우 - 나 _ 등등 여러가지 Swift가 원하는 형태가 아닌 다른형태로 나타나게 되어 오류가 발생하기 떄문에 적절히 수정해주는 것은 필수이다.

Service

API를 사용한다면 MVVM 외에도 Service 파트를 추가해주어야한다. 이 파트에선 교신을 통해서 Json 코드를 디코더하는 역할을 하게 된다.

class PokeService {
    func getData<T: Decodable>(url: URL) -> AnyPublisher<T, Error> {
        return URLSession.shared.dataTaskPublisher(for: url)
            .map(\.data)
            .decode(type: T.self, decoder: JSONDecoder())
            .eraseToAnyPublisher()
    }
}

ViewModel

뷰모델에선 Service의 함수를 통해서 모델에 데이터를 담는 함수를 작성한다.

    @Published var PokemonWeight: PokemonData?


 func fetchPokemonWeight(id: Int) {
        let url = URL.urlWith(id: id)
        guard let url = url else { return }

        Pokemon.getData(url: url)
            .sink(receiveCompletion: { completion in
                if case .failure(let error) = completion {
                    print("Error during fetchMainPokemon: \(error)")
                }
            }, receiveValue: { [weak self] data in
                self?.PokemonWeight = data
            })
            .store(in: &cancellables)
    }

id에 원하는 숫자를 입력한 후, 이 함수를 실행하면 data에 임시적으로 원하는 데이터가 담긴다.

여기서 주의할 점은 만약 weight외에 height도 필요하다고해서 둘다 같은 함수에서 받아오려고 하면 data에서 비동기적으로 작업을 처리하기 때문에 모두 같은 데이터로 통일돼서 출력된다거나 하는 방식으로 원하는 데이터를 가져오기 쉽지않다.

그래서 원하는 데이터의 수만큼 함수를 작성하는 방식으로 해결했다.

View

View에선 ViewModel에서 작성했던 함수들을 원하는 타이밍에 호출하며, 데이터들을 화면에 뿌려준다.

//Service와 뷰모델을 인스턴스화 함
var pokeService: PokeService?
var pokemonViewModel: PokemonViewModel? 

//원하는 타이밍에 함수를 호출함
pokemonViewModel?.fetchPokemonWeight(id: pokeNumber[row!])


//호출한 후, 데이터를 구독하여 사용
  func PokemonWeight(label: UILabel) {
        pokemonViewModel?.$PokemonWeight
                    .receive(on: DispatchQueue.main)
                    .sink { data in
                        guard let PokemonWeight = data?.weight else {
                            return
                        }
                        label.text = "몸무게: " + String(Double(PokemonWeight)/10) + "kg"
                    }
                    .store(in: &cancellables)
    }
    
    

여기서 중요한점은
pokemonViewModel?.fetchPokemonWeight(id)를 실 행한후 뷰모델의 Pusblisher에 접근하여 사용하여야 바뀐 데이터를 구독할 수 있다.

0개의 댓글