Search code examples
swiftswiftui

Swift onChange event of FamilyActivityPicker not firing


I'm playing around with some swift development for the first time in quite a few years and the language has changed some what with swiftui. I've been following a few different tutorials and I think I might be muddling up some ways of doing things.

I'm trying to initialize a FamilyActivityPicker and respond to the onChange event. Following one tutorial, the data I've bound to the FamilyActivityPicker is from a model used as state in my view. However the onChange event for the picker never fires (I have a print statement that never fires). If I add some code to a didSet in my model, this does fire (though seems to fire 6 times). Can someone see anything obvious I'm doing wrong here? I'm not sure if it's down to my understanding of swiftui and how everything binds together

struct ContentView: View {
  @StateObject var model = FamilyControlModel.shared
  @State var isPresented = false
  
  var body: some View {
      Button("Open picker") {
          Task {
              try await model.authorize()
              isPresented = true
          }
      }
      .familyActivityPicker(
          isPresented: $isPresented,
          selection: $model.selection
          
      ).onChange(of: model.selection) {
          print("on change")
          let data = try? encoder.encode(model.selection)
      
          let strData = data!.base64EncodedString()
          print(strData)
           
      }
      .padding()
  }
}

Class FamilyControlModel {
   static let shared = FamilyControlModel()
   var selection = FamilyActivitySelection() 
}

Solution

  • Here is my fully working test code using @StateObject private var model = FamilyControlModel() and class FamilyControlModel: ObservableObject {...} and an example func authorize().

    On MacOS 15.3, using Xcode 16.2, target iOS-18, tested on real iOS device.

    Note, you also need to add Family Controls (Development) to your Signing & Capabilities of your target. From the docs, "Family Controls enables your app for parental controls, granting access to the Managed Settings and Device Activity frameworks in the Screen Time API."

    import SwiftUI
    import FamilyControls
    
    struct ContentView: View {
      @StateObject private var model = FamilyControlModel()  // <-- here
      @State private var isPresented = false
    
      var body: some View {
          Button("Open picker") {
              Task {
                  try await model.authorize()
                  isPresented = true
              }
          }
          .familyActivityPicker(isPresented: $isPresented, selection: $model.selection)
          .onChange(of: model.selection) {
              print("----> on change")
              do {
                  let data = try JSONEncoder().encode(model.selection) // <-- here
                  let strData = data.base64EncodedString()
                  print(strData)
              } catch {
                  print(error)
              }
          }
          .padding()
      }
    }
    
    class FamilyControlModel: ObservableObject {  // <-- here
        let center = AuthorizationCenter.shared
        
        @Published var selection = FamilyActivitySelection()
        
        func authorize() async throws {  // <-- here
            do {
                try await center.requestAuthorization(for: FamilyControlsMember.individual)
            } catch {
                print(error)
            }
        }
    }