Swift 2: guard in for loop? - ios

what is the correct way to use guard inside a for loop?
for (index,user) in myUsersArray.enumerate() {
guard user.id != nil else {
print("no userId")
//neither break / return will keep running the for loop
}
if user.id == myUser.id {
//do stuff
}
}

There are a few ways to make some conditionals:
You can put a condition for whole for. It will be called for each iteration
for (index, user) in myUsersArray.enumerate() where check() {}
for (index, user) in myUsersArray.enumerate() where flag == true {}
You can check something inside for and skip an iteration or stop the loop:
for (index, user) in myUsersArray.enumerate() {
guard check() else { continue }
guard flag else { break }
}
In your case I will be write something like this:
for (index, user) in myUsersArray.enumerate() {
guard let userId = user.id, userId == myUser.id else { continue }
// do stuff with userId
}

#Arsens answer is correct but I think this is easier to understand
let ints = [1,2,3,4,5]
for (index,value) in ints.enumerate() {
guard value != 1 else {
print("Guarded \(value)")
continue
}
print("Processed \(value)")
}

for (index,user) in myUsersArray.enumerate() {
guard let userId = user.id else {
print("no userId")
continue;
}
if userId == myUser.id {
//do stuff
}
}

Related

Properly configuring closure to capture results

I am writing some custom store methods for in app purchases; sort of a wrapper for SwiftyStore. The problem I'm running into is the inability to get the results from the closures before they exit.
Any suggestions on how to properly set them up? IE: Closures...
I have a function that checks for an existing subscription and returns true if it finds one in firebase, if it doesn't then it goes out to the apple store to verify a previously purchased subscription:
func checkSubscription() -> Bool {
var RetVal: Bool = false
var retStat: String = ""
var myVal: Bool = false
self.rootRef.child("users").child(self.userID!).observeSingleEvent(of: .value, with: { (snapshot) in
// Get user value
let value = snapshot.value as? NSDictionary
let mySubType = value?["subtyp"] as? String ?? ""
// set value
if mySubType == "" {
// get receipt
if self.myStore.getReceipt() == true {
(myVal, retStat) = self.myStore.verifyPurchase(product: "com.xxxxx.xxxxx.monthly")
if myVal == true && retStat == "Valid" {
// we have a valid product update firebase
print("Valid")
} else if myVal == true && retStat == "Expired" {
// we have a valid product that is expired
print("Expired")
}
}
} else {
// we have a purchase, verify its not expired.
print("Purchased")
RetVal = true
}
}) { (error) in
print(error.localizedDescription)
}
return RetVal
}
The problem here is its dropping down to the return RetVal before the closure is complete so the function could be returning an invalid value. Not sure how I can fix this in the current setup, but any suggestions or pointers would be appreciated.
To expand on Tom's comment, if you want to return a result when the nested asynchronous function is complete, you could pass in a completion handler closure that uses the Result type that Swift offers like the following:
func checkSubscription(completion: #escaping (Result<Bool, Error>) -> Void) {
var RetVal: Bool = false
var retStat: String = ""
var myVal: Bool = false
self.rootRef.child("users").child(self.userID!).observeSingleEvent(of: .value, with: { (snapshot) in
// Get user value
let value = snapshot.value as? NSDictionary
let mySubType = value?["subtyp"] as? String ?? ""
// set value
if mySubType == "" {
// get receipt
if self.myStore.getReceipt() == true {
(myVal, retStat) = self.myStore.verifyPurchase(product: "com.xxxxx.xxxxx.monthly")
if myVal == true && retStat == "Valid" {
// we have a valid product update firebase
print("Valid")
} else if myVal == true && retStat == "Expired" {
// we have a valid product that is expired
print("Expired")
}
}
} else {
// we have a purchase, verify its not expired.
print("Purchased")
RetVal = true
}
completion(.success(RetVal))
}) { (error) in
print(error.localizedDescription)
completion(.failure(error))
}
}
Calling the function using this type of completion handler would look something like this:
checkSubscription { (result) in
switch result {
case .success(let boolValue):
// do something with resulting boolean
break
case .failure(let error):
// do something with resulting error
break
}
}
func checkSubscription(completion: (_ scuess:Bool) ->()){
var RetVal: Bool = false
var retStat: String = ""
var myVal: Bool = false
self.rootRef.child("users").child(self.userID!).observeSingleEvent(of: .value, with: { (snapshot) in
// Get user value
let value = snapshot.value as? NSDictionary
let mySubType = value?["subtyp"] as? String ?? ""
// set value
if mySubType == "" {
// get receipt
if self.myStore.getReceipt() == true {
(myVal, retStat) = self.myStore.verifyPurchase(product: "com.xxxxx.xxxxx.monthly")
if myVal == true && retStat == "Valid" {
// we have a valid product update firebase
print("Valid")
} else if myVal == true && retStat == "Expired" {
// we have a valid product that is expired
print("Expired")
}
}
completion(false)
} else {
// we have a purchase, verify its not expired.
print("Purchased")
completion(true)
}
}) { (error) in
print(error.localizedDescription)
completion(false)
}
return RetVal
}
call completion(true) whenever your retValue supposed to be true and completion(false) whenever your retValue supposed to be true
Then call this function this way:
checkSubscription { (sucuess) in
if(sucuess){
print("OK")
}else{
print("BAD")
}
}

altering a variable outside a closure

I am currently encountering a problem. I have a function with an array which has items needing appending to. The items are appended in a closure inside the function and I can see the items in the array only inside the closure. Since the function has a return I need the appended items to be viewed by the function as a whole and not just the array. What can I do to solve this?
var trueOrFalse: Bool = false
var tempArray:[String] = []
let reference_message = reference(.Append).whereField("delay", isEqualTo: 0)
reference_message.getDocuments { (snapshot, error) in
if error != nil {
print(error!.localizedDescription)
}
guard let snapshot = snapshot else { return }
let documents = snapshot.documents
if documents != nil {
for document in documents {
let messageID = document[kMESSAGEID] as? String
tempArray.append(messageID!)
//print(trueOrFalse)
}
}
if trueOrFalse {
if opened && trueOrFalse {
print("Successful Walloping")
}
} else if !trueOrFalse {
if !opened || !trueOrFalse {
decryptedText = placeholderText
}
}
return JSQMessage(senderId: userId, senderDisplayName: name, date: date, text: decryptedText)

Avoid many if conditions

i have 6 strings (text,year,age,....) i want to filter array based on them ... i want to check before if the string is empty or not .. if not empty then added to the if condition as an && .. but how to do something like this without the need to write many if conditions on each string with each other string ?
i want to avoid doing something like this:
if self.searchtext != "" && self.qualification != ""{
}else if self.searchtext != "" && self.qualification != "" && self.gender != ""{
}else if self.searchtext != "" && self.qualification != "" && self.gender != "" && self.year != ""{
}else if self.searchtext != "" && self.qualification != "" && self.gender != "" && self.year != "" && self.major != ""{
}
....
How to do this?
if self.searchtext != ""{
if user.name.contains(find: self.searchtext) || user.mobile.contains(find: self.searchtext) || user.email.contains(find: self.searchtext){
DispatchQueue.main.async {
self.phones.append(user.mobile)
self.names.append(user.name)
}
}
}else if self.qualification != ""{
if user.qualification.contains(find: self.qualification){
DispatchQueue.main.async {
self.phones.append(user.mobile)
self.names.append(user.name)
}
}
}else if self.gender != ""{
if user.gender.contains(find: self.gender){
DispatchQueue.main.async {
self.phones.append(user.mobile)
self.names.append(user.name)
}
}
}else if self.year != ""{
if user.graduateYear.contains(find: self.year){
DispatchQueue.main.async {
self.phones.append(user.mobile)
self.names.append(user.name)
}
}
}else if self.major != ""{
if user.specialization.contains(find: self.major){
DispatchQueue.main.async {
self.phones.append(user.mobile)
self.names.append(user.name)
}
}
}else if self.city != ""{
if user.city.contains(find: self.city){
DispatchQueue.main.async {
self.phones.append(user.mobile)
self.names.append(user.name)
}
}
}
}
This is what i have working .. but this works as an OR when i want it to be like AND for all none empty strings
Here's a paragraph copied from LinkedIn's style guide
Using guard Statements
3.11.1 In general, we prefer to use an "early return" strategy where applicable as opposed to nesting code in if statements. Using guard statements for this use-case is often helpful and can improve the readability of the code.
// PREFERRED
func eatDoughnut(at index: Int) {
guard index >= 0 && index < doughnuts.count else {
// return early because the index is out of bounds
return
}
let doughnut = doughnuts[index]
eat(doughnut)
}
// NOT PREFERRED
func eatDoughnut(at index: Int) {
if index >= 0 && index < doughnuts.count {
let doughnut = doughnuts[index]
eat(doughnut)
}
}
It's what I personally do, and it's a good convention to avoid nested if's.
In your case, you can do a couple of things. First and foremost, use .isEmpty instead of comparing the string to an empty string (""). If your only intent is to check if all your strings are empty, you can accomplish that like this:
let strings = [searchtext, qualification, ...]
guard strings.filter({ $0.isEmpty }).count == 0 else {
return
}
// Code that only works if all fields have values
We can certainly simplify things a lot.
I would start by wrapping search criteria into an object:
struct UserSearchCriteria {
var searchText = ""
var gender = ""
var qualification = ""
...
init() {}
}
Then you can use one variable instead of your X variables:
var searchCriteria = UserSearchCriteria()
then you can add a simple matching method:
extension UserSearchCriteria {
func matchesUser(_ user: User) -> Bool {
if !searchText.isEmpty && [user.name, user.mobile, user.email].contains(where: { $0.contains(find: searchText) }) {
return true
}
if !qualification.isEmpty && user.qualification.contains(find: qualification) {
return true
}
...
return false
}
}
Then your big condition is reduced to:
if self.searchCriteria.matches(user) {
DispatchQueue.main.async {
self.phones.append(user.mobile)
self.names.append(user.name)
}
}
You cannot really avoid the conditions, but you can simplify them and organize them better, without duplicating code.
It seems you want to match all search conditions then I would change the matching method to:
func matchesUser(_ user: User) -> Bool {
if !searchText.isEmpty && ![user.name, user.mobile, user.email].contains(where: { $0.contains(find: searchText) }) {
return false
}
if !qualification.isEmpty && !user.qualification.contains(find: qualification) {
return false
}
...
return true
}
(note the double negation - contains condition negated and returning false).
You can use Guard statement to Early exit a scope .
A guard statement is used to transfer program control out of a scope
if one or more conditions aren’t met.
This will pass only if all fields are not empty.
guard self.searchtext != "", self.qualification != "", self.gender != "", self.year != "", self.major != "" else { return }
Update:
guard let searchText = self.searchtext, !searchText.isEmpty else { return }
// do something with `searchText`. Here the above conditions will be true.
OR you can use if-let
if let searchText = self.searchtext, !searchText.isEmpty {
// do with `searchText`(non-optional).
} else {
// conditions failed.
}
If you are trying to filter your array, you can simply use array.filter
let filteredUsers = usersArray.filter {
let shouldIAddThisElement = $0.name.contains(searchText) // Do your logic here
return shouldIAddThisElement
}
Swift includes guard statement. The lines after the guard statement will only be executed if the guard condition is true. Like an if/else statement, the else clause runs if the condition is false.
guard condition else {
// false: execute some code
}
// true: execute some code
For your case:
guard self.searchtext && self.qualification && self.gender && self.year && self.major else { return }
// execute some code
The return statement will exit the function or method.
More information can be found in the documentation on "Early Exit" Section:
https://docs.swift.org/swift-book/LanguageGuide/ControlFlow.html

