How to using guard and condition in iOS Swift 3? - ios

I wanna to get result of function and set it in another variable(let or var) and then check it with a condition like this:
guard galleryArr:Array<UIImage> = fetchGalleryImages() , galleryArr.count != 0 {
}else{
}
Please tell me the right way to fix this.

Swift 3.0
you can user guard and where condition like below:
But it must be that fetchGalleryImages() return optional value.
guard let galleryArr = fetchGalleryImages(), galleryArr.count > 0 else {
//What ever condition you want to do on fail or simply return
}

Try this:
func doSomething() -> Int? {
guard let galleryArr = fetchGalleryImages(), galleryArr.count != 0 else {
// you must return from doSomething in here, be it by throwing
// a fatalError(), return nil, or some other value to indicate
// that the call has failed
return nil
}
// proceed with the function
return ...
}

Related

Role of underscore "_" in conditional statement (if else) in Swift

Can somebody please explain the role of "_" in Swift code below.
var guessWasMade: Bool {
if let _ = game.guesses[currentQuestion] {
return true
} else {
return false
}
}
I understand how it is usually used as unnamed parameter in for-loops and functions.
But could not find any explanation for if else statement.
Tutorial explains it as guessWasMade checks game.guesses for a value. If a value is found we know the user has made a guess at the question.
game.guesses is an array of integers.
This is part of class declaration. Full code looks like this
class GameViewModel: ObservableObject {
// MARK: - Published properties
// 2
#Published private var game = Game()
// MARK: - Internal properties
// 3
var currentQuestion: Question {
game.currentQuestion
}
// 4
var questionProgressText: String {
"\(game.currentQuestionIndex + 1) / \(game.numberOfQuestions)"
}
// 1
var guessWasMade: Bool {
if let _ = game.guesses[currentQuestion] {
return true
} else {
return false
}
}
// MARK: - Internal Methods
// 2
func makeGuess(atIndex index: Int) {
game.makeGuessForCurrentQuestion(atIndex: index)
}
// 3
func displayNextScreen() {
game.updateGameStatus()
}
}
If let statements check to see if the value is nil. If it is, then the statement is false. If it isn't, then the value is assigned to a variable.
var optionalValue: Int? = 42
if let nonOptionalValue = optionalValue {
print(nonOptionalValue) // -> 42
} else {
print("optionalValue is nil")
}
So, when you do if let _ = game.guesses[...], you are checking to see if game.guesses[...] is nil. If it isn't, then you are ignoring the value with a wildcard pattern (_), which will match anything.
Because you are ignoring the value, it is the same as saying
if game.guesses[...] != nil {
return true
} else {
return false
}
Then, because you are simply returning the value of the condition, you can just return the condition itself:
var guessWasMade: Bool {
game.guesses[currentQuestion] != nil
}

How to create an elegant guard / return statement in Swift that gives output?

For example, this works:
guard condition == true else { return }
Which is fine, but creates a silent failure. What would be nice is to have a static function that could output feedback whilst also returning. Something like:
guard condition == true else { stop("condition was false") }
Am I living in dreamland here, or might this be possible?
Of course, I recognise that the following is possible:
guard condition == true else {
print("condition was false")
return
}
But is boilerplate heavy and kind of ugly. I have guard statements everywhere, this sort of code is: 1. useful; but 2. would bulk out my code by, like, 10% minimum.
It's utopian of me I know, but I would prefer an elegant solution. Anyone?
Use precondition instead of guard:
func test() {
precondition(yourCondition, "This is an error message")
//rest of your function
}
If yourCondition is false, the scope is going to be exited and the error message will be printed.
It really depends on what your function is all about. Typically methods with guard statements either have no return value or return optionals.
func myReturn() -> String? {
guard condition else { return nil }
}
if you want an analogue of stop, well, you could throw an Error, or even a fatalError
func myReturn() throws -> String {
guard condition else {
throw BadConditionError
}
}
Or
func myReturn() -> String {
guard condition else {
fatalError("Bad condition")
}
}
guard is an early exit mechanism, it prevents your program from getting into invalid state, use it accordingly. I'd also recommend reading on defer mechanism.
As I understand, you want to produce some output or show message on false condition or on nil value, before return in guard.
Below is my thinking:
func checkForNil(value: Any?) -> Any?
{
if value == nil
{
//showMessage("Nil value...")
print("nil value")
}
return value
}
you can use this as below:
guard let obj = self.checkForNil(value: objLoggedInUser) else { return}
Try using closures to get it working, i.e.
func func1(handler: ((String)->())) {
let condition = (2 == 2)
guard condition == true else {
handler("condition was false")
return
}
}
func1 { (reason) in
print(reason)
}

Reference variable whose value is assigned inside block

Hi had a confusion on what is the scope of the value that is assigned to outside ref variable inside a completion block. For example in the below code will the values of operationError and savedRecords persist outside of completion block.
func applyLocalChangesToServer(insertedOrUpdatedCKRecords:Array<CKRecord>,deletedCKRecordIDs:Array<CKRecordID>) throws
{
var savedRecords:[CKRecord]?
var conflictedRecords:[CKRecord] = [CKRecord]()
var removeRecords:[CKRecord] = [CKRecord]()
var operationError : NSError?
let ckModifyRecordsOperation = CKModifyRecordsOperation(recordsToSave:insertedOrUpdatedCKRecords, recordIDsToDelete: deletedCKRecordIDs);
ckModifyRecordsOperation.atomic = true
ckModifyRecordsOperation.modifyRecordsCompletionBlock = ({(savedRecords1,deletedRecordIDs1,error)->Void in
operationError = error
if error == nil
{
wasSuccessful = true
savedRecords = savedRecords1
}
else
{
wasSuccessful = false
savedRecords = nil
errorCKS = self.handleError(error!)
}
})
ckModifyRecordsOperation.perRecordCompletionBlock = ({(ckRecord,error)->Void in
if error != nil
{
if error!.code == CKErrorCode.ServerRecordChanged.rawValue
{
conflictedRecords.append(ckRecord!)
}
}
})
self.operationQueue?.addOperation(ckModifyRecordsOperation)
self.operationQueue?.waitUntilAllOperationsAreFinished()
if conflictedRecords.count > 0
{
//Do work here
}
else if operationError != nil //Other then the partial error
{
throw operationError
}
}
Note: Had assign operationError since the func applyLocalChangesToServer throws an error and is inside a while loop.
Your assumption is correct, these variables defied in the enclosure scope will be modified after the completion handler is performed. So you code should work as expected.
Also you can use following:
ckModifyRecordsOperation.main()
instead of:
self.operationQueue?.addOperation(ckModifyRecordsOperation)
self.operationQueue?.waitUntilAllOperationsAreFinished()
Hope it helps.

Find first element matching condition in Swift array (e.g. EKSource)

I would like to find the first EKSource of type EKSourceType.Local with a "single"-line expression in Swift. Here is what I currently have:
let eventSourceForLocal =
eventStore.sources[eventStore.sources.map({ $0.sourceType })
.indexOf(EKSourceType.Local)!]
Is there a better way of doing this (such as without mapping and/or with a generic version of find)?
Alternatively in Swift3 you could use:
let local = eventStore.sources.first(where: {$0.sourceType == .Local})
There's a version of indexOf that takes a predicate closure - use it to find the index of the first local source (if it exists), and then use that index on eventStore.sources:
if let index = eventStore.sources.indexOf({ $0.sourceType == .Local }) {
let eventSourceForLocal = eventStore.sources[index]
}
Alternately, you could add a generic find method via an extension on SequenceType:
extension SequenceType {
func find(#noescape predicate: (Self.Generator.Element) throws -> Bool) rethrows -> Self.Generator.Element? {
for element in self {
if try predicate(element) {
return element
}
}
return nil
}
}
let eventSourceForLocal = eventStore.sources.find({ $0.sourceType == .Local })
(Why isn't this there already?)
I don't understand why you're using map at all. Why not use filter? You will then end up with all the local sources, but in actual fact there will probably be only one, or none, and you can readily find out by asking for the first one (it will be nil if there isn't one):
let local = eventStore.sources.filter{$0.sourceType == .Local}.first
Swift 4 solution that also handles the situation when there are no elements in your array that match your condition:
if let firstMatch = yourArray.first{$0.id == lookupId} {
print("found it: \(firstMatch)")
} else {
print("nothing found :(")
}
Swift 5 If you want to find out from Array of Model then speciyfy $0.keyTofound otherwise use $0
if let index = listArray.firstIndex(where: { $0.id == lookupId }) {
print("Found at \(index)")
} else {
print("Not found")
}
Let's try something more functional:
let arr = [0,1,2,3]
let result = arr.lazy.map { print("💥"); return $0 }.first(where: { $0 == 2 })
print(result) // 3x 💥 then 2
Whats cool about this?
You get access to element or i while you search. And it's functional.
For Swift 3 you'll need to make a few small changes to Nate's answer above. Here's the Swift 3 version:
public extension Sequence {
func find(predicate: (Iterator.Element) throws -> Bool) rethrows -> Iterator.Element? {
for element in self {
if try predicate(element) {
return element
}
}
return nil
}
}
Changes: SequenceType > Sequence, Self.Generator.Element > Iterator.Element

if let variable - Use of unresolved identifier

I am using SwiftyJSON to call some APIs and fetch some data.
When I use:
if let variable = json["response"]["fieldname"] {
} else {
println("error")
}
I am not able to use the variable later on, for example to append the value to an array.
For example:
if let variable1 = json["response"]["fieldname1"] {
} else {
println("error")
}
if let variable2 = json["response"]["fieldname2"] {
} else {
println("error")
}
var currentRecord = structure(variable1, variable2) ---> This line returns an error (use of unresolved identifier variable1) as not able to find variable1 or variable2
myArray.append(currentRecord)
How can I solve this?
The scope of an if let is inside the brackets immediately following it:
if let jo = joseph {
// Here, jo is in scope
} else {
// Here, not in scope
}
// also not in scope
// So, any code I have here that relies on jo will not work
In Swift 2, a new statement, guard was added, that seems to have exactly the kind of behaviour you want:
guard let jo = joseph else { // do something here }
// jo is in scope
If you're stuck in Swift 1, though, an easy way for you to unwrap those variables without a pyramid of doom is:
if let variable1 = json["response"]["fieldname1"], variable2 = json["response"]["fieldname2"] {
var currentRecord = structure(variable1, variable2)
myArray.append(currentRecord)
} else {
println("error")
}
#oisdk already explained that the scope of a variable defined by if let is only inside the braces of that statement.
That's what you want, because if it if let statement fails, the variable is undefined. The whole point of if let is to unwrap your optional safely, so that inside the braces, you can be sure the variable is valid.
Another solution to your problem (in Swift 1.2) is to use multiple if let statements:
if let variable1 = json["response"]["fieldname1"],
let variable2 = json["response"]["fieldname2"]
{
//This code will only run if both variable1 and variable 2 are valid.
var currentRecord = structure(variable1, variable2)
myArray.append(currentRecord)}
else
{
println("error")
}
Your code checks variable2 always even variable 1 fails. But that causes (edited!) not the error.
You can check and assign both variables in the same line. The "true" branch will be executed only if both variables are not nil
let response = json["response"]
if let variable1 = response["fieldname1"], variable2 = response["fieldname2"] {
let currentRecord = structure(variable1, variable2)
myArray.append(currentRecord)
} else {
println("error")
}

Resources