CGImageDestinationFinalize fails, sometimes crashes - ios

I'm trying to save some photos after capture. But while saving CGImageDestinationFinalize fails & mostly crashes.
let cgImage: CGImage...
let data: CFMutableData = CFDataCreateMutable(nil, 0)
// Older phones can't save to HEIC, so the fallback is needed.
let destination
= CGImageDestinationCreateWithData(data, "public.heic" as CFString, 1, nil)
?? CGImageDestinationCreateWithData(data, kUTTypeJPEG as CFString, 1, nil)!
CGImageDestinationAddImage(destination, cgImage, metadata)
NSLog(log: "Check #1") // This line executes
let _ = CGImageDestinationFinalize(destination) // Crashes here
NSLog(log: "Check #2") // not executed
PHPhotoLibrary.save(photo: data as Data, success: success)
It crashes with:
Message from debugger: Terminated due to memory issue
I tested it on iPhone7+ & iPhoneXS, it's working. But crashing on my iPhoneX. All of them running iOS12.2.
While this answer says it was a known issue in iOS 9 that has been resolved in iOS 10.
It does not tell the reason or give me a stack trace. Any ideas what might be wrong?

Related

Issue with CIQRCodeGenerator in CIFilter

I am updating existing app to support iOS 12 and strange issue with CIFilter appears.
This is simple class with one function generate():
class QRGenerator {
static func generate(from string: String) -> UIImage? {
let context = CIContext()
let data = string.data(using: String.Encoding.ascii)
if let filter = CIFilter(name: "CIQRCodeGenerator") {
filter.setValue(data, forKey: "inputMessage")
let transform = CGAffineTransform(scaleX: 7, y: 7)
if let output = filter.outputImage?.transformed(by: transform), let cgImage = context.createCGImage(output, from: output.extent) {
return UIImage(cgImage: cgImage)
}
}
return nil
}
}
This class work perfectly until iOS 12. Now in line if let filter = CIFilter(name: "CIQRCodeGenerator") I always receive nil.
I have spend some time on the Apple documentation but do not find any useful information about this issue.
I know very little about CIFilters, but may be you could try to get all possible filter names:
let allFiltersNames = CIFilter.filterNames(inCategories: nil)
I couldn't spot one that matches CIQRCodeGenerator but may be there is some other filter with a different name that would meet your needs.
It happens to me too, I also tried generating the QR with an external library like QRcode https://github.com/aschuch/QRCode but the image of the QR is always nil.
It's nil when I install the app in emulators with iOS 12.0 (16A5308d) from Xcode 10.0 beta 2.
But when I install it in a physical device iPhone SE with iOS 12 beta 4 (16A5339e) from the same Xcode (10.0 beta 2), the QR is generated with no problem at all.
I also tried with an emulated iPhone SE with iOS 11.4 (15F79) from the same Xcode (10.0 beta 2) and works perfect.

Getting “JPEGDecompressSurface : Picture decode failed:” on creating UIImage from JPG raw data on iOS 10

Since update to iOS 10, I'm getting the following error when creating UIImage from NSMutableData:
JPEGDecompressSurface : Picture decode failed:e000....."
There is no strange or not normal behaviour resulting, but when I debug my application I see the error quite each time I create the Image.
Here is the code, where I create the image from downloaded data using GCDAsynchSocket class:
NSData *imgDataToGen = [NSData dataWithData:imgBuffer];
UIImage *img = [[UIImage alloc] initWithData:imgDataToGen];
[_delegate client:self didReceiveImage:img];
The buffer imgBuffer is a NSMutableData-Object, which holds the downloaded image data. When the download is complete the data is converted into a picture and passed to the main GUI by firing the custom delegate method. In the main GUI then the image is set in image View.
Once an image is processed, the buffer is cleared.
I tried different things, like forcing the decoding of the UIImage already in background thread, but I get always same error. The image is rendered always in right way, so I really don't understand the error message.
I tried on iPhone 5c with iOS 10.0.2 and on Simulator 5s with iOS 9.2 and 10.0.2.
On iPhone 5c I get the error, on simulator not.
How can I fix this error, or can this type of error be ignored?
I had the same problem and solved.
In my case, I didn't notice but the conversion from Data to UIImage and the saving was performed in the main thread, once I dispatched it async to the global queue with background QOS priority and only the refresh of the GUI UIImage I dispatched back to main queue, the error stop showing in the debugger.
I hope it helps you.
Example:
fileprivate func downloadFromURL(urlString:String, callback:((_ image:UIImage?, _ error: Error?)->Void)?) {
var image: UIImage?
guard let url = URL(string: urlString) else {
callback!(nil, iError.RuntimeError(reason: "Wrong URL for image".localized))
return
}
DispatchQueue.global(qos: .background).async {
do {
let data = try Data(contentsOf: url)
image = UIImage(data: data)
DispatchQueue.main.async {
callback!(image,nil)
return
}
} catch {
print(error.localizedDescription)
callback!(nil, iError.RuntimeError(reason: "Data error for image".localized))
return
}
}
}

