LoginSignup
6
11

More than 3 years have passed since last update.

[iOS]APIKit + Combine + SwiftUIでGitHubリポジトリ検索

Last updated at Posted at 2020-08-01

iOS13に出てきたSwiftUI・Combineと、ライブラリのAPIKitを使い、通信部分の実装手法を紹介したいと思います。
今回は、GitHub APIを使用します。

どのようなアプリを作るか

以下のようなアプリを作ります。
リポジトリ名を入力し、結果を一覧で取得します。

apikit_02.gif

実装のポイント

  • できるだけシンプル:冗長なコードはなるべく書かない
  • 保守性がある:読みやすいコード
  • 再利用性がある:別の異なるAPIでも少しの変更で利用が可能

開発環境・使用するライブラリなど

開発環境

  • OS:10.15.6
  • Xcode:10.6
  • iOS:iOS13以降(iOS14では確認していません)

使用するライブラリ

APIKit
今回は、APIKitを拡張し、CombineのPublisherでAPIを叩いた結果を取得します。

使用するフレームワーク

  • SwiftUI
  • Combine

また、Swift標準のDecodableを使い、JSONをパースします。

使用する考え方

  • リポジトリパターン:APIやDBにアクセスするための定義を抽象化

実装

全体像

APIKit.png

  • ContentView:UI表示
  • GitHubSearchModel:ロジックを管理するクラス
  • GitHubRepository:APIへのアクセス方法を定義

API基本部分

API部分のクラス相関図は以下のようになります。

APIKit-2.jpg

GitHubRepository.swiftについては、中身は以下のようになっています。

GitHubRepository.swift
import Foundation
import APIKit

class GitHubRepository {

    //GitHubレスポンス用のデコーダー
    static let decoder: GitHubDecoder = .init()

    //リポジトリを検索
    struct SearchRepositories: GitHubRequestProtocol {
        //https://developer.github.com/v3/search

        //検索クエリ
        let query: String

        let method: HTTPMethod = .get
        let path: String = "/search/repositories"
        var decoder: JSONDecoder {
            return GitHubRepository.decoder
        }

        var params: [String: Any] {
            return [
                "q": query
            ]
        }

        typealias Response = SearchResponse

    }

    private init() { }
}

GitHubRepositoryはリポジトリなので、APIアクセスに必要な情報だけを定義(抽象化)しています。
SearchResponseはDecodableに準拠しています。
GitHubRequestProtocol.swiftGitHubDecoder.swiftAPIDataParser.swiftはソースをご確認ください。

ここまでで、GitHub APIを叩くための準備ができました。

Modelの実装

GitHub APIを使い、情報を取ってくるロジック(モデル)を実装します。
今回はViewとしてSwiftUIを使用するので、ロジックはObservableObjectを継承したクラスにします。

GitHubSearchModel.swift
/// 検索モデル
class GitHubSearchModel: ObservableObject {

    @Published var items: [SearchItem] = [] //検索結果
・・・・
    /// 検索を行う
    func search() {
        debugPrint("search")

        // 1)APIリクエストを作成
        let request = GitHubRepository.SearchRepositories(query: searchText)

        // 2)CombineのPublisherを作成し、通信処理を行う
        self.requestCancellable = request.publisher //Publisherに変換(この時点で通信処理を行なっている)
            .receive(on: DispatchQueue.main) //メインスレッドで受け取る
            .sink(receiveCompletion: { result in
・・・・
            }, receiveValue: { [weak self] response in
                // 3)結果をitemsに保存
                self?.items = response.items
            })
        // 4)itemsの更新はCombineを通して通知される

    }

1) APIリクエストを前述のリポジトリから作成します。
2) 通信処理を行います。
3) 結果をitemsに格納します
4) itemsの変更はCombineによりUIに通知されます。

UI

ContentView
こちらをご確認ください。

ソースコード

今回のソースコードはこちらに置いてあります。
https://github.com/usk-lab/APIKitCombine

再利用性について

今回はGitHub APIを使用しました。
GitHubRepository, GitHubRequestProtocol, GitHubDecoderを少し変えることで他のAPIサービスでも利用できます。

参考

6
11
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
6
11