LoginSignup
12
12
iOS強化月間 - iOSアプリ開発の知見を共有しよう -

【SwiftUI】ScreenTimeAPIで特定のアプリを一定時間全く使用できないよう制限する

Last updated at Posted at 2023-09-27

本記事ではScreenTimeAPIを用いて、他のアプリの使用を制限する方法を紹介します。

はじめに

作業をしている途中に、つい息をするようにXを開いてしまい、集中が途切れてしまうことは誰にでもあると思います。
AppleにはScreenTimeAPIという機能が用意されており、他のアプリを制限できるようカスタマイズすることが可能です。

ScreenTimeAPIとは

WWDC2021にはScreenTimeAPIが発表され、FamilyControlsManagedSettingsDeviceActivityという3つの新しいフレームワークが導入されました。

  • Family Controls
    ScreenTimeAPIへのアクセスを認証。アプリやウェブサイトを識別するために使われる透過トークンを提供して、ユーザープライバシーを保護する。

  • ManagedSettings
    ScreenTimeで設定可能な制限を適用し、アプリの使用制限や、Webサイトのフィルタリング等の、アクティビティを制限すること可能。

今回は上記2つのフレームワークを使用し、アプリの使用を制限してみます。

デモ

使用を制限するアプリと制限時間を設定し、制限開始をすると制限時間内はアプリの制限を解除することができない仕様になります。(残り時間が0になるまで、制御停止ボタンが押せない)

実装

1. FamilyControlsをアプリに追加する

  • Apple DeveloperのProfileの設定欄にあるAdditional Capabilitiesで、FamilyControlsにチェックを入れる
  • Xcodeの+Cababilityの箇所から、FamilyControlsを追加する

注意
FamilyControlsを使用するアプリをTestFlight、またはAppStoreで公開するには、こちらからAppleに許可をリクエストする必要があるそうです。

2. FamilyControlsの承認をリクエストする

import SwiftUI
import FamilyControls

@main
struct ScreenTimeSamplesApp: App {
    
    let center = AuthorizationCenter.shared
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .onAppear {
                    Task {
                        do {
                            try await center.requestAuthorization(for: .individual)
                        } catch {
                            // Handle the error here.
                        }
                    }
                }
        }
    }
}

3. familyActivityPickerを実装する

アプリ、カテゴリを指定するPickerViewを設定します。

import SwiftUI

struct ContentView: View {

    @StateObject var viewModel: ContentViewModel
    @State private var isPresented = false    

    var body: some View {
        Button {
            isPresented = true
        } label: {
            Text("選択する")
        }
        .familyActivityPicker(
            isPresented: $isPresented, 
            selection: $viewModel.selection
        )
    }
}

4. FamilyActivityPickerで選択したアプリ、カテゴリーのTokenをManagedSettingsStoreで設定する

選択したアプリ、カテゴリーのTokenをshieldプロパティにセットすることで、使用を制限することができます。nilをセットすれば、制限を解除することができます。

func startBlocking() {
    let store = ManagedSettingsStore(named: ManagedSettingsStore.Name)
    store.application.denyAppRemoval = true
    store.shield.applicationCategories = .specific(selection.categoryTokens)
    store.shield.applications = selection.applicationTokens
}

func stopBlocking() {
    let store = ManagedSettingsStore(named: ManagedSettingsStore.Name)
    store.shield.applications = nil
    store.shield.applicationCategories = nil
    store.clearAllSettings()
}

Q1: アプリを削除すれば、制御を解除することができるのでは?

A1: static let denyAppRemoval: SettingMetadata<Bool>を指定することで、アプリの削除を防止することができます。

let store = ManagedSettingsStore(named: ManagedSettingsStore.Name)
store.application.denyAppRemoval = true

これにより、アラート内の「アプリを削除する」の選択肢が表示されなくなり、制御を設定しているアプリの削除を防ぐことが可能です。

注意
このプロパティを設定すると、使用制限をかけているアプリ以外もアンインストールが制限されます。

Q2: アプリのアイコンはどう取得するのか?

A2: ユーザーが選択したアプリやカテゴリのアイコンを表示するには、ApplicationTokenActivityCategoryToken、またはWebDomainTokenのいずれかを使用してLabelインスタンスを生成することで実現できます。

FamilyActivityPicker(selection: $selection)
if let applicationToken = selection.applicationTokens.first {
    Label(applicationToken)
        .labelStyle(.iconOnly)
}

8789961f-6596-4a38-9448-28992c921349.png
引用記事

まとめ

今回はManagedSettingsを使用してアプリの制限だけを試してみましたが、DeviceActivityも使用すればアプリの使用状況を監視することもできるようになるので、いつか試してみたいと思います。

サンプルリポジトリ

参考資料

12
12
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
12
12