iOS/SwiftUI

API Calls with Combine

당근쥬스 2023. 10. 20. 09:36
반응형

iOS 앱 개발에서는 API 호출과 같은 비동기 작업을 처리하는 것이 일반적인 요구 사항입니다.지금까지 개발자는 콜백이나 완료 핸들러를 사용하여 이러한 작업을 관리했습니다. 그러나 iOS 13에 Combine이 도입되면서 비동기 작업을 처리하기 좋은  방법이 생겼습니다.

 

Combine 프레임워크는 시간이 지남에 따라 값을 처리하기 위한 선언적 Swift API를 제공합니다. 그리고 Combine 프레임워크는 비동기 데이터 스트림으로 작업할 수 있으므로 API 호출 처리에 적합합니다. 이번글에서는 iOS에서 Combine을 사용하여 API 호출하는 방법을 살펴보겠습니다.

 

Combine 사용 방법

Combine은 iOS에 내장된 프레임워크이므로 외부 종속성을 추가할 필요가 없습니다. 기본적으로 Combine을 사용할 Swift 파일에서 아래 코드를 추가하면 됩니다.

import Combine

Model 생성

For this demonstration, let’s assume we’re building an app that fetches a list of todos from a REST API. We’ll need a model to represent a todo item. Create a Swift file named Todo.swift and define the model like this:swiftCopy code

 

이 글에서는 REST API에서 할 일 목록을 가져오는 앱을 구축한다고 가정해 보겠습니다. 할 일 항목을 나타내는 모델이 필요합니다. Todo.swift라는 Swift 파일을 만들고 다음과 같이 모델을 정의합니다.

struct Todo: Decodable {
    let id: Int
    let title: String
    let completed: Bool
}

API Service 생성

다음으로 API 요청을 위한 서비스를 만들어 보겠습니다. 실제 시나리오에서는 Alamofire와 같은 네트워킹 라이브러리를 사용하고 싶을 수도 있지만 단순화를 위해 내장된 URLSession을 사용하겠습니다. TodoService.swift라는 Swift 파일을 생성하고 서비스에 대한 클래스를 정의합니다.

import Foundation
import Combine

class APIService {
    private let url = URL(string: "https://jsonplaceholder.typicode.com/todos")!    
    
    func fetchTodos() -> AnyPublisher<[Todo], Error> {
        URLSession.shared.dataTaskPublisher(for: url)
            .map(\.data)
            .decode(type: [Todo].self, decoder: JSONDecoder())
            .eraseToAnyPublisher()
    }
    
}

 

이 코드에서는 Combine AnyPublisher를 반환하는 fetchTodos 메서드를 사용하여 APIService 클래스를 정의합니다.

  • URLSession을 사용하여 API 요청을 만듬
  • JSON 응답을 Todo 객체 배열로 디코딩
  • AnyPublisher에 대한 유형을 지움

View Model 생성

Now, let’s move to our view model. Create a Swift file named TodoViewModel.swift:

이제 뷰 모델을 만들어 보겠습니다. ViewModel.swift라는 Swift 파일을 만들고 아래와 같이 만듭니다.

import Foundation
import Combine

class TodoViewModel: ObservableObject {
    private var cancellables = Set<AnyCancellable>()
    private let apiService = APIService()    
    @Published var todos: [Todo] = []    
    
    func fetchTodos() {
        apiService.fetchTodos()
            .receive(on: DispatchQueue.main)
            .sink(receiveCompletion: { _ in }) { todos in
                self.todos = todos
            }
            .store(in: &cancellables)
    }
}

뷰 모델에서는 APIService의 fetchTodos 메서드를 호출하는 fetchTodos 메서드를 정의합니다. @Published 속성 래퍼를 사용하여 데이터가 변경될 때 UI를 자동으로 업데이트하는 todos라는 게시된 속성을 만듭니다. 또한 UI 업데이트를 위해 메인 스레드에서 업데이트가 수신되었는지 확인하기 위해 receive(on:)을 사용합니다.

View

마지막으로 할 일 목록을 표시하는 SwiftUI 보기를 만들어 보겠습니다. TodoListView.swift라는 Swift 파일을 만듭니다.

import SwiftUI

struct TodoListView: View {
    
    @ObservedObject var viewModel = TodoViewModel()    
    
    var body: some View {
        NavigationView {
            List(viewModel.todos, id: \.id) { todo in
                Text(todo.title)
            }
            .onAppear {
                viewModel.fetchTodos()
            }
            .navigationBarTitle("Todos")
        }
    }
}

struct TodoListView_Previews: PreviewProvider {
    static var previews: some View {
        TodoListView()
    }
}

TodoListView.swift 에서는 todos 속성의 변경 사항을 관찰하기 위해 ViewModel 유형의 viewModel이라는 ObservedObject를 만듭니다. List를 사용하여 할 일 목록을 표시하고 onAppear 수정자에서 viewModel.fetchTodos()를 호출하여 뷰가 나타날 때 데이터를 가져옵니다.

마무리

Combine은 iOS 앱의 API 호출과 같은 비동기 작업을 처리하기 위한 강력한 프레임워크입니다. 비동기 데이터 스트림으로 작업하는 선언적이고 기능적인 방법을 제공하여 코드를 더 읽기 쉽고 유지 관리하기 쉽게 만듭니다.

이번 글에서는 기본 프로젝트 설정, 모델 생성, API 서비스 구축, Combine을 사용하여 뷰 모델에서 데이터 가져오기 과정을 살펴보았습니다. 이러한 기본 사항을 갖추면 API 호출 및 기타 비동기 작업을 처리하기 위해 Combine을 사용하는 보다 복잡하고 반응성이 뛰어난 iOS 앱을 구축할 수 있습니다. 

 

감사합니다.

반응형