28-07-2019. I still have a question about the code below. I would like to separate the data model out of the ContentView. So I made a separate file and added the class, like this:
import SwiftUI
import Combine
class User: BindableObject {
let willChange = PassthroughSubject<Void, Never>()
var username : String = "Jan" { willSet { willChange.send() }}
var password : String = "123456" { willSet { willChange.send() } }
var emailAddress : String = "jan@mail.nl" { willSet { willChange.send() } }
}
#if DEBUG
struct User_Previews: PreviewProvider {
static var previews: some View {
User()
.environmentObject(User())
}
}
#endif
This doesn't work however, I'm getting an error:
Protocol type 'Any' cannot conform to 'View' because only concrete types can conform to protocols
Error occurs on the .environmentObject(User()) line in # if DEBUG.
After watching some video's I made the following code (including changes for Xcode 11 beta 4). Tips from both answers from dfd and MScottWaller are already included in the code.
import Combine
import SwiftUI
class User: BindableObject {
let willChange = PassthroughSubject<Void, Never>()
var username = "Jan" { willSet { willChange.send() } }
var password = "123456" { willSet { willChange.send() } }
var emailAddress = "jan@mail.nl" { willSet { willChange.send() } }
}
struct ContentView: View {
@EnvironmentObject var user: User
private func buttonPressed() {
print(user.username) // in Simulator
}
var body: some View {
VStack {
TextField("Username", text: $user.username)
TextField("Password", text: $user.password)
TextField("Emailaddress", text: $user.emailAddress)
Button(action: buttonPressed) {
Text("Press me!")
}
}
}
}
#if DEBUG
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.environmentObject(User())
}
}
#endif
But now onto the next part. If I have another view... how can I reference the data then? Since the source of truth is in the above ViewContent() view. The answer is:
import SwiftUI
struct DetailView: View {
@EnvironmentObject var user: User
var body: some View {
VStack {
TextField("Username", text: $user.username)
TextField("Password", text: $user.password)
TextField("Email", text: $user.emailAddress)
}
}
}
#if DEBUG
struct DetailView_Previews: PreviewProvider {
static var previews: some View {
DetailView()
.environmentObject(User())
}
}
#endif
Don't forget to edit the SceneDelegate (answer from dfd):
var user = User()
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: ContentView()
.environmentObject(user)
)
self.window = window
window.makeKeyAndVisible()
}
}