Justin Sato
Justin Sato

Reputation: 574

"unexpectedly found nil while unwrapping an Optional value" when I try to get a CGImage by AVAssetImageGenerator

I'm trying to make a thumbnail image of UIImage from PHLivePhoto so that I can get a moment before or after the picture captured. I'm struggling with the error:

unexpectedly found nil while unwrapping an Optional value 

at

cgImage = try? imgGenerator.copyCGImageAtTime(CMTimeMake(avAsset.duration.value / 3, avAsset.duration.timescale), actualTime: nil)

I'm very new to swift2 syntax. please help me to get the thumbnail.

func makeThubnailsFromLivePhoto(livePhoto: PHLivePhoto) -> UIImageView {
    let assetResource = PHAssetResource.assetResourcesForLivePhoto(livePhoto)
    let avAsset = AVURLAsset(URL: NSURL(fileURLWithPath: assetResource[1].assetLocalIdentifier))
    var err: NSError? = nil
    let imgGenerator = AVAssetImageGenerator(asset: avAsset)
    var cgImage: CGImage?
    var imageView: UIImageView?
    cgImage = try? imgGenerator.copyCGImageAtTime(CMTimeMake(avAsset.duration.value / 3, avAsset.duration.timescale), actualTime: nil)
    let uiImage = UIImage(CGImage: cgImage!)
    imageView = UIImageView(image: uiImage)

    imageView!.frame = CGRect(x: 0, y: 0, width: self.view.bounds.width/2, height: self.view.bounds.height/2)
    return imageView!
}

Upvotes: 0

Views: 484

Answers (2)

Victor Sigler
Victor Sigler

Reputation: 23449

As @RobNapier said to you in his very good answer, is very improbable that your error resides in the following line:

cgImage = try? imgGenerator.copyCGImageAtTime(CMTimeMake(avAsset.duration.value / 3, avAsset.duration.timescale), actualTime: nil)

According to Apple:

You use try? to handle an error by converting it to an optional value. If an error is thrown while evaluating the try? expression, the value of the expression is nil.

Your error definitely is in this line:

let uiImage = UIImage(CGImage: cgImage!)

You can fix it using optional chaining like in the following way:

cgImage = try? imgGenerator.copyCGImageAtTime(CMTimeMake(avAsset.duration.value / 3, avAsset.duration.timescale), actualTime: nil)

if let cgImage = cgImage {
    let uiImage = UIImage(CGImage: cgImage)
    // do the rest of your code here to return the UIImage
}
else {
   // was an error and you need to return nil or something else
}

Or you can use the new guard statement like in the following way:

cgImage = try? imgGenerator.copyCGImageAtTime(CMTimeMake(avAsset.duration.value / 3, avAsset.duration.timescale), actualTime: nil)

guard let cgImage = cgImage else {
    // here you need to return nil or something else you want to know the caller of the function that there was an error
}

let uiImage = UIImage(CGImage: cgImage)
imageView = UIImageView(image: uiImage)

imageView!.frame = CGRect(x: 0, y: 0, width: self.view.bounds.width/2, height: self.view.bounds.height/2)
return imageView!

It's up to you what to use. I hope this help you.

Upvotes: 0

Rob Napier
Rob Napier

Reputation: 299605

Are you sure that's the line that raises the error? I would much more expect that it's this line:

    let uiImage = UIImage(CGImage: cgImage!)

That's going to crash any time there's an error creating the image. You either need to use do/try/catch here to catch the errors and deal with them, or you need to use guard-let to validate that cgImage is not nil. Any time you use !, you're saying "I bet my program's life that this is not nil."

Similarly, this line doesn't make sense:

    var imageView: UIImageView?

You later assign imageView from a constructor that cannot fail. But it causes you to use imageView!, which could be dangerous (it happens not to be in this case, but it's hard to know that). Avoid ! as much as you can.

In general, you should avoid this pattern of var x: Type? followed by x = .... Instead, use the pattern let x = ... Type inference is an important part of Swift.

Upvotes: 1

Related Questions