🧑‍🚀

Apollo iOSとGithub APIでContributionを表示するWidget

2023/02/24に公開

GithubのContributionを表示するiOSのWidgetを作った。
今日のコミット数のみ表示。


今日のコミット数を表示するWidget

Xcodeプロジェクトの作成

Xcodeで新規プロジェクト作成>SwiftUI
プロジェクト作成後、File>New>TargetからWdiget Extensionを追加

Github APIのトークン生成

https://github.com/settings/tokensでトークン生成

Apolloのインストール

公式ドキュメントに従ってセットアップ

1. ApolloのInstall

SPM with Xcode Projectでインストール
WidgetExtensionの設定のFrameworks and LibrarieでApolloを追加しないとimportでエラーとなったので追加。

2. Github APIのSchemaをダウンロード

ApolloとGraphqlをインストール

yarn add global apollo graphql

GithubAPIのSchemaをダウンロード

apollo schema:download --endpoint="https://api.github.com/graphql" --header "Authorization: Bearer {TOKEN}" 

schema.jsonが作成される。

3. .graphql ファイルの作成

取得したいデータのクエリを作成。
この記事を参考に作成しました。
https://zenn.dev/yuichkun/articles/b207651f5654b0

GithubのContributionを取得するクエリを.graphqlファイルとして作成。
次のStepでこのクエリを元にコード生成するためqueryの後にクエリ名を指定する必要があります。

ContributionCount.graphql
query ContributionCount {
  user(login: "username"){
    contributionsCollection {
      contributionCalendar {
        totalContributions
        weeks {
          contributionDays {
            contributionCount
            date
          }
        }
      }
    }
  }
}

4. code generationの設定

コード生成用の設定ファイルの作成。
チュートリアル通りにProject>Install CLIでapollo-ios-cliが生成されます。

以下のコマンドで設定ファイルapollo-codegen-config.jsonを生成します。

/apollo-ios-cli init --schema-name {SCHEMA_NAME} --module-type embeddedInTarget --target-name {TARGET_NAME}

schemaSearchPathsを変更。

apollo-codegen-config.json
"schemaSearchPaths" : [
  "./schema.json"
]

3で作成したクエリからAPIアクセス用のコードを生成します。

./apollo-ios-cli generate

--schema-nameで指定した名前のディレクトリが作成されます。
このディレクトリをXcodeのTarget(WidgetExtension)に追加
Copy items if neededCreate groupsが選択されてることを確認。

Apollo Clientの作成

こちらの記事のApolloClientの実装をお借りしました。
https://zenn.dev/u_dai/articles/e344a130919281

データ取得

データ取得する関数とクラス作成。

Contribution.swift
func fetchGithubContribution(completion: @escaping (Int?) -> Void) {
    let apollo = GraphQLClient.shared.apollo
    apollo.fetch(query: GithubAPISchema.ContributionCountQuery()) { result in
        guard let data = try? result.get().data else {
            completion(nil)
            return
        }
        let contributionCount = data.user?.contributionsCollection.contributionCalendar.weeks.last?.contributionDays.last?.contributionCount
        completion(contributionCount)
    }
}

WidgetのProviderのgetTimelineを編集
widgetの更新間隔は最低でも15分開ける必要があるとのこと。

contributionWidget.swift

func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
    let currentDate = Date()
    let futureDate = Calendar.current.date(byAdding: .minute, value: 15, to: currentDate)!
    contribution().fetchGithubContribution { contributionCount in
        var entry = SimpleEntry(date: futureDate, contribution: 0, configuration: configuration)
        if let count = contributionCount {
            entry = SimpleEntry(date: futureDate, contribution: count, configuration: configuration)
        }
        let timeline = Timeline(entries: [entry], policy: .after(futureDate))
        completion(timeline)
    }
}

WigetのViewを編集。

contributionWidget.swift

struct contributionWidgetEntryView : View {
    var entry: Provider.Entry

    var body: some View {
        ZStack {
            Color.black
            VStack {
                VStack(alignment: .center){
                    Text("Contribution")
                }
                .foregroundColor(Color.white)
                .font(Font.system(size: 12).bold())
                
                Text(String(entry.contribution))
                    .foregroundColor(Color.white)
                    .font(Font.system(size: 40).bold())
            }
        }
    }
}

Widget Extentionのデバッグ

Xcodeで普通にブレークポイントを置くだけではデバッグできず。
エミュレーターで実行後、Debug->Attach to Process-> WidgetExtensionのプロセスを指定することでデバッグすることができた。

Discussion