Form validation issue in macOS app - swift

I am trying to run through a settings form and make sure that the user hasn't left any of the required fields empty.
Some of the forms fields are secure ( eg password).
Whats the easiest way to loop through all these fields and check they are not empty?
I have tried below - but I get a weird error:
if textfield1.stringValue == "",
textfield2.stringValue == "",
passwordfield.stringValue == "" {
//Shows error: Braced block of statements is an unused closure
}
Additionally I am unable to group all these NSTextfields into an array as the password textfields are NSSecureTextField which despite being inherited from NSTextfield, the are not groupable with NSTextfield.

You can have NSTextField and NSSecureTextField in the same array. This is indeed an easy way to find the empty ones.
let tf = NSTextField()
let stf = NSSecureTextField()
let tf2 = NSTextField()
tf2.stringValue = "some text"
let all = [tf, stf, tf2]
let emptyTextFields = all.filter { $0.stringValue.isEmpty }
Also in your example you can't use commas to group conditions in if, you have to use &&:
if tf.stringValue.isEmpty && stf.stringValue.isEmpty && tf2.stringValue.isEmpty {
// do something
}
but this is not a good solution, better use the array and filter.

Under Swift 2, here's what Eric Aya correctly identified:
if textfield1.stringValue == "" && textfield2.stringValue == "" && == "" {
}
It also compiles under Swift 3.
On the other hand, the code you put in your question actually works in Swift 3.

Other way to check empty string with isEmpty variable of String object.
let userName = ""
let email = ""
if(userName.isEmpty && email.isEmpty) {
print("empty strings")
}
else {
print("good strings")
}

Related

Swift : affect struct by reference or "alias" to a variable

I have to update a struct, but this struct needs a long expression to be found from my viewController :
let theMessage:Message? = self.messageSections.first(where: { $0.date == DateDMY(fromNSDate:msg.date) })?
.msgs.first(where: { $0 == msg })
I want to mutate one property of this struct, but not a copy of it, I want to update it "where it is", that is in messageSections[].msgs[]
The problem here is that if I try this code after the one above :
theMessage.status = .NOT_SENT
Only the local copy will be updated, and not the real one in messageSections[].msgs[]. So when I reload my collectionView, nothing changes.
I could do
self.messageSections.first(where: { $0.date == DateDMY(fromNSDate:msg.date) })?
.msgs.first(where: { $0 == msg }).status = .NOT_SENT
But if I have to manipulate this property several times in my function, I dont want to re-write the whole expression each time. So what I'm looking for is something like in C :
let theMessage:Message?* = &self.messageSections.first(where: { $0.date == DateDMY(fromNSDate:msg.date) })?
.msgs.first(where: { $0 == msg })
if(theMessage != NULL)
theMessage->status = .NOT_SENT
I saw in other questions that I could use & before argument and inout before parameter name, but its only for functions calling. I'm looking for a way to create an alias for a big expression when affecting a variable, just to avoid re-writting it each time.
You can use firstIndex to get the index of the element in the array, then access the element directly using the retrieved index.
if let messageSectionIndex = self.messageSections.first(where: { $0.date == DateDMY(fromNSDate:msg.date) }), let messageIndex = self.messageSections[messageSectionIndex].msgs.firstIndex(where: { $0 == msg }) {
self.messageSections[messageSectionIndex].msgs[messageIndex].status = .NOT_SENT
}

Swift: "Where" vs "If"

