How do I persist the Family Activity Picker?

I am currently building a screen time app and I am trying to figure out how to persist the family activity picker so that when my app closes and re-opens, the app selections in it are saved. I've successfully implemented core data and figured out how to store names of the selected apps in a list like this -

Core Data addApp Function -

func addApp(name: String, context: NSManagedObjectContext){
    let newApp = AppToken(context: context)
    newApp.bundleIdentifier = name
    saveData(context: context)
}

Adding app selections to Core Data (after the family activity picker has updated the selection) -

 .onChange(of: model.selectionToDiscourage)
           
{     
              for i in model.selectionToDiscourage.applications {
                    print(i)
                    dataController.addApp(name:i.localizedDisplayName ?? "Temp", context: moc)
                }
                

Printing saved selections in a list (bundleIdentifier is my attribute for my appToken entity, but I am just pulling the names here. For whatever reason all of them end up being Temp" as shown above anyway. In other words name:i.localizedDisplayName is not working and Temp is shown in the list for every app chosen) -

            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)
            }
            

So, when my app closes and reopens, the list of app names persists. Now, my issue is figuring out how to write back to selectionToDiscourage and loading the family activity picker with those saved apps. I have no idea if I should be doing this a different way and if using Core Data is overkill, but I cannot figure out how it's syntactically possible to write back to this family activity picker when the app reopens -

.familyActivityPicker(isPresented: $isPresented, selection:$model.selectionToDiscourage)

Thank you to whoever takes a look at this!!

It seems that you are trying to achieve the following steps:

  1. Persist the current selected data into a Core Data store.
  2. Fetch the data back (in your app's next launch session).
  3. Present a family activity picker with a selection.

From your description, it seems that i.localizedDisplayName doesn't have the right value, indicating that the issue may be at step 1 or 2. Your post doesn't have the details about how you persist and fetch the data though, and so I can't say anything for sure.

If you can provide a minimal project that contains only the code relevant to the issue, with detailed steps to reproduce the issue. I may be able to take another look.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

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()
        }
    }

}
}

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)
        }
    }
}

}

How do I persist the Family Activity Picker?
 
 
Q