SwiftUI+URLSession+Combine에서 API를 두드려 보았습니다.

소개



취미로 Swift를 쓰고 있는 @KaitoKudou 입니다.
지금까지 SwiftUI는 학습한 적이 없어, 어쩐지 해보려고 생각해, SwiftUI를 사용해 외부 API를 두드려 보려고 했습니다. 어차피라면 해본 적 없는 Combine 프레임워크도 조금 걸어 보았습니다. (조금만)

데모 앱 개요



이번에는 Connpass API을 사용하여 List에서 이벤트를 표시하는 간단한 데모 앱을 만들었습니다.

먼저 완성판을 보여드리겠습니다.

이번에 만든 프로젝트는 GitHub로 올리고 있습니다.
htps : // 기주 b. 코 m / 카이 토쿠 도 / 슈 f 츠 _ 코 m 비네

모델 정의



응답을 보면서 모델을 작성합니다.
import Foundation

struct ConnpassGeneral: Codable {
    let events: [Event]
}

struct Event: Codable, Hashable, Identifiable {
    let id: Int
    let title: String
    let eventUrl: String

    enum CodingKeys: String, CodingKey {
        case id = "event_id"
        case title = "title"
        case eventUrl = "event_url"
    }
}

통신부


import Combine
import Foundation

class ConnpassTopViewModel: ObservableObject {
    var disposable = Set<AnyCancellable>()
    @Published var connpassGeneral: ConnpassGeneral?
    @Published var eventData: [Event]?

    init() {
        fetchConpassEvents()
    }

    func fetchConpassEvents() {
        let decoder = JSONDecoder()
        decoder.dateDecodingStrategy = .iso8601
        let url = URL(string: "https://connpass.com/api/v1/event/?keyword=Swift&order=3")!
        let request = URLRequest(url: url)

        URLSession.shared.dataTaskPublisher(for: request)
            .map({ (data, response) in
                return data
            })
            .decode(type: ConnpassGeneral.self, decoder: decoder)
            .receive(on: DispatchQueue.main)
            .sink(receiveCompletion: { completion in
                switch completion {
                case .failure(let error):
                    print("error : " + error.localizedDescription)
                case .finished:
                    print("----------success-----------")
                }
            }, receiveValue: { [weak self] connpassGeneral in
                self?.eventData = connpassGeneral.events
                print(self?.eventData as Any)
            })
            .store(in: &disposable)
    }
}

List 부분



UIKit에서 말하는 UITableView에 해당하는 부분입니다.
import SwiftUI

struct ConnpassTopView: View {
    @ObservedObject var connpassTopViewModel = ConnpassTopViewModel()

    var body: some View {
        NavigationView {
            List(connpassTopViewModel.eventData ?? [Event.init(id: 0, title: "hoge", eventUrl: "hoge")]) { event in
                NavigationLink(
                    destination: ConnpassEventDetailView(eventData: event)) {
                    ConnpassEventRowView(eventData: event)
                }
            }
            .navigationTitle("Connpassイベント")
        }
        .onAppear(perform: {
            self.connpassTopViewModel.fetchConpassEvents()
        })
    }
}

struct ConnpassTopView_Previews: PreviewProvider {
    static var previews: some View {
        ConnpassTopView(connpassTopViewModel: ConnpassTopViewModel())
    }
}

셀을 눌렀을 때 전환 대상



이번에는 셀을 누르면 SFSafariViewController를 사용하여 Safari로 전환하도록했습니다. 그러나 SFSafariViewController는 UIKit에서 사용할 수있는 클래스입니다. SwiftUI의 View에서 UIKit을 사용하려면 UIViewControllerRepresentable 프로토콜을 준수하면 사용할 수 있습니다.
UIViewControllerRepresentable 프로토콜에서는 makeUIViewController(context:)와 updateUIViewController(_:context:)가 필수 메서드입니다.
import SafariServices
import SwiftUI

struct ConnpassEventDetailView: UIViewControllerRepresentable {

    var eventData: Event
    typealias UIViewControllerType = SFSafariViewController

    func makeUIViewController(context: Context) -> SFSafariViewController {
        let url = URL(string: eventData.eventUrl)
        return SFSafariViewController(url: url!)
    }

    func updateUIViewController(_ uiViewController: SFSafariViewController, context: Context) {
    }
}

struct ConnpassEventDetailView_Previews: PreviewProvider {
    static var eventData = ConnpassTopViewModel().eventData
    static var previews: some View {
        ConnpassEventDetailView(eventData: eventData?[0] ?? Event.init(id: 0, title: "hoge", eventUrl: "hoge"))
    }
}

마지막으로



전체적으로 적은 코드 양으로 끝났습니다. SwiftUI는 UIKit에 비해 기술량이 줄어들기 쉽다고 생각했습니다. 아직 나는 UIKit 쪽이 구현에 걸리는 시간이 짧습니다만, SwiftUI에 익숙해지면 개발이 폭속으로 진행된다고 생각했습니다. Combine에 관해서는 아직 지식이 부족하기 때문에 정진합니다 ...
기존의 라이브러리도 SwiftUI에 대응한 것은 적고, 아직 잠시는 UIKit를 사용한 개발이 주류일까라고 생각했습니다.

좋은 웹페이지 즐겨찾기