iOS

Meet the Screen Time API (WWDC21)

GREEN.1229 2024. 3. 14. 19:00

안녕하세요. 그린입니다 🍏

이번 포스팅에서는 Screen Time API에 대해 알아보려 합니다 🙋🏻

나온지 꽤 되었지만, 이제야 궁금해서 한번 학습해보려고해요ㅎㅎ

학습 레퍼런스는 WWDC를 기준으로 알아봅니다!

 

 


Meet the Screen Time API

2021년 WWDC에서 Meet the Screen Time API라는 섹션에서 처음 이 API가 어떻게 사용되는지 소개하고 있습니다.

사실 나온지는 3년전인 2018년에 기능 자체는 나왔죠!

 

 

디바이스를 통해 사용자와 가족의 관계를 개선하는데 큰 진전이 있었다고 합니다.

실제로 가장 큰 사용사례이자 원했던 의도도 아이와 관련이 있을겁니다.

녀들을 둔 부모님들이 자녀의 디바이스에서 앱 사용 시간들을 파악할 수 있고, 또 제한을 걸 수도 있습니다.

이를 통해 조금 더 안전하고 절제된 스마트폰 사용 습관을 형성해줄 수 있죠!

또한, 자녀가 없더라도 가족들과 서로 사용량을 공유해 해당 디바이스가 어떻게 사용되는지 확인할 수 있죠.

 


사용 환경

1️⃣ iPhone, iPad, Mac 모두 해당 기능 사용이 가능합니다.

2️⃣ iOS와 iPadOS 15 이상부터 도입되어 사용 가능합니다.

3️⃣ 100% Swift와 SwiftUI 코드로 이루어져 있습니다.

 


세가지 기본 원칙

1️⃣ 기존 제한 사항에 대해 직접적인 API 접근을 위한 최신 온디바이스 프레임워크를 제공

2️⃣ 사용자 개인정보를 보호하는 것

3️⃣ 자녀 보호 기능 경험을 위해 더욱 다채로운 개발적인 부분을 보장

 

즉, 정리해보면 쉽고 다채롭게 개발자는 개발할 수 있고 또한 사용자 개인정보가 보호되는 원칙이 있기에, 애플 외에 그 누구도(가족 구성원이라 할지라도) 앱이나 웹에서 어떤 정보들을 보았고 사용했는지까지는 알 수 없습니다.

 

이 세가지 기본 원칙은 아래서 설명될 세가지 프레임워크가 만나 형성되었습니다.


세가지 프레임워크

 

1️⃣ Managed Settings

관리 설정을 통해서 앱은 스크린 타임에서 사용할 수 있는것과 동일한 제한 사항에 직접적으로 접근할 수 있습니다.

앱에서 자녀가 기기로 할 수 있는 작업들을 제한하며 보호자가 다시 설정할때까지 이 제한이 그대로 유지되는 더 나은 방법이 필요했습니다.

그럴때 이 관리 설정을 통해서 앱에서 스크린 타임과 마찬가지로 계정 잠금, 패스워드 변경 방지, 앱 보호 등 다양한 제한 사항들을 설정할 수 있고 또한 앱의 기능에 맞게도 맞춤으로 설정이 가능하죠.

 

2️⃣ Family Controls

가족 컨트롤을 통해 애플에서 추구하는 개인 정보 보호 정책을 주도하고 있습니다.

해당 기능을 통해 가족 공유를 활용하여 보호자 승인 없이는 Screen Time API에 접근하는것을 방지할 수 있습니다.

또한 보호자 승인이된 앱은 기기에서 보호자 승인 없이는 제거가 불가능해요.

가족 컨트롤은 앱과 웹 사이트를 나타내는 불투명 토큰을 제공합니다.

이 토큰으로 API 전체에서 사용을 모니터링할 수도 있고 가족 공유 그룹 외에는 누구도 어떤 앱이나 웹을 사용했는지 알 수 없도록 해줍니다.

 

3️⃣ Device Activity

장치 활동을 사용해 앱을 실행하지 않고도 코드를 실행할 수 있는 등의 새 기능을 앱에 제공해 스크린 타임 이상의 기능을 활용할 수 있습니다.

쉽게 생각해서 보호자만 사용할 수 있는 앱을 지정한다면 자녀가 보호자의 앱을 실행할 수 없죠.

이 제한을 설정하기 위해서는 장치 활동 일정과 이벤트를 이용해야 합니다.

장치 활동 일정은 시작과 끝에서 앱의 확장을 호출하는 시간 창입니다.

이벤트는 사용자가 장치 활동 일정의 사용 임계값에 도달할때 확장을 호출하는 사용 모니터입니다.

 

결국 이 세가지의 프레임워크를 결합하여 아래와 같은 플로우를 따를 수 있어요.

 

1️⃣ 보호자와 자녀 기기에 앱을 설치 후 보호자가 자녀의 기기에서 앱을 오픈 

2️⃣ 앱은 가족 컨트롤을 통해 승인

3️⃣ 나중에 보호자 기기의 앱이 설정/제한/규칙을 선택

4️⃣ 앱이 해당 정보를 자녀의 기기로 전송

5️⃣ 자녀의 기기에서 앱은 기기 활동을 통해 일정과 이벤트를 생성하여 일정 및 이벤트가 발생하면 앱의 장치 활동 확장이 호출

6️⃣ 확장 프로그램에서 관리 설정으로 제한 사항을 설정

 


Screen Time API를 Homework 데모앱으로 사용해보기

프로젝트 설정

 

Xcode에서 해당 앱 타겟을 선택하고 Signing & Capabilities 탭에서 Family Controls를 추가합니다.

 


가족 컨트롤 승인 요청

import FamilyControls

AuthorizationCenter.shared.requestAuthorization { result in
    switch result {
    case .success():
	...
    case .failure(let error):
	...
    }
}

 

해당 권한 요청 코드를 작성해야 합니다.

즉, 앱이 실행될 때 가족 컨트롤 프레임워크의 공유 인증 센터를 사용해 요청을 합니다.

해당 기능을 호출하기 위해서는 우선 보호자가 해당 앱에 대한 가족 컨트롤을 승인해야 합니다.

 

 

한번도 실행된 적이 없는 앱이라면 requestAuthorization이 이렇게 얼럿을 통해 보호자의 승인을 요청합니다.

 

 

허용을 클릭하면 이제 보호자가 애플 계정을 통해 인증해야 합니다.

당연하겠지만, 로그인한 iCloud가 가족 공유를 사용하는 자녀가 아니라면 실패를 반환합니다.

 

성공적으로 인증이되면 이제 requestAuthorization을 호출하고 얼럿은 다시 뜨지 않고 자동으로 성공을 반환해주죠.

위치정보나 미디어 접근과 같은 일반적인 요청 얼럿의 플로우와 동일해요!

 

이 가족 컨트롤로 인증하여 기기가 인증이 된다면 사용자는 이제 더 이상 iCloud에서 로그아웃을 할 수 없습니다.

또한, 네트워크 확장 프레임워크로 구축된 기기 내 웹 콘텐츠 필터가 앱에 포함될 수 있고 자동으로 설치되며 제거할 수 없습니다.

이를 통해서 앱은 기기의 웹 트래픽을 필터링할 수 있죠.

 

자녀가 앱을 실행하지 않을 가능성이 높은 상황에서도 자녀의 기기에서 코드가 실행되도록 하는것은 중요합니다.

그렇기에 Screen Time API의 경우 장치 활동을 통해서 백그라운드에서 코드 실행을 수행하는 새로운 방법을 만들었다고 합니다!


Screen Time shield를 통해 기기 제한하기

보호자가 반복 일정에 따라 사용하지 않도록 선택한 앱을 보호할 수 있습니다.

즉, 해당 앱이 자녀의 기기에서 실행될거라는 보장을 할 수 없기에 보호자가 설정한 이후 앱이 실행되지 않아도 기기 활동 일정을 사용해 매일 앱의 보호 제한을 설정하는 방법입니다.

 

import DeviceActivity

class MyMonitor: DeviceActivityMonitor {
    override func intervalDidStart(for activity: DeviceActivityName) {
        super.intervalDidStart(for: activity)
    }
    override func intervalDidEnd(for activity: DeviceActivityName) {
        super.intervalDidEnd(for: activity)
    }
}

 

우선 익스텐션을 구현하려면 DeviceActivityMonitor를 상속받아 클래스화 시켜야합니다.

두 필수 메서드를 통해 일정 시작 및 종료 후 기기를 처음 사용할 때 호출되도록 합니다.

 

import DeviceActivity

extension DeviceActivityName {
    static let daily = Self("daily")
}

let schedule = DeviceActivitySchedule(
    intervalStart: DateComponents(hour: 0, minute: 0),
    intervalEnd: DateComponents(hour: 23, minute: 59),
    repeat: true
)

let center = DeviceActivityCenter()
try center.startMonitoring(.daily, during: schedule)

 

그 다음으로 장치 활동 모니터 익스텐션이 기본 앱에서 설정되었기에 장치 활동 네임과 장치 활동 일정을 만들어줍니다.

이렇게 만들어진 활동과 일정을 startMonitoring을 사용해 호출합니다.

이 코드를 통해 일정의 시작과 끝에서 호출되게 됩니다.

 

또한, Family Controls 프레임워크에는 작업을 위해서 family activity picker라는 SwiftUI 컴포넌트가 있습니다.

 

import FamilyControls
import SwiftUI

@StateObject var model = MyModel()
@State var isPresented = false

var body: some View {
    Button("Select Apps to Discourage") {
        isPresented = true
    }
    .familyActivityPicker(isPresented: $isPresented, selection: $model.selectionTpDiscourage)
}

 

즉 이러한 시트를 노출해주는데요.

 

여기서 보호자가 가족이 사용하는 앱, 웹사이트 그리고 카테고리들을 리스트로 선택할 수 있도록 허용할 수 있습니다.

보호자가 선택을 완료하면 앱은 반환된 불투명 토큰을 사용해 해당 토큰이 갖는 앱, 웹사이트, 카테고리에 제한을 설정할 수 있죠.

 

다시 DeviceActivityMonitor 확장으로 돌아와 관리 설정 모듈에 대해 임포트를 하여 앱 보호 제한에 엑세스할 수 있도록 구성합니다.

 

import DeviceActivity
import ManagedSettings

class MyMonitor: DeviceActivityMonitor {
    let store = ManagedSettingsStore()

    override func intervalDidStart(for activity: DeviceActivityName) {
        super.intervalDidStart(for: activity)

        let model = MyModel()
        let applicationos = model.selectionToDiscourage.applications

        store.shield.applications = applications.isEmpty ? nil : applications
    }

    override func intervalDidEnd(for activity: DeviceActivityName) {
        super.intervalDidEnd(for: activity)

        store.shield.applications = nil
    }
}

 

이제 intervalDidStart 메서드에서 앱 모델에서 선택된 항목을 꺼내와 그에 따라서 앱 보호 제한을 구성할 수 있어요.

intervalDidEnd 메서드에서는 이 설정을 nil로 변경하여 제한을 제거할 수도 있습니다.

 

이렇게 간단히 수정을 거쳐 앱이 매일 자정부터 자정까지 보호자가 선택한 권장하지 않는 앱을 보호해줍니다.

이러한 설정들뿐 아니라 앱은 계정 생성/삭제를 방지할 수도 있고 앱을 완전히 차단하거나 연령별로 미디어 컨텐츠를 거부하는 선택등 다양한 기능들이 존재합니다.

관리 설정에서 미디어 제한 사항을 사용할 수 있죠.

 

지금까지는 해당 앱에서 매일 자정에 제한이 설정되어 있었는데, 앱이나 웹 사용량에 따라 제한을 변경하고 싶을수도 있습니다 🙋🏻

예를들어, 위 숙제 앱에서 자녀가 숙제 일정이 끝났을때 보상으로 다른 제한된 앱들을 사용할 수 있게 보호 제한을 제거하고 싶을수도 있죠.

 

import DeviceActivity

extension DeviceActivityName {
    static let daily = Self("daily")
}

extension DeviceActivityEvent.Name {
    static let encouraged = Self("encouraged")
}

let schedule = DeviceActivitySchedule(
    intervalStart: DateComponents(hour: 0, minute: 0),
    intervalEnd: DateComponents(hour: 23, minute: 59),
    repeat: true
)

