[SwiftUI] Networking , Opserver Pattern ( ObservableObject, Published )

오늘은 SwiftUI를 활용해서 URLSession으로 API의 데이터를 가져오는 과정과 해당 데이터가 변동될때 자동으로 반영되도록 하는 Opserver Pattern을 적용해 보았다.

 

데이터 모델 정의

먼저 데이터를 가질 모델을 정의해야한다. Hacker News API의 JSON 응답을 기반으로 Results , Post 구조의 모델을 만들었다.

struct Results: Decodable {
    let hits: [Post]
}

struct Post: Decodable, Identifiable {
    var id: String {
        return objectID
    }
    let objectID: String
    let points: Int
    let title: String
    let url: String
}

Post 는 개별 게시물의 데이터를 나타내고 Results는 API 응답의 루트 객체이다. Post는 Identifiable 프로토콜을 채택하여 SwiftUI의 List에서 사용될 수 있도록 할 예정이다.

 

NetworkManager 클래스 구현

다음으로 API 요청하고 데이터를 가져오는 네트워크매니저를 만들었다. 이 네트워크매니저는 ObservableObject 프로토콜을 채택하여 데이터를 변경할때 SwiftUI 뷰가 자동으로 업데이트 되도록 할 예정이다.

import Foundation

class NetworkManager: ObservableObject {
    
    @Published var posts = [Post]()
    
    func fetchData() {
        if let url = URL(string: "<http://hn.algolia.com/api/v1/search?tags=front_page>") {
            let session = URLSession(configuration: .default)
            let task = session.dataTask(with: url) { data, response, error in
                if error == nil {
                    let decoder = JSONDecoder()
                    if let safeData = data {
                        do {
                            let results = try decoder.decode(Results.self, from: safeData)
                            DispatchQueue.main.async {
                                self.posts = results.hits
                            }
                        } catch {
                            print(error)
                        }
                    }
                }
            }
            task.resume()
        }
    }
}

ObservableObject 로 채택했기 때문에 이 객체의 변경 사항은 구독하는 SwiftUI 뷰가 자동으로 업데이트 된다.

@Published로 변수를 선언해주면 해당하는 변수가 변경 될 때 마다 자동으로 구독된 객체에 알려주게된다.

 

ContentView 구현

ContentView에서는 네트워크매니저의 데이터를 구독하고 해당 데이터를 LIst에 표시하도록 구현했다.

import SwiftUI

struct ContentView: View {
    
    @ObservedObject var networkmanager = NetworkManager()
    
    var body: some View {
        NavigationView {
            List(networkmanager.posts) { post in
                HStack {
                    Text(String(post.points))
                    Text(post.title)
                }
            }
            .navigationBarTitle("H4X0R NEWS")
        }
        .onAppear {
            networkmanager.fetchData()
        }
    }
}

NetworkManager 의 인스턴스를 구독하여 해당 인스턴스의 posts 배열이 변경될 때마다 뷰가 업데이트 되도록 구현한다.

 

그 이후 List 내 해당 데이터를 보여주고 싶기 떄문에 List(networkmanager.posts) 로 API의 넘어온 데이터를 보여줄 수 있도록 구현한다.

 

onAppear는 뷰가 화면에 나타날때 networkmanager.fetchData() 를 호출하여 데이터를 가져올 수 있도록 하는 역할을 담당한다.

 

이렇게 데이터를 받아온 뒤 옵저버 패턴을 사용해서 데이터를 뷰에 보여지게 된다. 이 값은 변경될때마다 자동으로 업데이트 내용을 변경한다.

 

이전에 업데이트 된 내용을 변경하려면 신경써서 리로드를 해주거나 데이터를 항상 최신으로 변경해야하는 번거로움이 있었는데 훨씬 편하고 생산성있게 구현할 수 있게 되는 것 같다.