1

I'm confused about using UIGraphicsGetImageFromCurrentImageContext() in Swift, making me force unwrap it, even though it is defined with let. Adding in the ? or ! is making my code look messy and making me change everything after it. I'd like to not require this when defining scaledImage.

let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()

let scaledCIImage:CIImage = (scaledImage?.ciImage)! // makes me force upwrap

// gives error 'Value of optional type 'CIFilter?' not unwrapped; did you mean to use '!' or '?'?'
let filter = CIFilter(name: "CIAffineTile", withInputParameters:[kCIInputImageKey:scaledCIImage]).outputImage 
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Chewie The Chorkie
  • 4,896
  • 9
  • 46
  • 90

2 Answers2

1

There are plenty of legitimate uses of ! if you know when to use it properly.

From the documentation for UIGraphicsGetImageFromCurrentImageContext:

If the current context is nil or was not created by a call to UIGraphicsBeginImageContext(_:), this function returns nil.

Since you are using UIGraphicsBeginImageContext and your current context isn't nil, then UIGraphicsGetImageFromCurrentImageContext() won't return a nil so it is safe to force-unwrap.

let scaledImage = UIGraphicsGetImageFromCurrentImageContext()!

As for creating the CIImage, the CIFilter, and the file CIImage, safely unwrap using if let.

if let scaledCIImage = scaledImage.ciImage,
   let filter = CIFilter(name: "CIAffineTile", withInputParameters:[kCIInputImageKey:scaledCIImage]),
   let outputImage = filter.outputImage {
    // Do what you need with outputImage
}

Update based on your comments:

Given that scaledmage.ciImage is nil and you want to try to create a CIImage from the cgImage, you can update the chain of if let to:

if let cgImage = scaledImage.cgImage {
    let scaledCIImage = CIImage(cgImage: cgImage)

    if let filter = CIFilter(name: "CIAffineTile", withInputParameters:[kCIInputImageKey:scaledCIImage]),
       let outputImage = filter.outputImage {
        // Do what you need with outputImage
    }
}
rmaddy
  • 314,917
  • 42
  • 532
  • 579
  • Sorry this is such a mess. I just found out the scaledImage.ciImage is nil, so it must be a CGImage? But if I use if let scaledCIImage = CIImage(cgImage: scaledCGImage.cgImage) it's complaining "Value of optional type 'CGImage?' not unwrapped; did you mean to use '!' or '?'?" And I can't find any way out. – Chewie The Chorkie Jun 08 '18 at 20:34
1

Adding in the ? or ! is making my code look messy and making me change everything after it.

Well, whether it's messy is quite subjective. Swift is designed this way to make you think about each optional you unwrap carefully. By doing this, you can avoid lots of those "unexpectedly found nil while unwrapping an optional value" errors.

Methods and properties return nil for a reason. Usually the reason is that an error occurred, the thing couldn't be done, or there is no valid value for a property. You should think about these problems - "what should my method do if this method returns nil?"

If you want to do something else when the value is nil (i.e. it being nil is not fatal to your logic), use a if let statement. e.g.

if let scaledCIImage = scaledImage?.ciImage {
    // deal with the scaledCIImage
} else {
    // do something else to remedy. The method can still continue running
}

If your method couldn't continue if the value is nil, then you can consider writing a guard statement:

guard let scaledCIImage = scaledImage?.ciImage else { 
    // you need to return some value or call "fatalError()" here
}

If you are absolutely sure that the value could not be nil, force unwrap it:

let scaledCIImage = scaledImage!.ciImage!

For the second error, you can fix it like:

// with force unwrapping
let filter = CIFilter(name: "CIAffineTile", withInputParameters:[kCIInputImageKey:scaledCIImage])!.outputImage!

// if let
if let filter = CIFilter(name: "CIAffineTile", withInputParameters:[kCIInputImageKey:scaledCIImage])?.outputImage {
    // ...
}

// guard let
guard let filter = CIFilter(name: "CIAffineTile", withInputParameters:[kCIInputImageKey:scaledCIImage])?.outputImage else {
    // ...
}

You can chain all of these together in a big if let or guard let statement like rmaddy has shown.

Sweeper
  • 213,210
  • 22
  • 193
  • 313