Post not yet marked as solved
We are making an appstore app to be opened in single app kiosk mode(App Lock Policy for a single app) .
When tried to open and login , a popup which is seen when opened without kiosk mode is not opening up.
Attached the screenshot of the popup screen. (not able to attach the video here)
Raised Feedback id - FB13304240
AppLock Policy Payload sent to the device :
<dict>
<key>PayloadVersion</key>
<integer>1</integer>
<key>PayloadUUID</key>
<string></string>
<key>PayloadType</key>
<string>Configuration</string>
<key>PayloadOrganization</key>
<string>fhd</string>
<key>PayloadIdentifier</key>
<string>sample_id</string>
<key>PayloadDisplayName</key>
<string>Kiosk Zenoti</string>
<key>PayloadRemovalDisallowed</key>
<true/>
<key>PayloadContent</key>
<array>
<dict>
<key>PayloadVersion</key>
<integer>1</integer>
<key>PayloadUUID</key>
<string>ad18a938-211e-4670-9be6-6f43162b6290</string>
<key>PayloadType</key>
<string>com.apple.app.lock</string>
<key>PayloadOrganization</key>
<string>MDM</string>
<key>PayloadIdentifier</key>
<string>a � �d18a938-211e-4670-9be6-6f43162b6290</string>
<key>PayloadDisplayName</key>
<string>AppLock Policy</string>
<key>App</key>
<dict>
<key>Options</key>
<dict>
<key>DisableTouch</key>
<false/>
<key>DisableDeviceRotation</key>
<false/>
<key>DisableVolumeButtons</key>
<false/>
<key>DisableRingerSwitch</key>
<false/>
<key>DisableSleepWakeButton</key>
<false/>
<key>DisableAutoLock</key>
<true/>
<key>EnableVoiceOver</key>
<false/>
<key>EnableZoom</key>
<false/>
<key>EnableInvertColors</key>
<false/>
<key>EnableAssistiveTouch</key>
<false/>
<key>EnableSpeakSelection</key>
<false/>
<key>EnableMonoAudio</key>
<false/>
<key>EnableVoiceControl</key>
<false/>
</dict>
<key>UserEnabledOptions</key>
<dict>
<key>VoiceOver</key>
<false/>
<key>Zoom</key>
<false/>
<key>InvertColors</ke � y>
<false/>
<key>AssistiveTouch</key>
<false/>
</dict>
<key>Identifier</key>
<string>com.zenoti.mpos</string>
</dict>
<key>Identifier</key>
<string>com.zenoti.mpos</string>
</dict>
</array>
</dict>
</plist>
Post not yet marked as solved
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 not yet marked as solved
ShieldActionDelegate called .secondaryButtonPressed with secondaryButtonLabel == nil.
Step to reproduce:
Make configure ShieldConfiguration with primaryButtonLabel configure only.
Add some method (in my case I add unblock all) in ShieldActionDelegate by case .secondaryButtonPressed
On Shield make a few taps on secondary button area see attached image
handle(action: ShieldAction, for application: ApplicationToken ... for case .secondaryButtonPressed called - it's wrong, I don't have second button configured, but action is called.
Added feedback: 13254523
Post not yet marked as solved
We are developing an application that utilizes the Screen Time API. As outlined in Apple's documentation on family controls(https://developer.apple.com/documentation/familycontrols/), we are obtaining authorization as follows:
Task(priority: .high) {
do {
try await AuthorizationCenter.shared.requestAuthorization(for: .child)
NSLog("Authorization State ~ AUTHORIZED")
}catch let error {
NSLog("Authorization ERROR: \(error.localizedDescription)")
}
}
As mentioned in the documentation, once the app is authorized for family controls, the user should not be able to delete the app without giving the parent iCloud credentials. When deleting the app, the prompt for entering the parent iCloud credentials is shown sometimes and in some unknown cases, the prompt is not shown and the app is deleted without requiring the parent’s iCloud account.
Post not yet marked as solved
Hello,
I am curious if someone else also noticed this. We have started getting reports about our app not working correctly (mostly related to the Device Activity monitor that "runs" in the background, https://developer.apple.com/documentation/deviceactivity/deviceactivitymonitor).
Upon checking the Xcode Organizer I can see somewhat significant amount of crashes that mostly appear to happen on iOS 16.6 - it is possible that our users don't wait with updating iOS but still looks suspicious.
The crashes are related to ManagedSettings calls outside our own code.
We haven't changes this code in a while so this coupled with the fact that these crashes happen "deep" in the ManagedSettings framework leads me to believe there is some other issue.
Post not yet marked as solved
If a user has Screen Time limits set up for certain apps, will that interfere with my app that allows users to restrict app usage using the Screen Time API?
I've had some users experiencing this issue (they have Screen Time limits set up for apps, and my app isn't able to restrict those and/or other apps), but I haven't been able to find a reliable pattern yet. Has anyone else encountered this?
Post not yet marked as solved
Is there any way to show the user system apps in the FamilyActivityPicker? Specifically, I am asking about Safari.
I am working on an app that allows users to block other apps, and I am trying to figure out whether I can make this functionality available for Safari.
Post not yet marked as solved
Hello!
I'm new to iOS development and am developing an app that blocks certain websites. At the moment, I'm thinking of using the Network Extension capability to do the job. From what I have read, in the production version of the app, you'd need to make use of MDM profiles since NE filtering only works on supervised devices. So, I'm here to ask the community if there are better options than using this method.
As far as screen time api is concerned, I believe it requires the user to specify which websites they want blocked by themselves using the activity picker so that doesn't quite work for me since i want to allow the app to block groups of websites by itself based on the user's preference.
Thanks!
Post not yet marked as solved
Hi,
I'm trying to make use of the Device Activity Labels where you supply an ApplicationToken. I can successfully get it to show the icon + title of the Application (twitter in my case) but I cannot get the styling to work.
// Works
.labelStyle(.iconOnly)
.labelStyle(.titleOnly)
.border(...)
![]("https://developer.apple.com/forums/content/attachment/9660b578-a36f-4d5a-ae18-653a207aa5ab" "title=Screenshot 2023-03-12 at 12.57.34 PM.png;width=1218;height=844")
// Does NOT work
.font(.largeTitle)
.foregroundColor(.blue)
I have checked the same style (or just modifiers) against a standard Label and they actually do work in the code below.
// This is an application token. Some style not applied.
Label(targetApp)
.labelStyle(MyStyle())
// Showing the same style using a simple label. All styles correctly applied.
Label("Twitter", systemImage: "video.square.fill")
.labelStyle(MyStyle())
Is changing the font + color of the title for this Label(_ applicationToken:) supported?
Post not yet marked as solved
Hello! Rookie Swift developer here!
I am trying to make an app that uses the Screen Time API to block a list of websites. Specifically, I want it to take a list of web domains in String format and restrict them instead of using the FamilyActivityPicker. So far, I have been unsuccessful.
When using the FamilyActivityPicker, I have been able to restrict websites by modifying the shield instance variable of the ManagedSettingsStore.
let store = ManagedSettingsStore()
// Got selection from familyActivityPicker.
store.shield.webDomains = selection.webDomainTokens
"store.shield.webDomains" is of type "Set<Token>" and as far as I know, you can create a WebDomain struct with a String url but not Token. I was only able to get a token using the FamilyActivityPicker.
Hence, I was wondering if it was even possible to do this and if so, how you'd go about it. Thanks!
Post not yet marked as solved
Hello, I have a mobile game company, but the account where we uploaded the games was closed due to non-payment and we did not renew the domain of our mail. Is there any chance I can recover the account? If I recover it, will the games be deleted?
Post not yet marked as solved
Hi, has anyone been able to make to work the new geofence Item under Object CellularPrivateNetwork Object, I was able to create the mobileconfig file and install it on the a Iphone 13 with iOS 17 Beta 7 without error but looks like the geofence is not actually working at all, since the properties of the object are not reflecting at all on the actual operation of the phone, specifically CellularDataPreferred and CellularPrivateNetwork.GeofenceItem any guidance on this would be greatly appreciated, thanks!
Post not yet marked as solved
I am looking for a way to ensure that every time I close Safari on my mobile device, all open tabs are automatically closed and the browser history is cleared. This is to enhance privacy and keep the browser experience clean each time I access it. I've searched through Safari's settings but haven't found an option for this.
I'm wondering if there is a built-in feature I might have missed, or if it's possible to develop an extension or use any other method to achieve this.
Post not yet marked as solved
Our app uses Cordova with a Cordova Local Webserver plugin. This plugin uses the url http://localhost: with a randomly chosen port. Some of the devices that our app runs on are MDM configured and have Content Filters enabled which only allows a finite list of website URLs to be accessed. This configuration has always worked in the past. Starting with the release of iPadOS 16.5, our app now hangs because the content filters are now preventing access to http://localhost. We've tried adding http://localhost, plus any derivative of that URL (i.e. http://localhost:, http://localhost:*, etc.) to the Allowed Website list, but this does not help.
Wondering if anyone else has encountered this issue.
Post not yet marked as solved
I added the extension via targets. And I added code in the intervalDidStart() override to print and send a notification.
But it never seems to run. Am I supposed to do any other steps after adding the extension?
// Make sure that your class name matches the NSExtensionPrincipalClass in your Info.plist.
class DeviceActivityMonitorExtension: DeviceActivityMonitor {
func getSelectionFromUserDefaults() -> FamilyActivitySelection {
let defaults = UserDefaults.standard
if let encodedData = defaults.data(forKey: "ScreenTimeSelection") {
let decoder = PropertyListDecoder()
if let selection = try? decoder.decode(FamilyActivitySelection.self, from: encodedData) {
return selection
}
}
return FamilyActivitySelection()
}
override func intervalDidStart(for activity: DeviceActivityName) {
super.intervalDidStart(for: activity)
print("HERE!")
let content = UNMutableNotificationContent()
content.title = "Feed the cat"
content.subtitle = "It looks hungry"
content.sound = UNNotificationSound.default
// show this notification five seconds from now
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)
// choose a random identifier
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
// add our notification request
UNUserNotificationCenter.current().add(request)
let selection = getSelectionFromUserDefaults()
let applications = selection.applicationTokens
let categories = selection.categoryTokens
let webCategories = selection.webDomainTokens
let store = ManagedSettingsStore()
store.shield.applications = applications.isEmpty ? nil : applications
store.shield.applicationCategories = ShieldSettings.ActivityCategoryPolicy.specific(categories, except: Set())
store.shield.webDomainCategories = ShieldSettings.ActivityCategoryPolicy.specific(categories, except: Set())
}
Post not yet marked as solved
I'm trying to modify a ShieldConfigurationExtension, but am having trouble adding an icon. I have tried directly adding a png to the ShieldConfiguration extension folder and creating a UIImage from it, but it doesn't seem to work. Am I working with images within an extension properly?
This code displays the default configuration
override func configuration(shielding application: Application, in category: ActivityCategory) -> ShieldConfiguration {
// Customize the shield as needed for applications shielded because of their category.
let image = UIImage(named: "icon")
return ShieldConfiguration( icon: image)
}
This code properly displays a customized configuration
return ShieldConfiguration( title: ShieldConfiguration.Label(text: "test", color: .white))
Post not yet marked as solved
I'm having trouble with my DeviceActivityMonitorExtension. The intervalDidStart function is not being called when the scheduler is created. Does anyone have an idea why this is?
let schedule = DeviceActivitySchedule(
intervalStart: DateComponents(hour: 15, minute: 23),
intervalEnd: DateComponents(hour: 16, minute: 55),
repeats: true
)
class MySchedule {
static public func setSchedule() {
let center = DeviceActivityCenter()
center.stopMonitoring([.daily])
do {
try center.startMonitoring(.daily, during: schedule)
} catch {
print("Error monitoring schedule: ", error)
}
}
}
class DeviceActivityMonitorExtension: DeviceActivityMonitor {
override func intervalDidStart(for activity: DeviceActivityName) {
super.intervalDidStart(for: activity)
SelectedApps.shared.setRestrictions()
}
private let _SelectedApps = SelectedApps()
class SelectedApps: ObservableObject{
@Published var selection: FamilyActivitySelection
let store = ManagedSettingsStore()
init() {
if let savedSelection = UserDefaults.standard.object(forKey: "savedSelection") as? Data {
let decoder = JSONDecoder()
if let loadedSelection = try? decoder.decode(FamilyActivitySelection.self, from: savedSelection) {
selection = loadedSelection
} else {
selection = FamilyActivitySelection(includeEntireCategory: true)
}
} else {
selection = FamilyActivitySelection(includeEntireCategory: true)
}
}
class var shared: SelectedApps {
return _SelectedApps
}
func setRestrictions(){
let applications = selection
store.shield.applications = applications.applicationTokens.isEmpty ? nil : applications.applicationTokens
store.shield.applicationCategories = applications.categoryTokens.isEmpty
? nil
: ShieldSettings.ActivityCategoryPolicy.specific(applications.categoryTokens)
}
I am creating a custom shield UI for when an app is blocked. have an AppDelegate file where my @UIApplicationMain is run
I pasted in the ShieldConfiguration code from the WWDC21 Meet Screen Time demo inside my app:
class MyShieldConfiguration: ShieldConfigurationDataSource {
override func configuration(shielding application: Application) -> ShieldConfiguration {
return ShieldConfiguration(
title: ShieldConfiguration.Label(text: "Doxo", color: UIColor.blue),
subtitle: ShieldConfiguration.Label(text: "heyhey")
)
}
}
but after calling store.shield.applications = ....
the default restricted screen is being shown instead of the one I configured above
Where am I supposed to put MyShieldConfiguration ?
Post not yet marked as solved
I've added ShieldConfigurationExtension to my project. The only thing that doesn't work for me is proper color for System Appearance.
Let's say I only wants to change color of title for shield, I create color in Asset Catalog with different light and dark appearance then create ShieldConfiguration as shown:
ShieldConfiguration(title: ShieldConfiguration.Label(
text: "My title", color: UIColor(named: "mycolor") ?? .systemGray
))
This will make the title to be one of the variant of mycolor BUT it looks to be randomly selected. Sometimes it has light appearance, sometimes dark and not always match selected system appearance. Other UI elements that I didn't set are properly changing theirs colors.
On the other hand if I use some system colors, it works as expected:
ShieldConfiguration(title: ShieldConfiguration.Label(
text: "My title", color: .label
))
.label will always has proper color - white for dark mode and black for light
I've tried to use mycolor inside main app and there it works as it should so the problem is happening only in ShieldConfigurationExtension
I am trying to develop an application that can enforce Screen Time restrictions on the target device, however I get stuck at the part where I have scheduled callbacks for my class that implements the extension for DeviceActivityMonitor.
For debugging, I first attach to the app extension target process via Debug > Attach to Process > Process Name. But after scheduling monitoring that receives a callback immediately, the process exits with the following error:
Thread 1: EXC_RESOURCE (RESOURCE_TYPE_MEMORY: high watermark memory limit exceeded) (limit=6 MB)
Is the maximum allowed memory for Device Activity Monitor extensions really just 6 MB, or am I doing something wrong? If it is indeed the case, is there any way to increase it? 6 MB seems quite restrictive to me.