UIImage Download Returning nil and Crashing App (Swift)

I have an image url string:
var remoteImage: String = "http://server.com/wall-e.jpg"
I then construct a UIImage to download on a separate thread using Grand Central Dispatch with remoteImage as the NSURL string parameter:
let getImage = UIImage(data: NSData(contentsOfURL: NSURL(string: remoteImage)!)!)
When it is finished and I return back to the main thread, I have it save internally:
UIImageJPEGRepresentation(getImage, 1.0).writeToFile(imagePath, atomically: true)
On Wi-fi and LTE it downloads fine, but when testing edge cases such as on an Edge network (no pun intended), I inconsistently get the error:
fatal error: unexpectedly found nil while unwrapping an Optional value
Now I thought I would be safe by making sure that it wasn't nil by adding in:
if getImage != nil { ... }
But it didn't seem to make a difference. It still gets the error and highlights the let getImage as written above. What am I doing wrong here? Should I be checking nil in a different manner or method?
The error, does, in fact lie on the line:
let getImage = UIImage(data: NSData(contentsOfURL: NSURL(string: remoteImage)!)!)
The reason is that it's not the UIImage that is initially returning nil, it is probably NSData returning nil. You could check if NSData is returning nil, and then create the UIImage object instead.
EDIT: What the particular line of code is doing is it is assuming that NSData is always returning a non-nil value, which may not be true when you are not connected. When you're not connected, it gets a nil value, which you are trying to say will never be a nil value using the exclamation mark (!).
I suggest you read further on how Swift works. For this particular example, take a look at what the exclamation marks actually mean in Swift: What does an exclamation mark mean in the Swift language?
I would recommend you to use AsyncRequest to fetch and download the image and saved it locally.
As you didn't posted any of code of your problem.
So i am posting a sample working for me.
Sample for downloading and saving image locally
var url = NSURL(string : "http://freedwallpaper.com/wp-content/uploads/2014/09/Tattoo-Girl.jpg")
let urlrequest = NSURLRequest(URL: url!)
NSURLConnection.sendAsynchronousRequest(urlrequest, queue: NSOperationQueue.mainQueue(), completionHandler: {
response ,data , error in
if error != nil
{
println("error occurs")
}
else
{
let image = UIImage(data: data)
/* Storing image locally */
var documentsDirectory:String?
var paths = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)
println(paths)
if paths.count > 0{
documentsDirectory = paths[0] as? String
var savePath = documentsDirectory! + "/bach.jpg"
NSFileManager.defaultManager().createFileAtPath(savePath, contents: data, attributes: nil)
self.bach.image = UIImage(named: savePath)
}
}
})
}

Unable to edit screenshots, performChanges block fails

I'm developing an app that allows users to edit photos using PhotoKit. I was previously saving the edited photo to disk as a JPEG. I would like to avoid converting to JPEG and have implemented the modifications in order to do that. It works great for photos taken with the camera, but if you try to edit a screenshot, the PHPhotoLibrary.sharedPhotoLibrary().performChanges block will fail and log The operation couldn’t be completed. (Cocoa error -1.). I am not sure why this is causing the performChanges block to fail, what have I done wrong here?
I've created a sample app available to download that demonstrates the problem, and I've included the relevant code below. The app attempts to edit the newest photo in your photo library. If it succeeds it will prompt for access to edit the photo, otherwise nothing will happen and you'll see the console log. To reproduce the issue, take a screenshot then run the app.
Current code that works with screenshots:
let jpegData: NSData = outputPhoto.jpegRepresentationWithCompressionQuality(0.9)
let contentEditingOutput = PHContentEditingOutput(contentEditingInput: self.input)
var error: NSError?
let success = jpegData.writeToURL(contentEditingOutput.renderedContentURL, options: NSDataWritingOptions.AtomicWrite, error: &error)
if success {
return contentEditingOutput
} else {
return nil
}
Replacement code that causes screenshots to fail:
let url = self.input.fullSizeImageURL
let orientation = self.input.fullSizeImageOrientation
var inputImage = CIImage(contentsOfURL: url)
inputImage = inputImage.imageByApplyingOrientation(orientation)
let outputPhoto = createOutputImageFromInputImage(inputImage)!
let originalImageData = NSData(contentsOfURL: self.input.fullSizeImageURL)!
let imageSource = CGImageSourceCreateWithData(originalImageData, nil)
let dataRef = CFDataCreateMutable(nil, 0)
let destination = CGImageDestinationCreateWithData(dataRef, CGImageSourceGetType(imageSource), 1, nil) //getType automatically selects JPG, PNG, etc based on original format
struct ContextStruct {
static var ciContext: CIContext? = nil
}
if ContextStruct.ciContext == nil {
let eaglContext = EAGLContext(API: .OpenGLES2)
ContextStruct.ciContext = CIContext(EAGLContext: eaglContext)
}
let cgImage = ContextStruct.ciContext!.createCGImage(outputPhoto, fromRect: outputPhoto.extent())
CGImageDestinationAddImage(destination, cgImage, nil)
if CGImageDestinationFinalize(destination) {
let contentEditingOutput = PHContentEditingOutput(contentEditingInput: self.input)
var error: NSError?
let imageData: NSData = dataRef
let success = imageData.writeToURL(contentEditingOutput.renderedContentURL, options: .AtomicWrite, error: &error)
if success {
//it does succeed
return contentEditingOutput
} else {
return nil
}
}
The problem happens due to the fact that adjusted photos are always saved as JPG files, and screenshots are in fact PNG files.
It occurred to me while I was debugging your sample project and saw the in the PhotoEditor, contentEditingOutput.renderedContentURL is a URL to a JPG, while if you examine the result of CGImageSourceGetType(imageSource) it is clear the it's a PNG (returns a PNG UTI: public.png).
So I went and read the documentation for renderedContentURL which states that if editing a photo asset, the altered image is written in JPEG format - which clearly won't work if your image is a PNG. This leads me to think that Apple don't support editing PNG files or don't want you to. Go figure..

