Screen Time API

Screen Time API is a proposed cross platform API to allow third-party developers to provide apps that can monitor and control the time spent using the device. It aims to provide a generic API that can be used for a wide range of use cases, from personal health to remote parental controls to social media monitoring. It also aims to do this in a way that is respectful of the device owners privacy, by not providing more information than is nessessary and using the platforms permissions system to access data. This document shows how the API would look for iOS, MacOS and tvOS.

In its most minimal form, the API only gives access to App and Device usage data, but no App data is exposed. So the Screen Time API app knows that you used Safari for 10 mins at 11am, but has no idea what you did in Safari. With more permissions, the Screen Time API app is able to stop you from using Safari at this point.

The Screen Time API makes available existing networking APIs for monitoring network activity on a device. These are powerful APIs for monitoring all network connections from the device. These could be used by apps for basic content filtering as well as for more advanced features such as monitoring and blocking social networking apps. This would also allow privacy apps to monitor and block trackers in all the apps on a device. These APIs work for all web browsers and apps.

Technically, the API provides events to an app, (e.g. that an app is being opened or a notification is about to be shown) and allows that app to allow or block this action. That app can also present its own view to interupt what the user is doing in another app.

The diagram below provides an overview of how an app would use the API, showing the interaction between the app and the system.

Flow

API

STKManager

class STKManager {
        static default: STKManager
    }

This would be the main class for interacting with Screen Time API on the device, accessed as a static singleton from the app or extension.

Register

func register(eventTypes: [.openApp, .closeApp])

Register this app as the/a Screen time manager on the device. This would trigger the user to setup a PIN code, for parental controls, and enable all the apis/extension.

The app can no longer be deleted, and this permission cannot be changed without providing the parental control PIN.

Deregister

func deregister()

Stop the extension from receving events, also the app can be deleted normally.

Authorization Status

func authorizationStatus() -> STKAuthorizationStatus

Get the apps authorization status, similar to CLLocationManager, there are two levels of access, to allow the app to only access tracking information, or to allow the app to control the device.

The functions to present app UI at anytime, and prevent apps from loading are not available when the user chooses tracking only.

enum STKAuthorizationStatus {
      case notDetermined
      case restricted
      case denied
      case authorizedAccess
      case authorizedControl
    }

Apps

func apps() -> [App]

Get a list of apps that are currently installed on the device.

struct App {
        name: String
        itunesStoreId: String
        bundleIdentifier: String
        version: String
        icon: UIImage,
        rating: Int
    }

Is Child

func isChild() -> Bool

Find out if the apple id on the device is a child account.

Restrictions

var restrictions: Restrictions

Property for managing restrictions on the device, just like MDM provided or in Restrictions settings on the device.

struct Restrictions {
        allowAppInstallation: Bool
        allowCamera: Bool
        allowChat: Bool
        allowCloudDocumentSync: Bool
        allowFindMyDevice: Bool
        allowFindMyFriends: Bool
        allowGameCenter: Bool
        allowHome: Bool
        allowNews: Bool
        allowPairedWatch: Bool
        allowPodcasts: Bool
        allowSafari: Bool
        allowUIAppInstallation: Bool
        allowVideoConferencing: Bool
        allowiTunes: Bool
        maximumMovieRatingForAgeGate: Double
        maximumTVShowRating: Double
        maximumTVShowRatingForAgeGate: Double
        maximumMovieRating: Double
    }

Query Events

func queryEvents(from: Date, to: Date, eventTypes: [.openApp, .closeApp]) -> [Event]

Allows access to the history of events.

Send Message

func sendMessage(_ messageData: Data,
                     responseHandler: ((Data?) -> Void)? = nil) throws

Send a message to the Screen Time extension. This can be used for comunication between the host app and the extension.

Delegate

var delegate: STKManagerDelegate?

Assign a delegate to the manager.

protocol STKManagerDelegate {
    }
 optional func screenTimeManager(_ manager: STKManager,
           didChangeAuthorization status: STKAuthorizationStatus)

Get updates on the STKAuthorizationStatus, will be called if the user disables the screen time app. The app can be launched in the background to enable handling of this event.

Extension

The next part would be an app extension. The main class of the extension should extend from this class, and override the methods needed.

class ScreenTimeEventsHandler {
    }

Before Event

func before(event: Event, response: (EventResponse) -> Void)

This would be the extension function to implement, receive event and provide a response, to allow or block the event from proceeding.

E.g. with notifications, the function is called first, and if the extension returns .block then iOS will not show the notification.

The list of events could be expanded futher to include other OS events that are relevent. E.g. for desktop platforms events for file downloads or user login would be for interest.

struct Event {
      eventType: EventType
      identifier: Int
      at: Date
    }

    enum EventType {
      case openApp(App)
      case closeApp(App)
      case installApp(App)
      case uninstallApp(App)
      case lockScreen
      case unlockScreen
      case devicePickUp
      case notification(App, userInfo: [AnyHashable : Any])
      case makePhoneCall(Contact)
      case receivePhoneCall(Contact)
      case sendMessage(Contact, Message)
      case receiveMessage(Contact, Message)
    }

    enum EventResponse {
      case allow
      case block
    }

After Event

func after(event: Event)

Called after the event has finished happening.

Present View

func present(_ viewControllerToPresent: UIViewController,
        animated flag: Bool,
      completion: (() -> Void)? = nil)

Similar to in UIViewController to allow the extension to present a view controller at any time.

The view controller would yield to the system via the extensionContext.

This can be called during the before or after events, or in response to some other app specific logic, e.g. a time limit has been reached.

App Message

func handleAppMessage(_ messageData: Data,
        completionHandler: ((Data?) -> Void)? = nil)

Handle messages sent by the extensions containing app. Similar to NETunnelProvider.

Changes

UIBackgroundModes

A new UIBackgroundMode is added, screentime, to allow an app to always receive background location updates.

The app will still ask for location permissions in the normal way, but if the app gets 'always' permission, even if the user quits the app it will be relaunched. This allows a parental control app to reliably receive location updates when the child might try to prevent it from doing so.

An app with this mode will also always receive remote notifications in the background, again even if the user has quit the app. This means a parental control app will be able to use push notifications to delivery configuration changes to the extension.

Content Filter Providers

Currently Content Filter Providers are only supported on supervised iOS devices. This is changed to allow Screen Time API apps to create content filters. More data is exposed to content filter apps, in a way that Apple has designed and already allows schools and businesses to use. This level allows complete web data. This API is more powerful that the currently available Safari Content Blocker because it allows apps to filter for all web browsers and apps and make intelligent decisions about content not just provide basic block lists.

The extension can be enabled using NEVPNManager and iOS should ask for permissions from the user to enable it. Similar to enabling a screen time app, the parental control PIN code should be provided/created in order to enable it.

A function should be added to NEVPNManager to allow the app to get its current authorization status.

func authorizationStatus() -> NEAuthorizationStatus

DNS Proxy Provider

Similarly, DNS Proxy Providers are only supported on supervised iOS devices. This is changed to allow Screen Time API apps to create dns proxy providers in exactly the same way as content filter providers.

DNS Proxy Providers are a less intrusive way to monitor and block network activity, as they dont get access to the content of networking, just the domain names being accessed. This would be appriate for apps that dont need full access to the network.