Тема 1: Неустранимая ошибка: не найден ObservableObject типа StoryVM

Я новичок в SwiftUI, поэтому, пожалуйста, оцените меня, мне бы хотелось узнать больше. Моя программа выдает ошибку «Тема 1: Неустранимая ошибка: ObservableObject типа StoryVM не найден. View.environmentObject(_:) для StoryVM может быть отсутствует как предок этой точки зрения». тот же код, использующий фиктивные данные, работает нормально, но я пытаюсь реализовать его с помощью Firebase

struct StoryCellView: View {
    @Binding var story: Story
    var isSeen: Bool = false
    @Environment(\.colorScheme) var scheme
    @EnvironmentObject var viewModel: StoryVM
    var body: some View {

        Text(story.name ?? "")
                    .font(.system(size: 12))
                    .fontWeight(.heavy)
                
                    .frame(minWidth: 62, maxWidth: 62, minHeight: 62, maxHeight: 62)
                    .clipShape(Circle())
                    .padding(2)
                    .background(scheme == .dark ? .black: .gray, in: Circle())
                    .padding(3)
                    .background(
                        LinearGradient(gradient: Gradient(colors: [.red, .orange, .red, .orange]), startPoint: .top, endPoint: .bottom)
                            .clipShape(Circle())
                            .opacity(isSeen ? 0 : 1)
                            
                    )
                    .onTapGesture {
                        withAnimation{
                            //story.isSeen = true
                            viewModel.markStoryAsSeen(story)
                            viewModel.currentStory = story.id ?? ""
                            viewModel.showStory = true
                            print("Story tapped: \(story.id ?? "")")
                        }
                    }
        
                    

    }
}
struct StoryCircleView: View {

    @EnvironmentObject var viewModel: StoryVM
    var body: some View {
        ScrollView(.horizontal, showsIndicators: false) {
            HStack(spacing: 12, content: {
                ForEach($viewModel.stories) { $story in
                    
                    StoryCellView(story: $story) //revert back to original when auth is implemented
                        .environmentObject(viewModel)
                    
    
                }
                
            })
            .padding()
            .padding(.top, 10)
        }
        
        
        .onAppear{
            //viewModel.getStoriesCount()
            viewModel.getStories()
        }
    }
}

модель представления

@MainActor
final class StoryVM: ObservableObject {
    @Published var showStory: Bool =  false
    
    @Published var currentStory: String = ""
    
    @Published var stories: [Story] = []
    
    private var isSeenStatus: [String: Bool] = [:]
    
    private var lastDocument: DocumentSnapshot? = nil
//    func getAllProducts() async throws {
//        self.products = try await ProductsManager.shared.getAllProducts()
//
//    }
    
    func getStories(){
        Task{
            do{
                let (newStories, lastDocument) = try await StoriesManager.shared.getAllStories(count: 8, lastDocument: lastDocument)
                
                self.stories.append(contentsOf: newStories)
                if let lastDocument {
                    self.lastDocument = lastDocument
                }
                
                newStories.forEach { story in
                                if isSeenStatus[story.id ?? ""] == nil {
                                    isSeenStatus[story.id ?? ""] = false
                                }
                            }
                
                print("you ran me!!!")
            }catch{
                print(error)
            }
        }
    }
    
    //will manage seen status when authentication has been implemented
    func markStoryAsSeen(_ story: Story) {
            if let storyID = story.id {
                isSeenStatus[storyID] = true
            }
        }

        func isStorySeen(_ story: Story) -> Bool {
            if let storyID = story.id {
                return isSeenStatus[storyID] ?? false
            }
            return false
        }
    
       func getStoriesCount() {
           Task {
               let count = try await StoriesManager.shared.getAllStoriesCount()
                print("all story count \(count)")
            }
        }
}

struct ContentView: View {
    @Environment(\.colorScheme) var colorScheme
    @StateObject private var viewModel = StoryVM()
    
    var body: some View {
        GeometryReader{
            let size = $0.size
            let safeArea = $0.safeAreaInsets
            NavigationStack{
                StoryCircleView()
                
                    .overlay(
                        StoryView(size: size, safeArea: safeArea)
                            
                        
                            .toolbar(.hidden, for: .tabBar)
                        
                    )
            }
            .environmentObject(viewModel)
        }
    }
}

пожалуйста, что я делаю не так. заранее спасибо

stackoverflow.com/questions/75752604/…
lorem ipsum 17.08.2024 01:16

Где вы объявили @StateObject var viewModel = StoryVM() в родительском представлении и передали ли вы его в подпредставления с помощью .environmentObject(viewModel)? Обратите внимание: в StoryCircleView вам следует удалить .environmentObject(viewModel) или хотя бы переместить его за пределы ScrollView.

workingdog support Ukraine 17.08.2024 01:22

@workingdogsupportUkraine единственное место, которое я объявил (в)StateObject var viewModel = StoryVM(), — это contentView. и перемещение его за пределы прокрутки не помогло. Я попробовал использовать (at)StateObject вместо (at)environmentobject, это устранило ошибку. но затем наложение в режиме просмотра контента перестало работать

Omotayo Samson 17.08.2024 01:45

@workingdogsupportUkraine есть идеи, почему наложение в представлении контента перестало работать после того, как OP использовал StateObject

gill bates 17.08.2024 02:11