Is there any difference between these two syntaxes? If not, any benefit?
if let userName = userNameTextField.text where userName.characters.count > 0,
let password = passwordTextField.text where password.characters.count > 0,
let confirmation = confirmationTextField.text where confirmation == password
else {
return false
}
and:
if userNameTextField.text?.characters.count > 0 &&
passwordTextField.text?.characters.count > 0 &&
confirmationTextField.text == passwordTextField.text
{
return false
}
First of all, note that where clauses in optional binding conditions is deprecated in Swift 3.0, replaced by ,.
Evolution Proposal SE-0099: Restructuring Condition Clauses
I.e.
let opt: Int?
/* Swift < 3 */
if let opt = opt where opt == 42 { /* ... */ }
/* Swift >= 3 */
if let opt = opt, opt == 42 { /* ... */ }
Secondly, your second example block wont compile in Swift >= 3.0, as the optional result from the optional chaining is not unwrapped; comparing optionals to literals has been removed in Swift 3.0 (thanks #MartinR), as per the following implemented proposals:
Evolution proposal SE-0121: Remove Optional Comparison Operators
Evolution proposal SE-0123: Disallow coercion to optionals in operator arguments
userNameTextField.text?.characters.count > 0 &&
/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
this is an optional that, for Swift >= 3.0, needs to be unwrapped
prior to comparing it to the integer literal */
Now, proceeding to answer your "is there any difference ..." question, assuming we look at your two options fixed for Swift 3.
You could choose to bind the value of the optional String property text of userNameTextField if you'd like to use it in the if block that follows, or
If youd only want to ascertain that the text property is not nil or empty (""), you could omit the binding in favour or simply checking its character.count
E.g.:
struct Foo {
var bar: String?
}
var foo = Foo()
/* if you want to use foo.bar within the if block */
if let str = foo.bar, str.characters.count > 0 {
// do something with str ...
print(str)
}
/* if you only need to ascertain foo.bar is not nil
and not empty */
if (foo.bar?.characters.count ?? 0) > 0 {
// do something with str ...
print("not empty")
}
// alternatively ... (not nil or empty)
if !(foo.bar?.isEmpty ?? true) {
// do something with str ...
print("not empty")
}
If you simply want to ascertain the latter and return false in case the ascertation fails, you could prefer using a guard statement instead of if:
guard (foo.bar?.characters.count ?? 0) > 0 else { return false }
// ... if no false return, proceed with app flow here ...
// alternatively ...
guard !(foo.bar?.isEmpty ?? true) else { return false }
Neither version works in Swift 3. And I think in the first version, you meant to use guard instead of let.
With Swift 3, you want to do the following:
guard
let userName = userNameTextField.text,
let password = passwordTextField.text,
let confirmation = confirmationTextField.text,
userName.characters.count > 0,
password.characters.count > 0,
confirmation == password
else {
return false
}
There are problems with using your approach in Swift 3 as explained by some of the other answers. But if you are using Swift 2, the following is my answer to your question.
The two are exactly the same with only one difference. Both approaches are testing for a nil value and then introducing a boolean test if the value is not nil. For example in your line,
if let userName = userNameTextField.text where userName.characters.count > 0
You are testing to see if userNameTextField is not nil and then you are testing if its count is greater than zero. The same thing applies this line,
if userNameTextField.text?.characters.count > 0
except it is much shorter and readable.
Because both approaches are within an 'if' statement, they both evaluate to true or false and achieve the same purpose.
However, what separates them is that with the 'where' clause, you can introduce a Boolean test after a binding or pattern, regardless of whether or not there's an underlying semantic link between the two. So I can basically do this with the 'where' clause,
if let userName = userNameTextField.text where age > 0
There is no semantic link between userNameTextField and age. As you can imagine, that might not be what you intended and this can quickly lead to errors.

Why does force Unwrapping give me an EXC_BREAKPOINT (SIGTRAP) Error?

My application is crashing on the declaration of my for loop for participant in event.attendees!. I'm relatively new to swift and understood that if I check that the attendees array is not nil then I'm free to force unwrap it. What have I misunderstood here?
private static func parseParticipants(event: EKEvent) -> [Attendee] {
var participants = [Attendee]()
if(event.attendees != nil && event.attendees?.count != 0) {
for participant in event.attendees! {
let participantName = parseEKParticipantName(participant)
let isRequiredParticipant = participant.participantRole == EKParticipantRole.Required
let hasAccepted = participant.participantStatus == EKParticipantStatus.Accepted
let attendee = Attendee(name: participantName, email: participant.URL.resourceSpecifier!.lowercaseString, required: isRequiredParticipant, hasAccepted: hasAccepted)
participants.append(attendee)
}
}
return participants
}
Turns out this was not about force unwrapping but was due to the EKParticipant.url property returning nil when it contained a String contained a " character.
let attendee = Attendee(name: participantName, email: participant.URL.resourceSpecifier!.lowercaseString, required: isRequiredParticipant, hasAccepted: hasAccepted)
We used this to access the participants email but any read or write operation to the url caused a crash so we took the EKParticipant.description property and parsed the email using a regular expression.
let participantEmail = participant.description.parse(pattern: emailRegex).first ?? ""
How about using optional binding?
if let attendees = event.attendees && attendees.count > 0 {
}

"If" statement not working with optional value

My problem is that I have some text fields that the user enters in numbers, the entered numbers then get saved to the corresponding variable.
However if the user doesn't enter a number and leaves it blank, the text field has a value of 'nil' and so would crash if unwrapped.
So I used an if statement to only unwrap if the contents of the test field are NOT nil, however this doesn't work. My program still unwraps it and crashes because the value is nil...
I don't understand how my if statement is not catching this.
On another note, how do I change my if statement to only allow Int values to be unwrapped and stored, strings or anything else would be ignored.
#IBAction func UpdateSettings() {
if CriticalRaindays.text != nil {
crit_raindays = CriticalRaindays.text.toInt()!
}
if EvapLess.text != nil {
et_raindays_lessthan_11 = EvapLess.text.toInt()!
}
if EvapMore.text != nil {
et_raindays_morethan_11 = EvapMore.text.toInt()!
}
if MaxWaterStorage.text != nil {
max_h2Ostore = MaxWaterStorage.text.toInt()!
}
if CarryForward.text != nil {
carry_forward = CarryForward.text.toInt()!
}
}
Your issue is that while the text exists, it doesn't mean toInt() will return a value.
Say the text was abc, CriticalRaindays.text != nil would be true but CriticalRaindays.text.toInt()! can still be nil, because abc cannot be converted to an Int.
The exact cause of your crash is likely that .text is equal to "", the empty string. It's not nil, but definitely not an Int either.
The better solution is to use optional binding to check the integer conversion and see if that passes, instead of merely the string existing:
if let rainDays = CriticalRaindays.text.toInt() {
crit_raindays = rainDays
}
If that doesn't compile, you possibly need to do Optional chaining:
if let rainDays = CriticalRaindays.text?.toInt()
Not on a Mac atm so can't test it for you but hope this makes sense!
Why not use an if let to unwrap on if the the text field's text is non-nil?
if let textString = self.textField.text as String! {
// do something with textString, we know it contains a value
}
In your ViewDidLoad, set CarryForward.text = "" This way it will never be nil
Edit:
To check if a textfield is empty, you can use this:
if (CarryForward.text.isEmpty) {
value = 10
}
else {
value = CarryForward.text
}

unwrapping multiple optionals in if statement

I want to unwrap two optionals in one if statement, but the compiler complaints about an expected expression after operator at the password constant.
What could be the reason?
if let email = self.emailField?.text && let password = self.passwordField?.text
{
//do smthg
}
Done in Swift.
Great news. Unwrapping multiple optionals in a single line is now supported in Swift 1.2 (XCode 6.3 beta, released 2/9/15).
No more tuple/switch pattern matching needed. It's actually very close to your original suggested syntax (thanks for listening, Apple!)
if let email = emailField?.text, password = passwordField?.text {
}
Another nice thing is you can also add where for a "guarding condition":
var email: String? = "baz#bar.com"
var name: String? = "foo"
if let n = name, e = email where contains(e, "#") {
println("name and email exist, email has #")
}
Reference: XCode 6.3 Beta Release Notes
Update for Swift 3:
if let email = emailField?.text, let password = passwordField?.text {
}
each variable must now be preceded by a let keyword
How about wrapping the optionals in a tuple and using switch to pattern match?
switch (self.emailField?.text, self.passwordField?.text) {
case let (.Some(email), .Some(password)):
// unwrapped 'email' and 'password' strings available here
default:
break
}
It's definitely a bit noisier, but at least it could also be combined with a where clause as well.
The usage
if let x = y {
}
is not equivalent to
if (let x = y) { // this is actually not allowed
}
"if let" is effectively a two-word keyword, which is equivalent to
if y != nil {
let x = y!
// rest of if let block
}
Before Swift 1.2
Like #James, I've also created an unwrap function, but this one uses the existing if let for control flow, instead of using a closure:
func unwrap<T1, T2>(optional1: T1?, optional2: T2?) -> (T1, T2)? {
switch (optional1, optional2) {
case let (.Some(value1), .Some(value2)):
return (value1, value2)
default:
return nil
}
}
This can be used like so:
if let (email, password) = unwrap(self.emailField?.text, self.passwordField?.text)
{
// do something
}
From: https://gist.github.com/tomlokhorst/f9a826bf24d16cb5f6a3
Note that if you want to handle more cases (like when one of the two fields is nil), you're better off with a switch statement.
Swift 4
if let suggestions = suggestions, let suggestions1 = suggestions1 {
XCTAssert((suggestions.count > suggestions1.count), "TEST CASE FAILED: suggestion is nil. delete sucessful");
}
I can't explain why the above code doesn't work, but this would be good a replacement:
if let email = self.emailField?.text
{
if let password = self.passwordField?.text
{
//do smthg
}
}
Based on #Joel's answer, I've created a helper method.
func unwrap<T, U>(a:T?, b:U?, handler:((T, U) -> ())?) -> Bool {
switch (a, b) {
case let (.Some(a), .Some(b)):
if handler != nil {
handler!(a, b)
}
return true
default:
return false
}
}
// Usage
unwrap(a, b) {
println("\($0), \($1)")
}