Run a function before hitting return. iOS, Swift,

Is there a way to run a function if a guard statment is not met before it gets to return?
guard let detectedRectangle = observations.first else {
functionToRun(completion: { (complete) in
print("Tony GOT TO RETURN"); return }
})
The above code makes me put the return outside of the complete in part.
guard let detectedRectangle = observations.first else {
functionToRun(completion: { (complete) in
})
print("Tony GOT TO RETURN"); return }
I have set 3 print statements in the function to run at certain points and it gets to 2 of them before hiting the return print
You can return functionToRun if the func, that contains guard statement, has same return type as functionToRun. Example:
func guardHolder() { // guardHolder returns Void
guard let detectedRectangle = observations.first else {
// so, if functionToRun also returns Void type, we can return this func
return functionToRun { _ in print("Tony GOT TO RETURN") }
}
}

Retain cycle in Async calls while updating UI

Following is the code used by me. With this code deinit is not called, but if I comment out this line weakSelf?.tableView.reloadData() from code deinit gets called. Am I doing something wrong?
ZLNetworkHelper.sharedManager.getUserSavedAddress { (response) in
print("getUserSavedAddressFinished")
ZLProgressIndicator.stopAnimation()
if response.isSuccess && response.value != nil {
weak var weakSelf = self
guard weakSelf != nil else {
return
}
weakSelf!.address = response.value!.sorted(by: {$0.isDefault && !$1.isDefault})
weakSelf!.isExistingAddressSectionExpanded = false
if weakSelf!.address.count == 0 {
weakSelf!.title = LocalizationUtility.RCLocalizedString("ADD_ADDRESS")
}
DispatchQueue.main.async {
weakSelf!.tableView.reloadData()
}
if completion != nil {
completion!(true)
}
}
else {
let message = response.error?.localizedDescription
ZLCustomAlertVC.presentAlertInVC(self, withErrorMessage:message)
}
}
You want to capture self weakly in the closure like:
getUserSavedAddress { [weak self] (response) in
When you capture it later, you're still grabbing a reference to self in the closure.
Try the implementation like this:
ZLNetworkHelper.sharedManager.getUserSavedAddress { [weak self] (response) in
DispatchQueue.main.async {
print("getUserSavedAddressFinished")
ZLProgressIndicator.stopAnimation()
if response.isSuccess && response.value != nil {
self?.address = response.value!.sorted(by: {$0.isDefault && !$1.isDefault})
self?.isExistingAddressSectionExpanded = false
if self?.address.count == 0 {
self?.title = LocalizationUtility.RCLocalizedString("ADD_ADDRESS")
}
self?.tableView.reloadData()
if completion != nil {
completion!(true)
}
}
else {
let message = response.error?.localizedDescription
ZLCustomAlertVC.presentAlertInVC(self, withErrorMessage:message)
}
}
}
(I've only updated this on SO, so you may need to unwrap, etc. as needed)
You can use the code given by Fred Faust but use weakself in the else part also where you present the alert.

Resources