Sorry I am not sure why my contentView didn't post. Here is all of my code that I used from contentView to try and save the selection in the list using the files I already posted -
import SwiftUI
import DeviceActivity
import FamilyControls
import ManagedSettings
struct ScaleButtonStyle: ButtonStyle {
func makeBody(configuration: Configuration) -> some View {
configuration.label
.scaleEffect(configuration.isPressed ? 0.95 : 1.0)
.opacity(configuration.isPressed ? 0.8 : 1.0)
}
}
struct ContentView: View {
@Environment(\.managedObjectContext) var moc
// @FetchRequest(sortDescriptors: []) var apps: FetchedResults<AppToken>
@State private var isSelected = false
@State var isPresented = false
@EnvironmentObject var model: DataModel
@EnvironmentObject var dataController: DataController
@State private var authorizationStatus = AuthorizationCenter.shared.authorizationStatus
var body: some View {
ZStack{
Color(red: 33 / 255, green: 33 / 255, blue: 33 / 255)
.ignoresSafeArea()
VStack{
Spacer()
if dataController.savedSelection.isEmpty {
Text("No Apps Selected")
.foregroundColor(.gray)
} else {
List(dataController.savedSelection, id: \.self) { app in
Text(app.bundleIdentifier ?? "Unknown App")
}
.scrollContentBackground(.hidden)
}
let appArray = dataController.savedSelection
if !appArray.isEmpty{
Button("Delete App") {
let app = dataController.savedSelection[0]
moc.delete(app)
do {
try moc.save()
// Update savedSelection to reflect the changes
dataController.fetchApps(context: moc)
} catch {
print("Error saving after deletion: \(error)")
}
}
.padding(.top, 15)
}
Spacer()
VStack{
Button(action: {
print("Blocking Button Clicked")
isPresented = true
})
{
Text("Select Apps to Block")
.font(.headline)
.foregroundColor(.white)
.padding()
.frame(width: 300)
.background(
LinearGradient(
colors: [.blue, .purple],
startPoint: .leading,
endPoint: .trailing
)
)
.cornerRadius(20)
.shadow(color: Color.blue.opacity(0.6), radius: 10, x: 0, y: 2)
}
.familyActivityPicker(isPresented: $isPresented, selection: $model.selectionToDiscourage)
.buttonStyle(ScaleButtonStyle())
}
.onChange(of: model.selectionToDiscourage)
{
print("APPS SELECTED : \(model.selectionToDiscourage.applications.count)")
for i in model.selectionToDiscourage.applications {
print(i)
dataController.addApp(name:i.localizedDisplayName ?? "Temp", context: moc)
}
model.setShieldRestrictions()
print($model.selectionToDiscourage.applicationTokens)
}
.padding(.bottom, 15)
.padding(.top, 15)
}
.onAppear()
{
print("Screen Time Authorization Status \(authorizationStatus)")
ScheduleModel.setSchedule()
dataController.fetchApps(context: moc)
}
}
}
}
Post
Replies
Boosts
Views
Activity
Yes those steps are exactly what I am trying to achieve! I had to remove some things so you can only see what I was trying to work on but let me know if you need anything else. I also put my contentView in a second reply because I went over the word count. Thank you so much!!!
Here is all of the code I used to try and persist the data so far -
// DataController.Swift (the entity is AppToken and Attribute is bundleIdentifier set to a string in my Detoxifier.xcdatamodeld file) -
import CoreData
import Foundation
import FamilyControls
import ManagedSettings
private let _DataController = DataController()
class DataController: ObservableObject {
let container = NSPersistentContainer(name: "Detoxifier")
@Published var savedSelection: [AppToken] = []
init(){
container.loadPersistentStores { description, error in
if let error = error{
print("ERROR LOADING CORE DATA. \(error)")
}else{
print("Successfully loaded core data.")
}
}
// fetchApps()
}
func fetchApps(context: NSManagedObjectContext){
let request = NSFetchRequest<AppToken>(entityName:"AppToken")
do{
savedSelection = try context.fetch(request)
print("Successfully fetched saved apps.")
}catch let error{
print("Error fetching. \(error)")
}
}
func addApp(name: String, context: NSManagedObjectContext){
let newApp = AppToken(context: context)
newApp.bundleIdentifier = name
saveData(context: context)
}
func saveData(context: NSManagedObjectContext) {
do{
try context.save()
fetchApps(context: context)
} catch let error{
print("Error saving. \(error)")
}
}
class var shared: DataController {
return _DataController
}
}
// MyModel.Swift -
import Foundation
import FamilyControls
import ManagedSettings
private let _DataModel = DataModel()
class DataModel: ObservableObject {
let store = ManagedSettingsStore()
@Published var selectionToDiscourage: FamilyActivitySelection
init() {
selectionToDiscourage = FamilyActivitySelection()
}
class var shared: DataModel {
return _DataModel
}
func setShieldRestrictions() {
let applications = DataModel.shared.selectionToDiscourage
store.shield.applications = applications.applicationTokens.isEmpty ? nil : applications.applicationTokens
store.shield.applicationCategories = applications.categoryTokens.isEmpty
? nil
: ShieldSettings.ActivityCategoryPolicy.specific(applications.categoryTokens)
}
}
// App.Swift -
import SwiftUI
import FamilyControls
import ManagedSettings
@main
struct AppBlockerApp: App {
let center = AuthorizationCenter.shared
@StateObject var model = DataModel.shared
@StateObject var dataController = DataController.shared
@StateObject var store = ManagedSettingsStore()
@State var show = false
var body: some Scene {
WindowGroup {
ZStack {
VStack {
if show {
ContentView()
.environmentObject(model)
.environmentObject(store)
.environmentObject(dataController)
.environment(\.managedObjectContext, dataController.container.viewContext)
}else{
LoadingView()
}
}
}.onAppear {
Task{
do{
print("Making Sure Authorization is Granted...")
try await center.requestAuthorization(for:
FamilyControlsMember.individual).
show = true
}catch{
print("Authorization request failed: \(error)")
}
}
}
}
}
}
// Change background and animation so it matches app style
struct LoadingView: View {
var body: some View {
ZStack{
Color(red: 33 / 255, green: 33 / 255, blue: 33 / 255)
.ignoresSafeArea()
ProgressView {
Text("Loading")
.font(.largeTitle) // Large and prominent font size
.fontWeight(.bold) // Makes the text bold
.foregroundColor(.white) // Matches the app theme
.padding()
}
}
}
}
I am getting the exact same error!
Not sure how I am failing to grant permission in my main app as well. I even checked with print statements in my code to see if the Screen Time Authorization status was approved like this -
print("Screen Time Authorization Status (authorizationStatus)")
This was the full error when I attempted to run DeviceActivityReport(appsConext, filter: filter) --
LaunchServices: store (null) or url (null) was nil: Error Domain=NSOSStatusErrorDomain Code=-54 "process may not map database" UserInfo={NSDebugDescription=process may not map database, _LSLine=68, _LSFunction=_LSServer_GetServerStoreForConnectionWithCompletionHandler}
Attempt to map database failed: permission was denied. This attempt will not be retried.
Failed to initialize client context with error Error Domain=NSOSStatusErrorDomain Code=-54 "process may not map database" UserInfo={NSDebugDescription=process may not map database, _LSLine=68, _LSFunction=_LSServer_GetServerStoreForConnectionWithCompletionHandler}