let model = MyModel()
let events = [DeviceActivityEvent.Name: DeviceActivityEvent] = [
    .encouraged: DeviceActivityEvent(
        applications: model.selectionToEncourage.applicationTokens
        threshold: DataComponents(minute: minutes)
    )
]

let center = DeviceActivityCenter()
try center.startMonitoring(.daily, during: schedule, events: events)

 

우선 기존 코드로 돌아와서 encouraged라는 이벤트 이름을 정의해봅니다.

즉, 이제 이 이벤트를 참조할 수 있겠죠.

그 후, 보호자가 선택한 권장하는 앱과 원하는 사용 임계값을 포함하도록 이벤트를 정의합니다.

마지막으로, startMonitoring 시 해당 이벤트를 포함하여 호출해줍니다.

 

import DeviceActivity
import ManagedSettings

class MyMonitor: DeviceActivityMonitor {
    let store = ManagedSettingStore()
    ...

    override func eventDidReachThreshold(_ event: DeviceActivityEvent.Name, activity: DeviceActivityName) {
        super.eventDidReachThreshold(event, activity: activity)

        store.shield.applications = nil
    }
}

 

이제 모니터 클래스로 돌아와서 eventDidReachThreshold 메서드를 재정의 해줍니다.

즉, 사용량이 임계값을 충족했다면 보호 제한을 nil로 설정하여 보호를 제거해줄 수 있습니다.

 

이렇게 기본적으로 구현을 해줄 수 있어요!


shield 커스터마이징

그런데, 지금까지 앱 보호 디자인은 기본적으로 제공되는 디자인이였다면, 이걸 자유롭게 구성해줄 수도 있습니다.

즉, 커스터마이징이 가능하죠!

 

 

요런식으로 아주 이쁘게 구현해줄수도 있습니다.

또한, 디자인뿐 아니라 버튼들에 대한 컴플리션 핸들러도 직접 다른 기능을 넣어줄 수 있죠.

 

 

커스터마이징을 하려면 관리 설정에 정의된 두 개의 새로운 익스텐션을 구현해야 합니다.

 

한 포인트는 Material, Title, Icon, Body, Button들의 모양을 사용자화 해줄 수 있습니다.

import ManagedSettings
import ManagedSettingsUI

class MyShieldConfiguration: ShieldConfigurationProvider {
    override func configuration(for application: Application) -> ShieldConfigutaion {
        return ShieldConfiguration(
           backgroundEffect: ...
           backgroundColor: ...
           icon: ...
           title: ShieldConfiguration.Label(
               text: ...
               color: ...
           ),
           subtitle: ShieldConfiguration.Label(
               text: ...
               color: ...
           )
       )
   }
}

 

ShieldConfigurationProvider를 상속받아 이용하는것이죠.

 

다른 포인트로는 사용자 정의 버튼 핸들러를 만들어 원하는 기능을 동작하게 할 수 있습니다.

import ManagedSettings

class MyShieldActionExtension: ShieldActionHandler {
    override func handle(
        action: ShieldAction,
        for application: Application,
        completionHandler: @escaping (ShieldActionResponse) -> Void
    ) {
        switch action {
        case .primaryButtonPressed:
            completionHandler(.defer)
        case .secondaryButtonPressed:
            completionHandler(.close)
        @unknown default:
            fatalError()
        }
    }
}

 

ShieldActionHandler를 상속받아 구현해줄 수 있습니다.

 

즉, 기본적으로 보여줄 뼈대 자체는 변경할 수 없지만, 이렇게 디자인과 기능을 자유롭게 커스터마이징 해줄 수 있습니다.


마무리

이렇게 Screen Time API를 어떻게 사용하고 어떤 용도로 사용되는지 WWDC를 통해 알아봤습니다!

WWDC22에서는 좀 더 보완되었다고 하는데 그건 다음 포스팅으로 알아보려 합니다 😃

 


레퍼런스

 

Meet the Screen Time API - WWDC21 - Videos - Apple Developer

Explore the Screen Time API and learn how you can build apps that support customized parental controls — all while putting privacy first...

developer.apple.com