I’m making an attempt to make use of the familycontrols module to restrict display screen time of apps. The limiter works accurately however I need to have the ability to see the names of the apps which might be restricted in my Flutter app. I’ve tried all the pieces and I can not determine easy methods to get the bundle ids of those apps.
I do know that that is potential as a result of there are app blockers that do that.
for (index, token) in functions.applicationTokens.enumerated() {
NSLog("Debug: check 1: (functions)")
NSLog("Debug: check 2: (kind(of: functions.applicationTokens.enumerated()))")
NSLog("Debug: index: (index)")
NSLog("Debug: token: (token)")
}
This code offers the next output:
Debug: check 1: FamilyActivitySelection(includeEntireCategory: false, applicationTokens: Set([ApplicationToken(data: 128 bytes: `D^R’įÅyñxnÆøH4sr;Øý³¶ï(DA^_ÒÖÜÄ~
Å^A¨u^O^ÎÕ^WÆG.aBÍEiÿ_n!jÜj¾U^Hi¼ý^[,H2v7cî·(o[Þò¤¤Þ^[ã/^Ní;ÿÙL^P=Ý ^]®+>¹·Á)]), categoryTokens: Set([]), webDomainTokens: Set([]), untokenizedApplicationIdentifiers: Set([]), untokenizedCategoryIdentifiers: Set([]), untokenizedWebDomainIdentifiers: Set([]))
Debug: check 2: EnumeratedSequence
Debug: index: 0
Debug: token: ApplicationToken(information: 128 bytes)
Right here is a few of my code (I added lots as a result of I don’t know what is going on):
DeviceActivityMonitorExtension.swift:
typealias ApplicationToken = ManagedSettings.Token
typealias ActivityCategoryToken = ManagedSettings.Token
class DeviceActivityMonitorExtension: DeviceActivityMonitor {
override func intervalDidStart(for exercise: DeviceActivityName) {
tremendous.intervalDidStart(for: exercise)
}
override func intervalDidEnd(for exercise: DeviceActivityName) {
tremendous.intervalDidEnd(for: exercise)
}
override func eventDidReachThreshold(_ occasion: DeviceActivityEvent.Title, exercise: DeviceActivityName) {
tremendous.eventDidReachThreshold(occasion, exercise: exercise)
NSLog("Threshold reached for occasion: (occasion.rawValue)")
// Retrieve saved tokens from shared UserDefaults
guard let defaults = UserDefaults(suiteName: appGroupID) else {
NSLog("No app group defaults accessible.")
return
}
let decoder = JSONDecoder()
var applicationTokens: [ApplicationToken] = []
var categoryTokens: [ActivityCategoryToken] = []
// Decode utility tokens from the brand new key
if let appTokensData = defaults.information(forKey: Constants.limitedApplicationTokensKey) {
if let tokens = strive? decoder.decode([ApplicationToken].self, from: appTokensData) {
applicationTokens = tokens
NSLog("Decoded (tokens.depend) utility tokens.")
} else {
NSLog("Did not decode utility tokens.")
}
} else {
NSLog("No utility tokens present in UserDefaults.")
}
// Decode class tokens if accessible
if let catTokensData = defaults.information(forKey: Constants.limitedCategoryIdentifiersKey) {
if let cats = strive? decoder.decode([ActivityCategoryToken].self, from: catTokensData) {
categoryTokens = cats
NSLog("Decoded (cats.depend) class tokens.")
} else {
NSLog("Did not decode class tokens.")
}
} else {
NSLog("No class tokens present in UserDefaults.")
}
// Apply restrictions now that the brink is reached
let retailer = ManagedSettingsStore()
// If we've no utility tokens, set nil; in any other case, set them as a Set
retailer.defend.functions = applicationTokens.isEmpty ? nil : Set(applicationTokens)
// For classes, we use a particular coverage if we've any
retailer.defend.applicationCategories = categoryTokens.isEmpty
? nil
: ShieldSettings.ActivityCategoryPolicy.particular(Set(categoryTokens))
NSLog("Restrictions utilized. The chosen apps and classes ought to now present the Restricted display screen.")
}
override func intervalWillStartWarning(for exercise: DeviceActivityName) {
tremendous.intervalWillStartWarning(for: exercise)
}
override func intervalWillEndWarning(for exercise: DeviceActivityName) {
tremendous.intervalWillEndWarning(for: exercise)
}
override func eventWillReachThresholdWarning(_ occasion: DeviceActivityEvent.Title, exercise: DeviceActivityName) {
tremendous.eventWillReachThresholdWarning(occasion, exercise: exercise)
}
}
MyModel:
import Basis
import FamilyControls
import ManagedSettings
personal let _MyModel = MyModel()
class MyModel: ObservableObject {
let retailer = ManagedSettingsStore()
@Printed var selectionToDiscourage: FamilyActivitySelection
@Printed var selectionToEncourage: FamilyActivitySelection
@Printed var selectionToLimit: FamilyActivitySelection
init() {
selectionToDiscourage = FamilyActivitySelection()
selectionToEncourage = FamilyActivitySelection()
selectionToLimit = FamilyActivitySelection()
}
class var shared: MyModel {
return _MyModel
}
func setShieldRestrictions() {
NSLog("Debug: Setting defend restrictions for discouraged apps: (selectionToDiscourage.applicationTokens)")
let functions = self.selectionToDiscourage
retailer.defend.functions = functions.applicationTokens.isEmpty ? nil : functions.applicationTokens
retailer.defend.applicationCategories = functions.categoryTokens.isEmpty
? nil
: ShieldSettings.ActivityCategoryPolicy.particular(functions.categoryTokens)
}
// Used to encode codable to UserDefaults
personal let encoder = PropertyListEncoder()
// Used to decode codable from UserDefaults
personal let decoder = PropertyListDecoder()
func saveFamilyActivitySelection(choice: FamilyActivitySelection) {
NSLog("Debug: chosen app up to date: ", choice.applicationTokens.depend," class: ", choice.categoryTokens.depend)
let defaults = UserDefaults.commonplace
defaults.set(
strive? encoder.encode(choice),
forKey: "limitedApplicationTokens"
)
//test is information saved to consumer defaults
getSavedFamilyActivitySelection()
}
//get saved household exercise choice from UserDefault
func getSavedFamilyActivitySelection() -> FamilyActivitySelection? {
let defaults = UserDefaults.commonplace
guard let information = defaults.information(forKey: "limitedApplicationTokens") else {
return nil
}
var selectedApp: FamilyActivitySelection?
let decoder = PropertyListDecoder()
selectedApp = strive? decoder.decode(FamilyActivitySelection.self, from: information)
NSLog("Debug: saved chosen app up to date: ", selectedApp?.categoryTokens.depend ?? "0")
return selectedApp
}
}
AppDelegate.swift:
func clearLimitedAppsData() {
guard let defaults = UserDefaults(suiteName: Constants.appGroupID) else { return }
defaults.removeObject(forKey: Constants.limitedApplicationBundleIdentifiersKey)
defaults.synchronize()
NSLog("Debug: Cleared limitedApplicationBundleIdentifiersKey information.")
}
var globalMethodCall = ""
@essential
@objc class AppDelegate: FlutterAppDelegate {
override func utility(
_ utility: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// clearLimitedAppsData()
guard let controller = window?.rootViewController as? FlutterViewController else {
return tremendous.utility(utility, didFinishLaunchingWithOptions: launchOptions)
}
let METHOD_CHANNEL_NAME = "flutter_screentime"
let mannequin = MyModel.shared
let retailer = ManagedSettingsStore()
let methodChannel = FlutterMethodChannel(title: METHOD_CHANNEL_NAME, binaryMessenger: controller.binaryMessenger)
methodChannel.setMethodCallHandler { (name: FlutterMethodCall, consequence: @escaping FlutterResult) in
Activity {
// Request authorization for Household Controls if accessible
if #accessible(iOS 16.0, *) {
do {
strive await AuthorizationCenter.shared.requestAuthorization(for: .particular person)
NSLog("Debug: Authorization request succeeded.")
} catch {
NSLog("Debug: Authorization request failed with error: (error)")
consequence(FlutterError(code: "AUTH_ERROR", message: "Authorization failed", particulars: nil))
return
}
} else {
NSLog("Debug: iOS model is under 16.0, can't proceed.")
consequence(FlutterError(code: "iOS_VERSION_ERROR", message: "iOS 16.0 or newer is required", particulars: nil))
return
}
swap name.methodology {
case "blockApp":
globalMethodCall = "selectAppsToDiscourage"
NSLog("Debug: Presenting blockApp picker.")
let vc = UIHostingController(rootView: ContentView()
.environmentObject(mannequin)
.environmentObject(retailer))
controller.current(vc, animated: true, completion: nil)
consequence("blockApp invoked")
case "unblockApp":
globalMethodCall = "selectAppsToEncourage"
NSLog("Debug: Presenting unblockApp picker.")
let vc = UIHostingController(rootView: ContentView()
.environmentObject(mannequin)
.environmentObject(retailer))
controller.current(vc, animated: true, completion: nil)
consequence("unblockApp invoked")
case "limitApp":
globalMethodCall = "selectAppsToLimit"
NSLog("Debug: Presenting limitApp picker.")
let vc = UIHostingController(rootView: ContentView()
.environmentObject(mannequin)
.environmentObject(retailer))
controller.current(vc, animated: true, completion: nil)
consequence("limitApp invoked")
case "getLimitedApps":
if #accessible(iOS 16.0, *) {
let limitedApps = self.decodeLimitedApps()
NSLog("Debug: Returning restricted apps: (limitedApps)")
consequence(limitedApps)
} else {
NSLog("Debug: iOS < 16.0, can't get restricted apps.")
consequence(["No limited apps or iOS < 16.0"])
}
default:
NSLog("Debug: Unhandled methodology name: (name.methodology)")
consequence(FlutterMethodNotImplemented)
}
}
}
GeneratedPluginRegistrant.register(with: self)
return tremendous.utility(utility, didFinishLaunchingWithOptions: launchOptions)
}
@accessible(iOS 16.0, *)
func decodeLimitedApps() -> [String] {
guard let defaults = UserDefaults(suiteName: Constants.appGroupID) else {
NSLog("Debug: No app group defaults accessible.")
return ["No limited apps found."]
}
let decoder = JSONDecoder()
var limitedApps: [String] = []
NSLog("Debug: Trying to learn restricted apps from UserDefaults.")
if let appBundleIDsData = defaults.information(forKey: Constants.limitedApplicationBundleIdentifiersKey) {
NSLog("Debug: Discovered app bundle IDs information: (appBundleIDsData.depend) bytes")
// Decode the info as [String]
do {
let bundleIDs = strive decoder.decode([String].self, from: appBundleIDsData)
if !bundleIDs.isEmpty {
NSLog("Debug: Decoded bundle IDs: (bundleIDs)")
limitedApps = bundleIDs
for bundleID in bundleIDs {
NSLog("Bundle Identifier: (bundleID)")
}
} else {
NSLog("Debug: Decoded bundle IDs array is empty.")
}
} catch {
NSLog("Debug: Did not decode bundle IDs: (error.localizedDescription)")
// Optionally, log the uncooked information as a string for inspection
if let rawString = String(information: appBundleIDsData, encoding: .utf8) {
NSLog("Debug: Uncooked appBundleIDsData as String: (rawString)")
}
}
} else {
NSLog("Debug: No app bundle IDs information present in UserDefaults.")
}
if limitedApps.isEmpty {
return ["No limited apps found!"]
}
return limitedApps
}
func storeLimitedApps(bundleIDs: [String], categoryIDs: [String]) {
guard let defaults = UserDefaults(suiteName: Constants.appGroupID) else {
NSLog("Debug: Did not entry UserDefaults with App Group ID.")
return
}
let encoder = JSONEncoder()
do {
let appData = strive encoder.encode(bundleIDs)
defaults.set(appData, forKey: Constants.limitedApplicationBundleIdentifiersKey)
NSLog("Debug: Saved (bundleIDs.depend) utility bundle identifiers: (bundleIDs)")
} catch {
NSLog("Debug: Did not encode utility bundle identifiers: (error.localizedDescription)")
}
// Equally deal with categoryIDs if vital
do {
let catData = strive encoder.encode(categoryIDs)
defaults.set(catData, forKey: Constants.limitedCategoryIdentifiersKey)
NSLog("Debug: Saved (categoryIDs.depend) class identifiers.")
} catch {
NSLog("Debug: Did not encode class identifiers: (error.localizedDescription)")
}
defaults.synchronize()
}
}