Вы путаете данные модели и данные представления. В модельном классе должны быть только истории. Выбор должен иметь вид @state. Таким образом, одну и ту же модель можно использовать в нескольких представлениях.

malhal 17.08.2024 09:01
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
5
54
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Пробовали ли вы изменить @EnvironmentObject var viewModel: StoryVM на @stateObject var viewModel = StoryVM()

это исправило ошибку, есть идеи, как заставить работать наложение в представлении контента. из вывода в терминале я вижу, что ontapgesture работает, но наложение не работает. это странно, учитывая, что если я использую @environmentObject, наложение работает

Omotayo Samson 17.08.2024 01:59

честно говоря, я понятия не имею, почему ваше наложение не работает. возможно, некоторые эксперты увидят ваш вопрос и дадут лучшее представление

gill bates 17.08.2024 02:07

У вас должен быть только один источник истины, то есть только один @StateObject private var viewModel = StoryVM() в вашем ContentView (в том виде, в котором он у вас есть), который вы передаете другим представлениям, несколько таких источников недопустимы. В качестве ответа предоставил свой тестовый код. Можете ли вы также сказать нам, какая строка вашего кода выдает ошибку?

workingdog support Ukraine 17.08.2024 02:15
Ответ принят как подходящий

Вот мой базовый тестовый код, который у меня не дает сбоев.

Обратите внимание: id (например, Story.id) не должен быть Optional.

Также у вас должен быть только один источник истины, то есть только один @StateObject private var viewModel = StoryVM() в вашем ContentView (в том виде, в котором он у вас есть), который вы передаете другим точкам зрения, несколько таких источников недопустимы.

// for testing
struct Story: Identifiable {
    let id = UUID()  // <--- here, not optional
    var name: String
}


struct StoryCellView: View {
    @Binding var story: Story
    
    var isSeen: Bool = false
    @Environment(\.colorScheme) var scheme
    @EnvironmentObject var viewModel: StoryVM
    
    var body: some View {
        Text(story.name)
            .font(.system(size: 12))
            .fontWeight(.heavy)
            .frame(minWidth: 62, maxWidth: 62, minHeight: 62, maxHeight: 62)
            .clipShape(Circle())
            .padding(2)
            .background(scheme == .dark ? .black: .gray, in: Circle())
            .padding(3)
            .background(
                LinearGradient(gradient: Gradient(colors: [.red, .orange, .red, .orange]), startPoint: .top, endPoint: .bottom)
                    .clipShape(Circle())
                    .opacity(isSeen ? 0 : 1)
            )
            .onTapGesture {
                withAnimation{
                    //story.isSeen = true
                    viewModel.markStoryAsSeen(story)
         //           viewModel.currentStory = story.id ?? ""
                    viewModel.showStory = true
         //           print("Story tapped: \(story.id ?? "")")
                    print("---> in StoryCellView Story tapped: \(story.id)")
                }
            }
    }
}

struct StoryCircleView: View {
    @EnvironmentObject var viewModel: StoryVM
    
    var body: some View {
        ScrollView(.horizontal, showsIndicators: false) {
            HStack(spacing: 12, content: {
                ForEach($viewModel.stories) { $story in
                    StoryCellView(story: $story)  // <--- here, no .environmentObject
                }
            })
            .padding()
            .padding(.top, 10)
        }
        .onAppear{
            viewModel.getStoriesCount()
            viewModel.getStories()
            print("----> in StoryCircleView viewModel: \(viewModel.stories.count) ")
        }
    }
}

@MainActor
final class StoryVM: ObservableObject {
    @Published var showStory: Bool =  false
    
    @Published var currentStory: String = ""
    
    // <--- for testing
    @Published var stories: [Story] = [Story(name: "story-1"), Story(name: "story-2")]
    
    private var isSeenStatus: [String: Bool] = [:]
    
//    private var lastDocument: DocumentSnapshot? = nil
    
    func getStories(){ }
    
    //will manage seen status when authentication has been implemented
    func markStoryAsSeen(_ story: Story) { }
    
    func isStorySeen(_ story: Story) -> Bool {
        return false
    }
    
    func getStoriesCount() { }
}

struct ContentView: View {
    @Environment(\.colorScheme) var colorScheme
    @StateObject private var viewModel = StoryVM()
    
    var body: some View {
        GeometryReader {
            let size = $0.size
            let safeArea = $0.safeAreaInsets
            NavigationStack{
                StoryCircleView()
                   .overlay(
                    StoryView(size: size, safeArea: safeArea)
                          .toolbar(.hidden, for: .tabBar)
                   )
            }
            .environmentObject(viewModel)
        }
    }
}

// for testing
struct StoryView: View {
    @EnvironmentObject var viewModel: StoryVM  // <--- here
    
    let size: CGSize
    let safeArea: EdgeInsets
    
    var body: some View {
        Text("StoryView")
            .onAppear {
                print("----> in StoryView viewModel: \(viewModel.stories.count) ")
            }
    }
}

вы гений... вы заслуживаете бутылку охлажденного пива

Omotayo Samson 17.08.2024 02:50

рад, что это наконец сработало для вас, удачного кодирования.

workingdog support Ukraine 17.08.2024 02:55

спасибо @workingdogsupportUkraine

Omotayo Samson 17.08.2024 03:04

Другие вопросы по теме