2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

SwiftUI+URLSession+CombineでAPI叩いてみた

Posted at

はじめに

趣味でSwiftを書いている@KaitoKudouです。
これまでSwiftUIは学習したことがなく、なんとなくやってみようと思い、SwiftUIを使って外部APIを叩いてみようと思いました。どうせならやったことないCombineフレームワークもちょっとかじってみました。(ほんのちょっとだけ)

デモアプリ概要

今回はConnpass APIを使用してListでイベントを表示する簡易的なデモアプリを作りました。

先に完成版をお見せします。
da17f00acdb28498565f0c2a0f7f6331.gif
今回作成のプロジェクトはGitHubに上げています。
https://github.com/KaitoKudou/SwiftUI_Combine

モデルを定義

レスポンスを見ながらモデルを書きます。

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を使った開発が主流なのかなと思いました。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?