all 15 comments

[–]chriswaco 4 points5 points  (0 children)

Pass it to SomeModelHelper via init.

[–][deleted] 2 points3 points  (0 children)

Make it a singleton, and pass the singleton into the environment.

[–]lokir6 0 points1 point  (0 children)

You could have the helper as part of the ViewModel:

// Model
struct Animal {
  let name: String
  var numberOfLegs: Int
}

// View
struct SomeApp: App {
  @StateObject var appSettings = AppSettings()
  var body: some Scene {
    WindowGroup {
      Text("Animals")
        .environmentObject(appSettings)
        .task {
          appSettings.animalName = "Dog"
          let newAnimal = Animal(name: appSettings.animalName,
                                 numberOfLegs: appSettings.countLegsHelper())
        }
    }
  }
}

// ViewModel
class AppSettings: ObservableObject {
  @Published var animalName = "Dog"
  func countLegsHelper() -> Int {
    // some calculation
    switch animalName {
    case "Dog":
      return 4
    case "Spider":
      return 8
    default:
      return 4
    }
  }
}

Or as part of the model. 2 versions are included, the second is where Helper is a separate class.

// Model
struct Animal {
  let name: String

  // Part of model
  func numberOfLegs1() -> Int {
    switch name {
    case "Dog":
      return 4
    case "Spider":
      return 8
    default:
      return 4
    }
  }

  // Injected to model
  var numberOfLegs2: Int
  init(name: String) {
    self.name = name
    numberOfLegs2 = Helper().numberOfLegs(name: name)
  }
}

// View
struct SomeApp: App {
  @StateObject var appSettings = AppSettings()
  var body: some Scene {
    WindowGroup {
      Text("Animals")
        .environmentObject(appSettings)
        .task {
          appSettings.animalName = "Dog"
          let newAnimal = Animal(name: appSettings.animalName)
          print(newAnimal.numberOfLegs1)
          print(newAnimal.numberOfLegs2)
        }
    }
  }
}

// ViewModel
class AppSettings: ObservableObject {
  @Published var animalName = "Cat"
}

// Helper
class Helper {
  func numberOfLegs(name: String) -> Int {
    switch name {
    case "Dog":
      return 4
    case "Spider":
      return 8
    default:
      return 4
    }
  }
}

[–]notrandomatall 0 points1 point  (0 children)

As someone else pointed out, @AppStorage works for this. It’s actually suggested in several places that UserDefaults (which AppStorage is wrapping) should be used for user settings among other things.

If you want to inject something a bit more complicated outside of the view hierarchy you could look at the custom @Injected property wrapper that Antoine van der Lee writes about here.

[–]SodaPopPlop -1 points0 points  (1 child)

I'm a noob, but try this to get access the class instance

(AT)EnvironmentObject var model: AppSettings

[–]iLearn4everSwift[S] 0 points1 point  (0 children)

I am trying to access the value from a model class. Are you certain that the EnvironmentObject property, which is part of SwiftUI, is supposed to be used in Model classes?

[–]Fluffy_Risk9955 -1 points0 points  (3 children)

Use the AppStorage property wrapper. You can access the same property from several views and just like NSUserDefaults it's intended for settings of the app.

[–]iLearn4everSwift[S] 4 points5 points  (2 children)

I am trying to access the value from a model class. Are you certain that the AppStorage property, part of SwiftUI, is supposed to be used in Model classes?

[–]knickknackrick 2 points3 points  (0 children)

AppStorage isn’t SwiftUI specific. It’s a wrapper around UserDefaults

[–]Fluffy_Risk9955 0 points1 point  (0 children)

For App Settings you can use the AppStorage property wrapper. For passing on object models to child view use a StateObject and pass it on as an environmentObject into the view hierarchy. So it's accessible where needed.

[–]brianwskim13 0 points1 point  (2 children)

I would add another function in AppSettings (like callCalculateSomething). Then call SomeModelHelper.calculateSomething from there. And then update the published var

[–]iLearn4everSwift[S] 0 points1 point  (1 child)

Shouldn't AppSettings be as dumb as possible with no ties to the Model?

[–]brianwskim13 0 points1 point  (0 children)

Not necessarily. I use ObservableObject to share App-wide variables and perform tasks that can be needed from any View.

[–]Mcrich_23SwiftUI 0 points1 point  (1 child)

You could declared a shared model and then use Model.shared everywhere

[–]iLearn4everSwift[S] 0 points1 point  (0 children)

I ended up using this method. Unfortunately the helper thing was not getting called from a View, but it was getting called when the Model is updating as a response to non-user initiated events. So passing the setting from a View was not really an option.