I have created a class in SceneDelegate.swift file and I am trying to access it in SettingView.swift and ContentView.swift but I keep getting this error "Fatal error: No ObservableObject of type IconNames found. A View.environmentObject(_:) for IconNames may be missing as an ancestor of this view." What do i do? Sharing the code of all three files below.
ContentView File @Environment(.managedObjectContext) private var managedObjectContext @EnvironmentObject var iconSettings: IconNames
@FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \Item.timestamp, ascending: true)],
animation: .default)
private var items: FetchedResults<Item>
var body: some View {
NavigationView {
ZStack {
List {
ForEach(items) { item in
HStack {
Text(item.name ?? "")
Spacer()
Text(item.priority ?? "")
}
}
.onDelete(perform: deleteItems)
}//END: LIST
.navigationBarTitle("Todo", displayMode: .inline)
.navigationBarItems(leading: EditButton())
.navigationBarItems(trailing:
Button(action: {
self.showingSettingsView.toggle()
}) {
Image(systemName: "paintbrush")
.imageScale(.large)
}
.sheet(isPresented: $showingSettingsView) {
SettingView().environmentObject(iconSettings) //error shown here
}
SettingView File
@Environment(\.presentationMode) var presentationMode
@EnvironmentObject var iconSettings: IconNames
struct SettingView_Previews: PreviewProvider {
static var previews: some View {
SettingView().environmentObject(IconNames())
}
}
SceneDelegate File
import UIKit
import SwiftUI
class IconNames: ObservableObject {
var iconNames: [String?] = [nil]
@Published var currentIndex = 0
init() {
getAlternateIconNames()
if let currentIcon = UIApplication.shared.alternateIconName {
self.currentIndex = iconNames.firstIndex(of: currentIcon) ?? 0
}
}
func getAlternateIconNames() {
if let icons = Bundle.main.object(forInfoDictionaryKey: "CFBundleIcons") as?
[String: Any],
let alternateIcons = icons["CFBundleAlternateIcons"] as? [String: Any] {
for (_, value) in alternateIcons {
guard let iconList = value as? [String: Any] else { continue }
guard let iconFiles = iconList["CFBundleIconFiles"] as? [String] else {
continue }
guard let icon = iconFiles.first else { continue }
iconNames.append(icon)
}
}
}
}
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options
connectionOptions: UIScene.ConnectionOptions) {
let context = (UIApplication.shared.delegate as!
AppDelegate).persistentContainer.viewContext
let contentView = ContentView().environment(\.managedObjectContext, context)
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView:
contentView.environmentObject(IconNames()))
self.window = window
window.makeKeyAndVisible()
}
}
func sceneDidDisconnect(_ scene: UIScene) {
//Called as the scene is being released by the system
}
func sceneDidBecomeActive(_ scene: UIScene) {
//Called
}
func sceneWillResignActive(_ scene: UIScene) {
//Called
}
func sceneWillEnterForeground(_ scene: UIScene) {
//Called
}
func sceneDidEnterBackground(_ scene: UIScene) {
(UIApplication.shared.delegate as? AppDelegate)?.saveContext()
}
}
In Xcode 14 you get this code in a new SwiftUI Core Data project for free
import SwiftUI
@main
struct TestApp: App {
let persistenceController = PersistenceController.shared
var body: some Scene {
WindowGroup {
ContentView()
.environment(\.managedObjectContext, persistenceController.container.viewContext)
}
}
}
There is no AppDelegate
and no SceneDelegate
.
Create an instance of IconNames
as @StateObject
and put it in the environment
import SwiftUI
@main
struct TestApp: App {
let persistenceController = PersistenceController.shared
@StateObject var iconNames = IconNames()
var body: some Scene {
WindowGroup {
ContentView()
.environment(\.managedObjectContext, persistenceController.container.viewContext)
.environmentObject(iconNames)
}
}
}
The you have access to in any descendant of the root view with
@EnvironmentObject var iconSettings: IconNames
If you want to observe the changes of the scene use
@Environment(\.scenePhase) var scenePhase