Last active
January 10, 2025 18:33
-
-
Save Alex-Ozun/9103832faac41dd34ccfd87868a92002 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
struct Email { | |
init?(_ string: String) { | |
// parsing | |
} | |
} | |
struct PhoneNumber { | |
init?(_ string: String) { | |
// parsing | |
} | |
} | |
enum ContactDetails { | |
case email(Email) | |
case phone(PhoneNumber) | |
case both(Email, PhoneNumber) | |
init?(email: String, phoneNumber: String) { | |
switch (Email(email), PhoneNumber(phoneNumber)) { | |
case let (email?, phoneNumber?): | |
self = .both(email, phoneNumber) | |
case let (email?, .none): | |
self = .email(email) | |
case let (.none, phoneNumber?): | |
self = .phone(phoneNumber) | |
case (.none, .none): | |
return nil | |
} | |
} | |
} | |
struct Form: View { | |
@State var email: String = "" | |
@State var phoneNumber: String = "" | |
var contactDetails: ContactDetails? { | |
ContactDetails(email: email, phoneNumber: phoneNumber) | |
} | |
var body: some View { | |
VStack { | |
TextField("Email", text: $email) | |
TextField("Phone Number", text: $phoneNumber) | |
Button("Register") { | |
if let contactDetails { | |
register(with: contactDetails) | |
} | |
} | |
.disabled(contactDetails == nil) | |
} | |
} | |
func register(with contactDetails: ContactDetails) {} | |
} | |
// WITH INPUT VALIDATION | |
protocol Parsable { | |
associatedtype ParsingError: Error | |
init(input: String) throws(ParsingError) | |
} | |
enum InputState<Value: Parsable> { | |
case empty | |
case invalid(inputText: String, errorMessage: String?) | |
case valid(inputText: String, value: Value) | |
// To be used as proof of validation | |
var value: Value? { | |
guard case let .valid(_, value) = self else { return nil } | |
return value | |
} | |
// To be displayed on UI | |
var inputText: String { | |
switch self { | |
case .empty: | |
return "" | |
case let .invalid(inputText, _), | |
let .valid(inputText, _): | |
return inputText | |
} | |
} | |
// To be displayed on UI | |
var errorMessage: String? { | |
guard case let .invalid(_, errorMessage) = self else { return nil } | |
return errorMessage | |
} | |
init(inputText: String) { | |
guard inputText.isEmpty else { | |
self = .empty | |
} | |
do { | |
self = .valid(inputText: inputText, value: try Value(input: inputText)) | |
} catch { | |
self = .invalid(inputText: inputText, errorMessage: error.localizedDescription) | |
} | |
} | |
} | |
struct FormWithInputValidation: View { | |
@State var email: InputState<Email> = .empty | |
@State var phoneNumber: InputState<PhoneNumber> = .empty | |
var contactDetails: ContactDetails? { | |
ContactDetails(email: email, phoneNumber: phoneNumber) | |
} | |
var body: some View { | |
VStack { | |
TextField( | |
"Email", | |
text: .init( | |
get: { | |
email.inputText | |
}, set: { inputText in | |
email = InputState<Email>(inputText: inputText) | |
} | |
) | |
) | |
if let errorMessage = email.errorMessage { | |
Text(errorMessage) | |
} | |
TextField( | |
"Phone Number", | |
text: .init( | |
get: { | |
phoneNumber.inputText | |
}, set: { inputText in | |
phoneNumber = InputState<PhoneNumber>(inputText: inputText) | |
} | |
) | |
) | |
if let errorMessage = phoneNumber.errorMessage { | |
Text(errorMessage) | |
} | |
Button("Register") { | |
if let contactDetails { | |
register(with: contactDetails) | |
} | |
} | |
.disabled(contactDetails == nil) | |
} | |
} | |
func register(with contactDetails: ContactDetails) {} | |
} | |
extension ContactDetails { | |
init?(email: InputState<Email>, phoneNumber: InputState<PhoneNumber>) { | |
switch (email.value, phoneNumber.value) { | |
case let (email?, phoneNumber?): | |
self = .both(email, phoneNumber) | |
case let (email?, .none): | |
self = .email(email) | |
case let (.none, phoneNumber?): | |
self = .phone(phoneNumber) | |
case (.none, .none): | |
return nil | |
} | |
} | |
} | |
extension Email: Parsable { | |
struct ParsingError: Error { | |
let errorMessage: String | |
} | |
init(input: String) throws(ParsingError) { | |
// parsing | |
} | |
} | |
extension PhoneNumber: Parsable { | |
struct ParsingError: Error { | |
let errorMessage: String | |
} | |
init(input: String) throws(ParsingError) { | |
// parsing | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment