I've followed along with the Screen Time API demos (https://developer.apple.com/videos/play/wwdc2021/10123/)
Also followed along with this guide, which is essentially the same:
https://www.folio3.com/mobile/blog/screentime-api-ios/
I'm able to restrict access to apps/categories with the FamilyActivityPicker and FamilyActivitySelection.
I can set a DeviceActivitySchedule, and then use DeviceActivityCenter to start monitoring it.
I can tell that the schedule is working, because MyMonitor:intervalDidStart() does get called, and it works except for the restricting of apps/categories/webCategories.
It's as if MyModel does not have any values set from within MyMonitor. I've confirmed that MyModel does have the correct FamilyActivitySelection apps etc, everywhere else in my Target, before and after the MyMonitor:intervalDidStart() gets called.
MyMonitor is in a separate target called MonitorExtension, that I created using the Device Activity Monitor Extension template. But I made sure to set the Target Membership of MyModel to both my main target, and my extension target.
I have set NSExtensionPrincipalClass to $(PRODUCT_MODULE_NAME).MyMonitor, as suggested.
I have added MyModel.swift to the Compiled Sources in my extensions Build Phases.
I have edited my apps build scheme, to make sure the extension target is also built.
One more interesting thing is that debugger breakpoints and print statements do not work from within my extension. I've even tried caching a string from within MyMonitor:intervalDidStart, and tried to retrieve it afterwards in my main target, but it is nil.
Still, I've confirmed that intervalDidStart was actually called by adding any removing store.application.denyAppInstallation = true, and having it work correctly.
I've spent so much time on this problem, any help would be massive..
Here are the files I've referenced:
import UIKit
import MobileCoreServices
import ManagedSettings
import DeviceActivity
class MyMonitor: DeviceActivityMonitor {
let store = ManagedSettingsStore()
override func intervalDidStart(for activity: DeviceActivityName) {
super.intervalDidStart(for: activity)
let model = MyModel.shared
let applications = model.selectionToDiscourage.applicationTokens
let categories = model.selectionToDiscourage.categoryTokens
let webCategories = model.selectionToDiscourage.webDomainTokens
if applications.isEmpty {
print("No applications to restrict")
} else {
store.shield.applications = applications
}
if categories.isEmpty {
print("No categories to restrict")
} else {
store.shield.applicationCategories = ShieldSettings.ActivityCategoryPolicy.specific(categories, except: Set())
}
if webCategories.isEmpty {
print("No web categories to restrict")
} else {
store.shield.webDomains = webCategories
}
store.dateAndTime.requireAutomaticDateAndTime = true
store.account.lockAccounts = true
store.passcode.lockPasscode = true
store.siri.denySiri = true
store.appStore.denyInAppPurchases = true
store.appStore.maximumRating = 200
store.appStore.requirePasswordForPurchases = true
store.media.denyExplicitContent = true
store.gameCenter.denyMultiplayerGaming = true
store.media.denyMusicService = true
store.application.denyAppInstallation = true
}
override func intervalDidEnd(for activity: DeviceActivityName) {
super.intervalDidEnd(for: activity)
store.shield.applications = nil
store.shield.applicationCategories = nil
store.shield.webDomains = nil
store.dateAndTime.requireAutomaticDateAndTime = false
store.account.lockAccounts = false
store.passcode.lockPasscode = false
store.siri.denySiri = false
store.appStore.denyInAppPurchases = false
store.appStore.maximumRating = 1000
store.appStore.requirePasswordForPurchases = false
store.media.denyExplicitContent = false
store.gameCenter.denyMultiplayerGaming = false
store.media.denyMusicService = false
store.application.denyAppInstallation = false
}
}
import Foundation
import FamilyControls
import DeviceActivity
import ManagedSettings
class MyModel: ObservableObject {
static let shared = MyModel()
let store = ManagedSettingsStore()
private init() {}
var selectionToDiscourage = FamilyActivitySelection() {
willSet {
let applications = newValue.applicationTokens
let categories = newValue.categoryTokens
let webCategories = newValue.webDomainTokens
store.shield.applications = applications.isEmpty ? nil : applications
store.shield.applicationCategories = ShieldSettings.ActivityCategoryPolicy.specific(categories, except: Set())
store.shield.webDomains = webCategories
}
}
func initiateMonitoring(scheduleStart: DateComponents, scheduleEnd: DateComponents) {
let schedule = DeviceActivitySchedule(intervalStart: scheduleStart, intervalEnd: scheduleEnd, repeats: true, warningTime: nil)
print(scheduleStart)
print(scheduleEnd)
let center = DeviceActivityCenter()
do {
try center.startMonitoring(.daily, during: schedule)
}
catch {
print ("Could not start monitoring \(error)")
}
store.dateAndTime.requireAutomaticDateAndTime = false
store.account.lockAccounts = false
store.passcode.lockPasscode = false
store.siri.denySiri = false
store.appStore.denyInAppPurchases = false
store.appStore.maximumRating = 1000
store.appStore.requirePasswordForPurchases = false
store.media.denyExplicitContent = false
store.gameCenter.denyMultiplayerGaming = false
store.media.denyMusicService = false
store.application.denyAppInstallation = false
}
}
extension DeviceActivityName {
static let daily = Self("daily")
}
import SwiftUI
import FamilyControls
struct AppPicker: View {
@StateObject var model = MyModel.shared
@State var isPresented = false
var body: some View {
Button("Select Apps to Discourage") {
isPresented = true
}
.familyActivityPicker(isPresented: $isPresented, selection: $model.selectionToDiscourage)
}
}
Post
Replies
Boosts
Views
Activity
Within my app, I'm using the FamilyActivityPicker.
Currently, I'm able to use my app to block itself... I don't want this to be an option.
Is there a way around this issue?
I was thinking I could convert my App into in ApplicationToken, and compare it to the set of ApplicationTokens that are to be blocked, and then filter it from that. Doesn't seem like that works though.
I have a class that uses DeviceActivityMonitor as a subclass:
// MyMonitor.swift
import DeviceActivity
@objc(MyMonitor)
class MyMonitor: DeviceActivityMonitor {
...
}
In order to use SwiftUI in Objective-C, I'm importing this in my objective-c file #import "MyProject-Swift.h".
But, when I try to build, I get the error Module 'DeviceActivity' not found from wthin #import "MyProject-Swift.h"
I've tried "Link Binary with Libraries":
I've also tried to turn on Define Modules in Build Settings -> Packaging.
It just seems as though the DeviceActivity framework doesn't have a header file. Seems like it should?
When I click "Show in Finder" in MyProject -> Frameworks -> DeviceAcitivity.framework, there is not DeviceActivity.h file in that dir (also not in the modules folder in my screenshot):
Is there a way to create a header file for an apple framework? Or force the creation of one?
I have also tried cleaning the build folder
Here's the framework for reference: https://developer.apple.com/documentation/DeviceActivity?changes=latest_major
Let me know if there's anything else I can describe to help. I'm new to this!