Я пытаюсь использовать оболочки свойств Swift 5.1, но каждый раз, когда мне кажется, что у меня есть классный вариант использования, я в конечном итоге сталкиваюсь с проблемой, когда не могу использовать их внутри инициализатора моей модели представления.
Возьмите этот чрезвычайно простой пример.
class NoProblem {
var foo = "ABC"
let upperCased: String
init(dependencies: AppDependencies) {
self.upperCased = foo.uppercased()
}
}
@propertyWrapper
struct Box<Value> {
private var box: Value
init(wrappedValue: Value) {
box = wrappedValue
}
var wrappedValue: Value {
get { box }
set { box = newValue }
}
}
class OhNoes {
@Box var foo = "ABC"
let upperCased: String
init(dependencies: AppDependencies) {
self.upperCased = foo.uppercased()
}
}
В NoProblem
все работает как положено. Однако в OhNoes
я получаю эту ошибку: 'self' used in property access 'foo' before all stored properties are initialized
.
Конечно, это чрезвычайно упрощенный пример, но я получаю ту же проблему при выполнении оболочки @Property
для наблюдаемых свойств или @Injected
обертка, как в в этой статье и т. д.
И, к сожалению, создание свойства lay не будет работать: Property 'foo' with a wrapper cannot also be lazy
.
InВ случае, когда приведенный выше пример был слишком упрощенным, я привел немного более реальный пример:
final class ContactUsVMWorks {
let subject = Observable<String?>("")
let replyToEmail = Observable<String?>("")
let message = Observable<String?>("")
let formValid: Signal<Bool, Never>
init() {
let subjectValid: Signal<Bool, Never> = subject.map { $0.nilIfEmpty != nil }
let emailValid: Signal<Bool, Never> = replyToEmail.map { $0?.isEmail() ?? false }
let messageValid: Signal<Bool, Never> = message.map { $0.nilIfEmpty != nil }
formValid = combineLatest(subjectValid, emailValid, messageValid) { subjectValid, emailValid, messageValid in
subjectValid && emailValid && messageValid
}
}
}
@propertyWrapper
struct Property<Value> {
private let property: ReactiveKit.Property<Value>
init(wrappedValue: Value) {
self.property = ReactiveKit.Property(wrappedValue)
}
var wrappedValue: Value {
get { property.value }
set { property.value = newValue }
}
public var projectedValue: ReactiveKit.Property<Value> {
return property
}
}
final class ContactUsVMBroken {
@Property var subject: String? = ""
@Property var replyToEmail: String? = ""
@Property var message: String? = ""
let formValid: Signal<Bool, Never>
init() {
let subjectValid: Signal<Bool, Never> = $subject.map { $0.nilIfEmpty != nil }
let emailValid: Signal<Bool, Never> = $replyToEmail.map { $0?.isEmail() ?? false }
let messageValid: Signal<Bool, Never> = $message.map { $0.nilIfEmpty != nil }
formValid = combineLatest(subjectValid, emailValid, messageValid) { subjectValid, emailValid, messageValid in
subjectValid && emailValid && messageValid
}
}
}
Еще один пример:
final class DependencyViewModelWorks {
private let dependencies: AppDependencies
let isLoggedIn: Signal<Bool, Never>
init(dependencies: AppDependencies) {
self.dependencies = dependencies
isLoggedIn = dependencies.userManager.observableUser.map { $0 != nil }
}
}
@propertyWrapper
struct Injected<Service> {
private var service: Service?
public var container: Resolver?
public var name: String?
public var wrappedValue: Service {
mutating get {
if service == nil {
service = (container ?? Resolver.root).resolve(
Service.self,
name: name
)
}
return service! // swiftlint:disable:this force_unwrapping
}
mutating set {
service = newValue
}
}
public var projectedValue: Injected<Service> {
get {
return self
}
mutating set {
self = newValue
}
}
}
final class DependencyViewModelBroken {
@Injected var dependencies: AppDependencies
let isLoggedIn: Signal<Bool, Never>
init() {
isLoggedIn = dependencies.userManager.observableUser.map { $0 != nil }
}
}