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")
}
Related
I'd like to use 'if let' here but I can't get it to work. Can someone help me?
guard animal?.img != nil else {
print ("image is nil")
return
}
let animalImage = UIImage(data: animal!.img!) as UIImage?
saveImageView.image = animalImage
I think you mean "guard let". BTW, you almost always want to avoid force unwrapping.
Also since UIImageView.image is optional you don't need to check the returned value from UIImage constructor
guard let data = animal?.img else {
print ("image data is nil")
return
}
saveImageView.image = UIImage(data: data)
There are several things you can do with an optional value:
var optionalText:String?
...
var text4:String? = optionalText // assign it to another optional variable
var text5 = optionalText // the same, the compiler will infer that text5 is also an optional string
optionalText?.append(" hello") // will only call `append(_:)` on `text` if it's not nil, otherwise this will be ignored
let text6:String = optionalText ?? "It was nil!" // use the coalescing operator
let text7:String = optionalText! // force-unwrap it into a String; if it's nil, your app will crash
// you CANNOT, however, treat it as a non-optional:
let newText = "Hello " + optionalText // this won't compile
// you can unwrap it:
if let text8 = optionalText {
// this code block will ONLY execute if textField.text was not nil
let newString = "Hello "+ text8 // this constant will only work in this block
// in that case, text8 will be of type String (non-optional)
// so it shouldn't be treated as an optional
}
let newString = "Hello "+ text8 // this won't compile
// unwrap it with `guard`:
guard let text8 = optionalText else {
// this code block will ONLY execute if textField.text WAS nil
// and it must return
return
}
let newString = "Hello "+ text8 // this is okay, text8 lives on in case of `guard`
These are the differences between guard let and if let:
if let nonOptional = optional {} will assign a value to a non-optional constant by unwrapping an optional value and execute the code block, but only if the optional isn't nil. The non-optional constant will only live inside the if let { } block. Use this if you want to handle both cases (nil or otherwise) and then move on with your code.
guard let nonOptional = optional else { } will do the same assignment if possible, but code flow will be different afterwards: it will execute the else block in case the optional value is nil, and that else block will have to quit the scope (return, continue, or break); it must not fall through, i.e. execution must not continue right after the block (the compiler will make sure of that). However, your nonOptional constant will live on after this statement. Use this if your code largely depends on the optional value not being a nil: quit early if the condition fails, and otherwise hold on to the non-optional value and use if for the rest of your enclosing scope.
Btw., you can also use var instead of let if it makes sense, in both cases.
The main purpose of guard is to avoid the "pyramid of doom" type of nested checks:
// pyramid of doom
func doomed() {
if condition1 {
if condition2 {
if condition3 {
// success scenario
print("all good")
// ...
} else {
print("failed condition 3")
return
}
} else {
print("failed condition 2")
return
}
} else {
print("failed condition 1")
return
}
}
// avoiding pyramid of doom
func nonDoomed() {
guard condition1 else {
print("failed condition 1")
return
}
guard condition2 else {
print("failed condition 2")
return
}
guard condition3 else {
print("failed condition 3")
return
}
// success scenario
print("all good")
// ...
}
Without guard, your success scenario is hidden in the middle of nested if statements related to error conditions, making your code difficult to read or edit.
With guard, each error condition is handled separately, and after you get them out of the way, you can go on with the success scenario. With guard let, you can also ensure that all the necessary constants and variables are available for the rest of the scope.
What you seem to need is one of two things:
Optionally unwrap and use your optional:
if let realImage = animal?.img {
let animalImage = UIImage(data: animal!.img!) as UIImage?
saveImageView.image = animalImage
}
// otherwise, saveImageView.image will not be assigned a new value
Simply pass an optional value
saveImageView.image = animal?.img
This should work because the left-hand side and the right-hand side are both UIImage? optionals.
This should do it, you assign your image data to first variable, then use it to assign to the second one:
if let img = animal?.img, let animalImage = UIImage(data: img) {
//do something
}
Probably don't need as? Data and as? UIImage, it depends on your model.
You can do it like below...
if let imgName = animal?.img {
saveImageView.image = UIImage(data: imgName)
}
I would extend Data and create an image property to return an optional image from your data object:
extension Data {
var image: UIImage? { UIImage(data: self) }
}
Usage:
saveImageView.image = animal?.img?.image
This would allow you to provide a default image as well in case of nil using the nil coalescing operator:
saveImageView.image = animal?.img?.image ?? UIImage(named: "defaultImage")
guard let and if let are effectively the same thing. In fact, if you are guarding the first statement to check if it's nil anyway, you can combine them into a single check and do something like this:
guard let animalImageData = animal?.img,
let animalImage = UIImage(data: animalImageData) else { return }
saveImageView.image = animalImage
or alternatively,
if let animalImageData = animal?.img, let animalImage = UIImage(data: animalImageData) {
saveImageView.image = animalImage
}
When you force-unwrap animal!.img!, it is unsafe practice and should generally be avoided since it could lead to fatal exceptions.
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 ...
}
I'm struggling to understand why my code isn't working? Any help/correction is appreciated.
struct DogBowlWithFailableInitializers {
var foodLeft: Int
init?(foodLeft: Int) {
if foodLeft < 0 {
return nil
}
self.foodLeft = foodLeft
}
}
if let negativeDogFoodTest = DogBowlWithFailableInitializers(foodLeft: 10) {
print("Success!")
let negativeDogFoodTest = DogBowlWithFailableInitializers(foodLeft: 10)
}
else {
print("Invalid dog food amount inputted")
}
From my understanding, a new instance should be created since it passed both if statements inside the structure and out. But when I try to access any properties within the instance, I'm not able to, why is that?
print(negativeDogFoodTest.foodLeft) //doesn't work
You don't seem to understand the concept of scoping. Consider this simple if let statement:
if let x = y {
// A
someVariable = x // works
}
// B
someVariable = x // does not work
x is only accessible inside the if statement, i.e. at A. Outside the if statement, i.e. at B, x is out of scope.
Why? Because it makes no sense to access x at B because B is executed whether or not y is nil.
So you should access it inside the if statement.
Another problem is that you declared negativeDogFoodTest twice:
// 1st time
if let negativeDogFoodTest = DogBowlWithFailableInitializers(foodLeft: 10) {
print("Success!")
// 2nd time!
let negativeDogFoodTest = DogBowlWithFailableInitializers(foodLeft: 10)
}
One time is enough!
if let negativeDogFoodTest = DogBowlWithFailableInitializers(foodLeft: 10) {
print("Success!")
print(negativeDogFoodTest.foodleft)
}
You can also consider a guard statement:
guard let negativeDogFoodTest = DogBowlWithFailableInitializers(foodLeft: 10)
else { return }
negativeDogFoodTest is a local variable with scope in the then-branch of your if. It does not exist outside of that block.
So you need to do this:
if let negativeDogFoodTest = ... {
print(negativeDogFoodTest.foodLeft)
}
Note that your second assignment to negativeDogFoodTest is redundant.
You can also use guard instead:
guard let negativeDogFoodTest = ... else {
dealWithFailure()
(return|break|continue)
}
print(negativeDogFoodTest.foodLeft)
I don't understand why I am getting this error. I don't see anything wrong with the code. Please help!! Thanks!
guard reason == .completed else { return }
***guard let symptomTrackerViewController = symptomTrackerViewController***,
let event = symptomTrackerViewController.lastSelectedAssessmentEvent else { return }
let carePlanResult=carePlanStoreManager.buildCarePlanResultFrom(taskResult: taskViewController.result)
carePlanStoreManager.store.update(event, with: carePlanResult, state: .completed) {
success, _, error in
if !success {
print(error?.localizedDescription)
}
}
}
}
The syntax
if let someVal = someVal {
//Inside the braces someVal is unwrapped
}
Works when the variable name is the same on both sides of the equal sign.
However, the code
guard let someVal = someVal else {
return
}
Is not legal. I believe the reason the first form, the if let conditional binding, lets you unwrap an optional using the same name, is that the reassignment is only valid inside the inner level of scope created by the braces for the if statement.
In contrast, the guard statement does not put braces around the code that gets executed when the optional unwrapping succeeds. There's no inner scope in which the new definition of someVar is valid.
The second part of it is that it sounds like your symptomTrackerViewController is not an optional. If symptomTrackerViewController is not an optional then any code that attempts to unwrap it (if let, guard let, and using ? and !) will fail.
Either symptomTrackerViewController or symptomTrackerViewController.lastSelectedAssessmentEvent is not an optional.
Check if your symptomTrackerViewController is optional. If it is not optional it can never be nil so you can remove it from the guard.
Try changing guard to
guard let symptomTrackerVC = symptomTrackerViewController,
let event = symptomTrackerViewController.lastSelectedAssessmentEvent
else { return }
When you are using if let something = somethingElse,
somethingElse must be an optional.
I want to load a PDF that is in my application bundle into a CGPDFDocument.
Is there some way of calling a function that if any of the parameters that don't accept options have values that are nil, the function isn't called and nil is returned.
eg:
let pdfPath : String? = NSBundle.mainBundle().pathForResouce("nac_06", ofType:"pdf")
//I want to do this
let data : NSData? = NSData(contentsOfFile:pdfPath)
//I have to do this
let data : NSData? = pdfPath != nil ? NSData(contentsOfFile:pdfPath) : nil
let doc : CGPDFDocumentRef? = CGPDFDocumentCreateWithProvider(CGDataProviderCreateWithCFData(data));
//pageView.pdf is optional, nicely this function accepts the document as an optional
pageView.pdfPage = CGPDFDocumentGetPage(doc, 1);
Because NSData.init?(contentsOfFile path:String), doesn't define path as optional, even though it is has an optional return value, I have to check before and if the parameter is nil, return nil. Is there some syntactic sugar for the data assignment (instead of the ?: operator)?
Either use multiple optional bindings separated by commas
func loadPDF() -> CGPDFDocumentRef?
{
if let pdfPath = NSBundle.mainBundle().pathForResouce("nac_06", ofType:"pdf"),
data = NSData(contentsOfFile:pdfPath),
doc = GPDFDocumentCreateWithProvider(CGDataProviderCreateWithCFData(data)) {
return doc
} else {
return nil
}
}
or use the guard statement
func loadPDF() -> CGPDFDocumentRef?
{
guard let pdfPath = NSBundle.mainBundle().pathForResouce("nac_06", ofType:"pdf") else { return nil }
guard let data = NSData(contentsOfFile:pdfPath) else { return nil }
return GPDFDocumentCreateWithProvider(CGDataProviderCreateWithCFData(data))
}
All explicit type annotations are syntactic sugar and not needed.
Edit:
In your particular case you need only to check if the file exists and even this – the file is missing – is very unlikely in iOS. Another benefit is to be able to return a non-optional PDFDocument.
func loadPDF() -> CGPDFDocumentRef
{
guard let pdfPath = NSBundle.mainBundle().pathForResource("nac_06", ofType:"pdf") else {
fatalError("file nac_06.pdf does not exist")
}
let data = NSData(contentsOfFile:pdfPath)
return CGPDFDocumentCreateWithProvider(CGDataProviderCreateWithCFData(data!))!
}
I assume that you also don't want to continue with the execution of the function if pdfPath or data is nil. In this case, guard would be the best choice:
guard let pdfPath = NSBundle.mainBundle().pathForResouce("nac_06", ofType:"pdf") else {
// eventually also report some error
return
}
guard let data = NSData(contentsOfFile:pdfPath) else {
// eventually also report some error
return
}
// at this point you have a valid data object
You could also combine this into a single guard statement, to reduce the code duplication, you'll loose however in this case the possibility to know which of the two failed.
guard let pdfPath = NSBundle.mainBundle().pathForResource("nac_06", ofType:"pdf"),
data = NSData(contentsOfFile:pdfPath) else {
// eventually also report some error
return
}
There is two ways to achieve this.
Extend NSData class and create your own convenience init? method
Use the guard statement
I prefer the second method:
func getPDF(path : String?) -> CGPDFDocumentRef?
{
guard let filePath = path,
data = NSData(contentsOfFile: filePath),
pdf = GPDFDocumentCreateWithProvider(CGDataProviderCreateWithCFData(data)) else
{
return nil
}
return pdf
}
Call the method like:
let doc = getPDF(path : NSBundle.mainBundle().pathForResouce("nac_06", ofType:"pdf"))
You could do something fancy by defining a custom operator to deal with this situation. For example:
infix operator ^> {associativity left precedence 150}
func ^><T, U>(arg: T?, f: T->U?) -> U?{
if let arg = arg {
return f(arg)
} else {
return nil
}
}
The operator takes an optional left-side argument and a function that takes a non-optional and returns another optional as a right-side argument.
You could then write your code like this:
let pdfPath = NSBundle.mainBundle().pathForResource("nac_06", ofType:"pdf")
//the line below needs a NSData extension
let data = pdfPath ^> NSData.fileContents
let doc = data ^> CGDataProviderCreateWithCFData ^> CGPDFDocumentCreateWithProvider
//pageView.pdf is optional, nicely this function accepts the document as an optional
pageView.pdfPage = CGPDFDocumentGetPage(doc, 1)
Note that for this to work you need to add an extension to NSData, as you cannot map the init(contentsOfFile:) initializer to a generic function that can be passed to ^>.
extension NSData {
class func fileContents(path: String) -> NSData? {
return NSData(contentsOfFile: path)
}
}
The usage of the ^> operator reverts however the order you write the function names, if you prefer having the function names in the same order as the original code, you can add a reversed operator that does the same thing:
infix operator ^< {associativity right precedence 150}
func ^< <T, U>(f: T->U?, arg: T?) -> U?{
if let arg = arg {
return f(arg)
} else {
return nil
}
}
let pdfPath = NSBundle.mainBundle().pathForResource("nac_06", ofType:"pdf")
let data = NSData.fileContents ^< pdfPath
let doc = CGPDFDocumentCreateWithProvider ^< CGDataProviderCreateWithCFData ^< data
//pageView.pdf is optional, nicely this function accepts the document as an optional
pageView.pdfPage = CGPDFDocumentGetPage(doc, 1)