UIDocumentInteractionController always fails

I'm trying to use the UIDocumentInteractionController. I've checked everything I can think off and everything appears to have all the data that is needed.
When calling presentOpenInMenuFromRect(inView:, animated:) the Bool that is returned is always false. My understanding is that its because there aren't any apps that support the UTI. I've tried using a flat out PNG and still nothing. Whats interesting is why it wouldn't work on my iPhone which has plenty of apps that support loading images via the UIDocumentInteractionController, but here is the kicker. This code was working fine before I upgraded the project to Swift 1.2.
Am I overlooking something now? I've checked the docs and couldn't find anything that I was missing.
Note: I have tested this on a device and in the simulator and both return the same.
let image = UIImage(named: "screenshot")
if let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) {
if paths.count > 0 {
if let dirPath = paths[0] as? String {
let path = dirPath.stringByAppendingPathComponent("screenshot.ig") // igo
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), { () -> Void in
if let image = image {
NSFileManager.defaultManager().removeItemAtPath(path, error: nil)
if UIImagePNGRepresentation(image).writeToFile(path, atomically: true) {
}
}
})
if let url = NSURL(fileURLWithPath: path) {
let documentController = UIDocumentInteractionController(URL: url)
documentController.UTI = "com.instagram.photo" // com.instagram.exclusivegram
documentController.annotation = ["InstagramCaption": ""]
if !documentController.presentOpenInMenuFromRect(sender.bounds, inView: self.view, animated: true) {
println("Can't do it")
}
}
}
}
}
I decided to go with the presentOptionsMenuFromRect(:, inView:, animated:) which does what I am trying to accomplish. No idea why presentOpenInMenuFromRect(: inView:, animated:) decided to break down, but I did mange to get it all working.
The problem is you are writing your file asynchronously on a background queue, but do not wait before opening your interaction controller. So when it attempts to open your file, it cannot find it because it hasn't been written just yet. You need to let the write operation succeed, then attempt to open the interaction controller.
Change your code like so:
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), { () -> Void in
if let image = image {
NSFileManager.defaultManager().removeItemAtPath(path, error: nil)
if UIImagePNGRepresentation(image).writeToFile(path, atomically: true) {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
if let url = NSURL(fileURLWithPath: path) {
let documentController = UIDocumentInteractionController(URL: url)
documentController.UTI = "com.instagram.photo" // com.instagram.exclusivegram
documentController.annotation = ["InstagramCaption": ""]
if !documentController.presentOpenInMenuFromRect(sender.bounds, inView: self.view, animated: true) {
println("Can't do it")
}
}
}
}
}
})
I had the same problem on an iPad which was pretty much right out of the box. I couldn't open txt or png files (the two tests I ran). presentOpenInMenuFromRect always returned NO.
I found that the accepted answer of using presentOptionsMenuFromRect did launch that window, but it wasn't precisely what I wanted. (It could be what some of you will want though).
It turned out that my device simply didn't have an app associated with either of these types by default! (How ridiculous is that?) I assumed they were so common that would never be an issue.
So, I randomly installed the first free file manager app that I found in the app store (USB Disk Free was what I picked). Then, my device gave me the option to open either of those types with that app.

Resources