swiftoption-typeforced-unwrapping

UIGraphicsGetImageFromCurrentImageContext() is giving me a chain reaction of forced unwrapped optionals I don't want in Swift


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 

Solution

  • 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
        }
    }