I tried to follow the code snippet of WWDC 2019's Combine in Practice talk (starting at minute 26:00) or see slides 179 ff. but it won't compile and looking at the API some parts don't make sense to me (e.g., calling CombineLatest.init(A, B)
with a third argument of type closure
. I tried to adapt the examples so they compile.
Question Part 1/2: Can someone help me out and let me know if I am misunderstanding the WWDC 2019 code snippets?
First code snippet (slide 179)
@Published var password: String = ""
@Published var passwordAgain: String = ""
var validatedPassword: CombineLatest<Published<String>, Published<String>, String?> {
return CombineLatest($password, $passwordAgain) { password, passwordAgain in
guard password == passwordAgain, password.count > 8 else { return nil }
return password
}
}
I can only get this snippet to at least return me the Publisher from
CombineLatest`
- adding the
Publishers
enum to the namespace ofCombineLatest
- removing the trailing closure
- adding
.Publisher
toPublished<String>
@Published var password: String = ""
@Published var passwordAgain: String = ""
var validatedPassword: Publishers.CombineLatest<Published<String>.Publisher, Published<String>.Publisher> {
return Publishers.CombineLatest($password, $passwordAgain)
}
Second code snippet (slide 185)
@Published var password: String = ""
@Published var passwordAgain: String = ""
var validatedPassword: Map<CombineLatest<Published<String>, Published<String>, String?>> {
return CombineLatest($password, $passwordAgain) { password, passwordAgain in
guard password == passwordAgain, password.count > 8 else { return nil }
return password
}
.map { $0 == "password1" ? nil : $0 }
}
I can get this snippet to compile when:
- doing all of the steps listed for the first snippet
- Adding
Publishers.
in front ofMap
- moving the
<>
to the correct position - returning
Publishers.Map
explicitly and by using the correct parameterupstream:
@Published var password: String = ""
@Published var passwordAgain: String = ""
var validatedPassword: Publishers.Map<Publishers.CombineLatest<Published<String>.Publisher, Published<String>.Publisher>, String?> {
return Publishers.Map(upstream: Publishers.CombineLatest($password, $passwordAgain)) { password, passwordAgain in
guard password == passwordAgain, password.count > 8 else { return nil }
return password
}
or when including the .map {}
from the slide:
- by wrapping the var type in another
Publishers.Map<..., String?>
@Published var password: String = ""
@Published var passwordAgain: String = ""
var validatedPassword: Publishers.Map<Publishers.Map<Publishers.CombineLatest<Published<String>.Publisher, Published<String>.Publisher>, String?>, String?> {
return Publishers.Map(upstream: Publishers.CombineLatest($password, $passwordAgain)) { password, passwordAgain in
guard password == passwordAgain, password.count > 8 else { return nil }
return password
}
.map { $0 == "password1" ? nil : $0 }
}
Question Part 2/2: What would be the swifty way to do this? e.g, by using something like this (which does not compile):
@Published var password: String = ""
@Published var passwordAgain: String = ""
var validatedPassword: AnyPublisher<String?, Never> {
return Just($password)
.combineLatest($passwordAgain) { password, passwordAgain in
guard password == passwordAgain, password.count > 8 else { return nil }
return password
}
.map{ $0 == "password1" ? nil : $0 }
.eraseToAnyPublisher()
}