I've been using Combine with UIKit and Cocoa. The following is a simple example.
import UIKit
import Combine
class ViewController: UIViewController {
// MARK: - Variables
private var cancellableSet: Set<AnyCancellable> = []
@Published var loginText: String = ""
@Published var passwordText: String = ""
// MARK: - IBOutlet
@IBOutlet weak var loginField: UITextField!
@IBOutlet weak var passwordField: UITextField!
// MARK: - Life cycle
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.publisher(for: UITextField.textDidChangeNotification, object: loginField)
.sink { result in
if let textField = result.object as? UITextField {
if let text = textField.text {
self.loginText = text
}
}
}
.store(in: &cancellableSet)
NotificationCenter.default.publisher(for: UITextField.textDidChangeNotification, object: passwordField)
.sink { result in
if let textField = result.object as? UITextField {
if let text = textField.text {
self.passwordText = text
}
}
}
.store(in: &cancellableSet)
Publishers.CombineLatest($loginText, $passwordText)
.sink { (result0, result1) in
if result0.count > 3 && result1.count > 3 {
print("You are good")
} else {
print("No way!!!")
}
}
.store(in: &cancellableSet)
}
}
Now, I want to use Combine with SwiftUI. The following is SwiftUI equivalent, so far.
import SwiftUI
import Combine
struct ContentView: View {
@State var anycancellables = Set<AnyCancellable>()
@State var userText: String = ""
@State var passText: String = ""
@State var canSave: Bool = false
var body: some View {
ZStack {
VStack {
Color.white
}.onTapGesture {
UIApplication.shared.endEditing()
}
VStack {
TextField("Username", text: $userText) {
}.onChange(of: userText) { newValue in
}
SecureField("Password", text: $passText) {
}.onChange(of: passText) { newValue in
}
Spacer()
.frame(height: 20.0)
Button("Save") {
print("Saved...")
}
.foregroundColor(canSave ? Color.black : Color.gray)
.font(.system(size: 32.0))
.disabled(!canSave)
}.padding(.horizontal, 20.0)
}
}
}
So where does Combine fit into the code? I want to enable the Save button if text counts of loginText and passwordText are both greater than 3, which is done at the top with UIKit.
Muchos thankos.
To utilize Combine
well, you may need to know what are (or should be or can be) publisher. @State
variables cannot be publishers without some additional code.
To make publishers easily, you can work with ObservableObject
with @Published
variables:
import SwiftUI
import Combine
class ValidateLogin {
let user: String
let pass: String
init(user: String, pass: String) {
self.user = user
self.pass = pass
}
func validateMe() -> Bool {
return user.count > 3 && pass.count > 3
}
}
class MyContent: ObservableObject {
@Published var userText: String = ""
@Published var passText: String = ""
}
struct ContentView: View {
@StateObject var content = MyContent()
@State var canSave: Bool = false
@State var contentSubscriber: AnyCancellable?
var body: some View {
ZStack {
VStack {
TextField("Username", text: $content.userText) {
}
SecureField("Password", text: $content.passText) {
}
}.padding(.horizontal, 20.0)
}.onAppear {
self.contentSubscriber = self.content.$userText
.combineLatest(content.$passText)
.sink {userText, passText in
let validateLogin = ValidateLogin(user: userText, pass: passText)
self.canSave = validateLogin.validateMe()
}
}
}
}