Передача объекта среды между классами без представления в SwiftUI

#swift #swiftui #environmentobject

Вопрос:

Я понимаю, что EnvironmentObject оболочку свойств можно использовать для передачи объектов в представления. У меня есть объект сеанса, который я передаю своим представлениям. Теперь у меня есть требование передать это в один из моих классов моделей (т. Е. Без представления). В идеале эта модель (получающая объект сеанса) создается как a StateObject .

 struct CreditDetailsView: View {
  @EnvironmentObject var session: Session
  @StateObject var transactionsModel = TransactionsModel(token: session.token)
 

Приведенный выше код не будет работать (понятно), потому что:

 cannot use instance member 'session' within property initializer; property initializers run before 'self' is available
 

Есть какие-нибудь предложения о том, как я могу перейти в сессию TransactionsModel ?

Ответ №1:

Попробуйте инициализировать объект состояния в .onAppear() реквизите для дочернего представления, например так:

 struct CreditDetailsView: View {
  @EnvironmentObject var session: Session
  @StateObject var transactionsModel: TransactionModel?
  
  var body: some View {
    SomeChildView()
      .onAppear(perform: {
        transactionModel = TransactionModel(token: session.token)
      })
  }
}
 

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

Комментарии:

1. Спасибо! В другой ситуации я смог передать токен в части действия при нажатии кнопки. Поэтому я предполагаю, что подход, описанный вами выше, а также нажатие кнопки-это 2 примера того, как это работает.

2. Нет проблем, надеюсь, это вам поможет 🙂

3. Как бы вы передали ссылку на сам объект среды, а не только на одно из его свойств (в данном случае свойство токена)? Я пытался это сделать, и я получаю множество ошибок.

Ответ №2:

Это неправильный ответ. Пожалуйста, ознакомьтесь с выбранным ответом выше.

Вы можете получить доступ session к объекту внутри init . В этом случае transactionsModel должно быть сделано, чтобы быть уже инициализированным любым способом.

 @EnvironmentObject var session: Session
@StateObject var transactionsModel = TransitionalModel(token: "")

init() {
    let token = self.session.token
    _transactionsModel = StateObject(wrappedValue: TransitionalModel(token: token))
}
 

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

Комментарии:

1. На самом деле я не видел никакого кода, предоставляющего инициализацию в представлении. Но подумать только, ничто не мешает вам это сделать. спасибо, что указали на это. Я могу просто вызвать метод в модели и предоставить токен через init.

2. Относительно поставки чего-то внутри init . насколько я знаю, это обычная схема. Когда вам нужно передать какое-то значение в представление и вы хотите использовать его в качестве начального значения определенного @State/@StateObject, оно обычно используется именно так. В этом случае обычно используется назначенный инициал, чтобы получить значение извне.

3. Среда недоступна в View методе a init . Это приведет к фатальной ошибке, из-за которой не удастся найти наблюдаемый объект такого типа.

4. О. Я не проверял это. извините за неправильный ответ. Я помещу комментарий поверх ответа.

Ответ №3:

Лучший способ, который я нашел для этого (потому что у вас не может быть необязательного объекта состояния), — это:

 struct CreditDetailsView: View {
  @EnvironmentObject var session: Session
  @StateObject var localModel = LocalModel()
  
  var body: some View {
    SomeChildView()
      .onAppear {
        localModel.transactionModel = TransactionModel(token: session.token)
      }
  }

  class LocalModel: ObservableObject {
    @Published transactionModel: TransactionModel?
  }
}