2回目の登場、はてなエンジニアアドベントカレンダー2024 50日目の記事です。
1回目は SwiftでAdvent of Code 2024 #AdventOfCode - がんばってなんか書く
Google Device Access
家には猫監視用にGoogle Nest Camがあります。
これはAPIが公開されているので、iOSアプリとしてアクセスしてみたい。
基本は Get Started | Device Access | Google for Developers を進めていくといいのですが、iOSアプリのでOAuthは直接フォローされていないので、備考録的にまとめます。
準備
iOSアプリ作成前に、環境の準備が必要なので済ませていきます。
Device Accessの登録
https://developers.google.com/nest/device-access/get-started#register_for_device_access
Device AccessのAPIを使うためには登録が必要で、US$5+taxがかかります。
¥860でした。
Google Cloud OAuth 2.0
https://developers.google.com/nest/device-access/get-started#set_up_google_cloud_platform
OAuth 2.0は、Google Cloudを使って行います。
プロジェクトを選んで、アプリケーションスコープを選択します。
iOS以外にもAndroidやDesktop Appもあります。

iOSでは、Bundle identifierを入力します。
作成が完了すると、Info.plistをダウンロードできます。 *1
一旦はテストモードにしておくとよいでしょう。
Google Auth Platform > Audience から、Publishing statusをTestingに変更します。
Test usersに自身のGoogleアカウントユーザーを登録すれば、自分だけが認証を通すことができます。
Device Accessプロジェクト作成
https://developers.google.com/nest/device-access/get-started#create_a_device_access_project
Device Accessコンソールに戻って、Device Access側でもプロジェクトを作成します。
Google Cloudで作った OAuth client ID が必要です。
Google Nest permissions設定
Device AccessプロジェクトをSandboxで運用する場合、デバイスを個別に許可する必要があります。
これはアプリ内OAuthのフローには組み込まれていないため、OAuth URLで選択してください。
https://nestservices.google.com/partnerconnections/{project-id}/auth
?redirect_uri={redirect_uri}
&response_type=code
&client_id={client_id}
&scope=https://www.googleapis.com/auth/sdm.service
&access_type=offline

これでAPIにアクセスする準備完了。
iOSアプリ
Google Sign-Inのインストール
Google CloudのOAuth 2.0は、Google Sign-InのSDKを使いましょう。
上記のGet Startedで概ねいいですが、微妙に情報が古かった。
Swift Package Managerでは7.0.0を使うように書いていますが、2025/01/19現在の最新は8.0.0がリリースされているので、こちらを使います。
OAuth client IDの設定
https://developers.google.com/identity/sign-in/ios/start-integrating#configure_app_project
Info.plistにGIDClientIDとCFBundleURLSchemesを設定します。
例示されている com.googleusercontent.apps.1234567890-abcdefg だと、以下を入力することになります。
- GIDClientID: 1234567890-abcdefg.apps.googleusercontent.com
- CFBundleURLSchemes: com.googleusercontent.apps.1234567890-abcdefg
後述の認証をしてみると、アプリ内で完結するためにDeepLinkは使わなそうな気もしますが、サインインを始めるとエラーになります。
Your app is missing support for the following URL schemes: com.googleusercontent.apps.1234567890-abcdefg
OAuth
https://developers.google.com/identity/sign-in/ios/sign-in#3_add_a_google_sign-in_button
GoogleSignではSwiftUIをサポートしています。
しかし、UIViewControllerを要求するのでちょっとテクニックが必要。
UIViewControllerRepresentable > UIHostingController > SwiftUI.View で回避しました。
SwiftUIで提供されているメリットが薄いので、もっと楽な方法がありそう。
public struct SignInView: UIViewControllerRepresentable { public init() {} public func makeCoordinator() -> () {} public func makeUIViewController(context: Context) -> some UIViewController { SignInViewController(rootView: SignInViewController.ContentView()) } public func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {} } private final class SignInViewController: UIHostingController<SignInViewController.ContentView> { public override func viewDidLoad() { super.viewDidLoad() self.rootView.rootViewController = self } } extension SignInViewController { struct ContentView: View { var rootViewController: UIViewController! public var body: some View { GoogleSignInButton {} } } }
SDKはドキュメントのサンプルコードから更新されていて、Swift Concurrency対応しています。
Smart Device Management APIにアクセスするためには、アクセススコープを追加する必要もあります。
https://www.googleapis.com/auth/sdm.service
GoogleSignInButton {
Task {
_ = try await GIDSignIn.sharedInstance.signIn(
withPresenting: rootViewController,
hint: nil,
additionalScopes: ["https://www.googleapis.com/auth/sdm.service"]
)
}
}
これでボタンを押すと、よく目にするGoogleアカウントの連携画面が表示されて、許可することでアクセストークンを取得できます。
最短ルートのはず。
API実行
https://developers.google.com/nest/device-access/authorize#4-api-call
devices.list APIを実行してみましょう。
Task {
let url = "https://smartdevicemanagement.googleapis.com/v1/enterprises/\(projectId)/devices"
var request = URLRequest(url: URL(string: url)!)
request.httpMethod = "GET"
request.setValue("Bearer \(access-token)", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let (data, response) = try await URLSession.shared.data(for: request)
print(String(data: data, encoding: .utf8))
}
Responseを確認して、成功しているでしょう!
おわり
までを行いました。
次の目標はLiveストリームにアクセスして、WebRTCを表示すること。
WebRTCのライブラリがないので、情報お待ちしております!
*1:現代的には、これを直接使う感じではなさそう