Как создать Generic if @EnvironmentObject?

Недавно я столкнулся с необходимостью написать Mock Class, так как это приводит к тому, что SwiftUI preview не работает. К сожалению, я получаю сообщение об ошибке:

Property type 'T' does not match that of the 'wrappedValue' property of its wrapper type 'EnvironmentObject'

В структуре просмотра:

struct ContentView<T>: View {
    @EnvironmentObject var mockFoobar: T
    ...
}

А также ошибка:

Type of expression is ambiguous without more context

Для структуры предварительного просмотра:

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        let mockFoobar: MockFoobar = MockFoobar()
        return ContentView<MockFoobar>()
            .environmentObject(mockFoobar)
    }
}

Класс MockFoobar:

class MockFoobar: ObservableObject {
  ...
}

Как любезно предоставил пользователь @Aspergi, протестировал следующее, как было предложено:

class Foobar: ObservableObject {
    @Published var param: Bool = false
    func start() {
        self.param = true
    }
}

struct MyFoobarView<T: ObservableObject>: View {
    @EnvironmentObject var foobar: T

    var body: some View {
        VStack {
            Text("Hello Foobar")
        }
        .onAppear {
            self.foobar.start()
        }
    }
}

struct MyFoobarView_Previews: PreviewProvider {
    static var previews: some View {
        let foobar: Foobar = Foobar()
        return MyFoobarView()
            .environmentObject(foobar)
    }
}

Но я получаю следующие ошибки (первая в .onAppear и вторая в PreviewProvider):

Cannot call value of non-function type 'Binding<Subject>'

Generic parameter 'T' could not be inferred

person punkbit    schedule 23.05.2020    source источник
comment
Если все работает нормально, в чем вопрос? А что за «имитация»? Для меня это выглядит реальным.   -  person matt    schedule 23.05.2020
comment
Вы правы, позвольте мне удалить материал Simulator, поскольку это должно было показать, что процесс инициализации работает в этом контексте; но не является универсальным. Макетная часть в основном предназначена для объяснения необходимости использования Generic для представления environmentObject; Mock as in, имеет тот же интерфейс, что и Class Foobar; но мне нужно издеваться над Foobar, чтобы избежать некоторых вещей CoreAudio, которые мешают Preview работать   -  person punkbit    schedule 23.05.2020


Ответы (1)


EnvironmentObject должен быть ObservableObject, поэтому вот исправление

struct ContentView<T: ObservableObject>: View {
    @EnvironmentObject var mockFoobar: T

    // .. other code here

Обновление: добавлена ​​демонстрация с протоколом представленной модели.

protocol Foobaring {
    var param: Bool { get set }
    func start()
}

class Foobar: ObservableObject, Foobaring {
    @Published var param: Bool = false
    func start() {
        self.param = true
    }
}

struct MyFoobarView<T: ObservableObject & Foobaring>: View {
    @EnvironmentObject var foobar: T

    var body: some View {
        VStack {
            Text("Hello Foobar")
        }
        .onAppear {
            self.foobar.start()
        }
    }
}

struct MyFoobarView_Previews: PreviewProvider {
    static var previews: some View {
        let foobar: Foobar = Foobar()
        return MyFoobarView<Foobar>()
            .environmentObject(foobar)
    }
}
person Asperi    schedule 23.05.2020
comment
Спасибо! Даже когда класс MockFoobar имеет тип ObservableObject? Это не подразумевается? - person punkbit; 23.05.2020
comment
@punkbit, протестировал и работал с Xcode 11.4 / iOS 13.4. Я предполагаю, что есть еще что-то из предложенного кода. - person Asperi; 23.05.2020
comment
Окей круто! Итак, я перепроверил, но получаю два сообщения об ошибках (обновил исходный пост). Я продолжу изучать это, чтобы понять это, и, надеюсь, дополню его ответом. - person punkbit; 23.05.2020
comment
@punkbit, см. обновлено - добавлена ​​демонстрация с определенным протоколом. - person Asperi; 23.05.2020
comment
Ага, ценю! Соответствие протоколу и ObservableObject - это решение! - person punkbit; 23.05.2020