CKAsset screwing up file url in Xcode 8 beta 6 / Swift 3 - swift

I am trying to upload an UIImage to CloudKit in two steps:
get a file URL for my UIImage
get an CKAsset for my file URL
However, when debugging the CKAsset I create in the second step I noticed that the file path to my image doesn't exist and therefore CloudKit is unable to save the asset!
Here is my code:
// thumbnail is my UIImage
if let thumbnailURL = thumbnail.urlOf() {
self.thumbnailAsset = CKAsset(fileURL: thumbnailURL)
self.record["thumbnail"] = thumbnailAsset
} else {
NSLog("Could not create thumbnail asset!")
}
extension UIImage {
func urlOf() -> URL? {
do {
let imageData = UIImageJPEGRepresentation(self, 1)
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let documentDirectory = paths[0] as String
let myFilePath = NSURL(fileURLWithPath: documentDirectory).appendingPathComponent("thumbnail.jpg")
try imageData?.write(to: myFilePath!, options: .atomicWrite)
let url = myFilePath! as URL
return url
} catch {
NSLog("Could not save thumbnail to local directory!")
return nil
}
}
}
Any idea what is going on here? Is this a regression in beta 6 or is my code wrong?
Thanks for feedback!
-- Mark
Here some lldb statements:
(lldb) po thumbnailURL
▿ file:///Volumes/Work/Users/mark/Library/Developer/CoreSimulator/Devices/B1F9E45D-2A75-454F-AC56-37186428E833/data/Containers/Data/Application/E02EE6DF-17E4-4809-8978-339E76ED1EB3/Documents/picture
(lldb) po self.record["thumbnail"]
▿ Optional<CKRecordValue>
(lldb) po self.record["thumbnail"]!
<CKAsset: 0x6100001e8400; path=~/Documents/picture, UUID=BF838CB7-E828-4CD8-8517-911E25818088>

Related

loadImageFromDisk - function modyfication swift

im using the below code to load a downloaded image from disk, when the file exists I can load it as image. If it doesn't image returns as nil.
How could I modify the code to replace return nil with loading a placeholder image from my asset folder called default.png. So instead of returning nil, there is always an image return, either downloaded one that's trying to load or if it doesn't exist, my own asset image.
func loadImageFromDiskWith(fileName: String) -> UIImage? {
let documentDirectory = FileManager.SearchPathDirectory.documentDirectory
let userDomainMask = FileManager.SearchPathDomainMask.userDomainMask
let paths = NSSearchPathForDirectoriesInDomains(documentDirectory, userDomainMask, true)
if let dirPath = paths.first {
let imageUrl = URL(fileURLWithPath: dirPath).appendingPathComponent(fileName)
let image = UIImage(contentsOfFile: imageUrl.path)
return image
}
return nil
}
You can do as below -
func loadImageFromDiskWith(fileName: String) -> UIImage {
let documentDirectory = FileManager.SearchPathDirectory.documentDirectory
let userDomainMask = FileManager.SearchPathDomainMask.userDomainMask
let paths = NSSearchPathForDirectoriesInDomains(documentDirectory, userDomainMask, true)
var image = UIImage(named: "default") // Make sure there must be "default.png" in your main bundle.
if let dirPath = paths.first {
let imageUrl = URL(fileURLWithPath: dirPath).appendingPathComponent(fileName)
if FileManager.default.fileExists(atPath: imageUrl.path) { //Check here for file existence. It won't crash.
image = UIImage(contentsOfFile: imageUrl.path)
}
}
return image
}

File Directory Changing Every Reboot Swift iOS

I am currently saving images a user selects from their photo library & saving the url to User Defaults.
The issue I am currently facing is whenever my app reboots, such as an Xcode re-launch the URL I am saving to User Defaults is no longer valid. From my understanding/researching I need to save a relative URL for the image; however I am not having any luck doing this correctly. This occurs with both simulator & actual device.
From my UIImagePicker here are my steps:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
guard let image = info[.originalImage] as? UIImage else { return }
let savingImage : SavingImage = SavingImage()
let ourStringedUrl : String = savingImage.saveImageDocumentDirectory(image: image, imageName: image.description)
}
From SavingImage, here is the saving of the photo itself
func getDirectoryPath() -> NSURL {
let path = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString).appendingPathComponent("AllImages")
let url = NSURL(string: path)
return url!
}
func saveImageDocumentDirectory(image: UIImage, imageName: String) -> String {
let fileManager = FileManager.default
let path = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString).appendingPathComponent("AllImages")
if !fileManager.fileExists(atPath: path) {
try! fileManager.createDirectory(atPath: path, withIntermediateDirectories: true, attributes: nil)
}
let url = NSURL(string: path)
let imagePath = url!.appendingPathComponent(imageName)
let urlString: String = imagePath!.absoluteString
let imageData = image.jpegData(compressionQuality: 1)
fileManager.createFile(atPath: urlString as String, contents: imageData, attributes: nil)
return urlString as String
}
& Finally here is how I am loading the image itself:
func getImageFromDocumentDirectory(imageName : String) -> UIImage {
let fileManager = FileManager.default
var ourImage : UIImage!
let imagePath = "\(imageName)"
let urlString: String = imagePath
if fileManager.fileExists(atPath: urlString) {
ourImage = UIImage(contentsOfFile: urlString)
} else {
if ourImage == nil {
ourImage = UIImage(named: "defaultImage.png")
}
}
return ourImage
}
Where am I going wrong with the loading aspect? How exactly should I be saving the URL?
The URL that is being saved looks something similar to the following:
//var/mobile/Containers/Data/Application/17C16D0D-1BFA-44F5-A6BD-18DFAEA051E0/Documents/AllImages/%3CUIImage:0x280a393b0%20anonymous%20%7B3024,%204032%7D%3E
But will come up as nil upon loading the image on reboot.
The new directory will be created every time you reboot your simulator or every time you install new build to device. (Uninstall previous and install new). iOS does not give us permission to select the directory. As you install new application it will create its sandbox. Please make sure you check your directory on the same build.
An alternative might be to use iCloud Drive and upload your image there. The iCloud drive is linked to an user's iCloud account, and therefore should persist in case of an app update.
Here's a great tutorial to get started: https://theswiftdev.com/2018/05/17/how-to-use-icloud-drive-documents/
Here's a link to the docs: https://developer.apple.com/icloud/icloud-drive/

Save UIImage array to disk

I have a UIImage array that I'm trying to save to disk (documents directory). I've converted it to Data so it can be saved but I can't figure out the correct/best way to save that [Data] to disk.
(arrayData as NSArray).write(to: filename, atomically: false) doesn't seem right or there seems like there is some better way.
Code so far:
// Get the new UIImage
let image4 = chartView.snapShot()
// Make UIImage into Data to save
let data = UIImagePNGRepresentation(image4!)
// Add Data image to Data Array
arrayData.append(data!)
// Get Disk/Folder
let filename = getDocumentsDirectory().appendingPathComponent("images")
// This is how I'd Write Data image to Disk
// try? data?.write(to: filename)
// TODO: Write array Data images to Disk
(arrayData as NSArray).write(to: filename, atomically: false)
To save your images to the document directory, you could start by creating a function to save your image as follows. The function handles creating the image name for you and returns it.
func saveImage(image: UIImage) -> String {
let imageData = NSData(data: UIImagePNGRepresentation(image)!)
let paths = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
let docs = paths[0] as NSString
let uuid = NSUUID().uuidString + ".png"
let fullPath = docs.appendingPathComponent(uuid)
_ = imageData.write(toFile: fullPath, atomically: true)
return uuid
}
Now since you want to save an array of images, you can simply loop and call the saveImage function:
for image in images {
saveImage(image: image)
}
If you prefer to name those images yourself, you can amend the first function to the following:
func saveImage(image: UIImage, withName name: String) {
let imageData = NSData(data: UIImagePNGRepresentation(image)!)
let paths = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
let docs = paths[0] as NSString
let name = name
let fullPath = docs.appendingPathComponent(name)
_ = imageData.write(toFile: fullPath, atomically: true)
}
When you save an image as a UIImagePNGRepresentation as you are trying to do, please note that is does not save orientation for you. This means that when you attempt to retrieve the image, it might be titled to the left. You will need to redraw your image to make sure it has the right orientation as this post will explain how to save png correctly. However, if you save your image with UIImageJPEGRepresentation you will not have to worry about all that and you can simply save your image without any extra effort.
EDITED TO GIVE ARRAY OF IMAGES
To retrieve the images you have saved, you can use the following function where you pass an array of the image names and you get an array back:
func getImage(imageNames: [String]) -> [UIImage] {
var savedImages: [UIImage] = [UIImage]()
for imageName in imageNames {
if let imagePath = getFilePath(fileName: imageName) {
savedImage.append(UIImage(contentsOfFile: imagePath))
}
}
return savedImages
}
Which relies on this function to work:
func getFilePath(fileName: String) -> String? {
let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
var filePath: String?
let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
if paths.count > 0 {
let dirPath = paths[0] as NSString
filePath = dirPath.appendingPathComponent(fileName)
}
else {
filePath = nil
}
return filePath
}
You can below code to stored image into local.
let fileManager = NSFileManager.defaultManager()
let paths = (NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as NSString).stringByAppendingPathComponent("ImageName.png")
let image = YOUR_IMAGE_TO_STORE.
let imageData = UIImageJPEGRepresentation(image!, 0.5)
fileManager.createFileAtPath(paths as String, contents: imageData, attributes: nil)

How to share Image in Swift

I tried to share a image that I generate by my own. However when I tried to share the image it doesn't works well.
here is the code to share
func shareQR(){
//mainImage is the view that hold the image I want to share
let tempImage = view.mainImage.image
let imageToShare = [ tempImage ]
let activityViewController = UIActivityViewController(activityItems: imageToShare as! [UIImage], applicationActivities: nil)
activityViewController.popoverPresentationController?.sourceView = self.view
activityViewController.excludedActivityTypes = [ UIActivityType.airDrop ]
self.present(activityViewController, animated: true, completion: nil)
}
So I tried to debug and the result is my tempImage value is the same with the one I want to share. And then I tried to hardcode the image and become let tempImage = UIImage(named: "image.png")
and tap share button, and it works perfectly.
So I figured out, did I have to save my image to local file first and then called it ?
If yes how ? I tried use this code but failed.
// Create path.
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let filePath = "\(paths[0])/temp.png"
// Save image.
UIImagePNGRepresentation(image!)?.write(to: filePath)
the error is : cannot convert value of type 'String' to expected
argument type 'URL' in this line
UIImagePNGRepresentation(tempImage!)?.write(to: filePath)
Or maybe I dont need to save image? how ?
Thank you
It seems like your variable image is a String variable instead of a UIImage (hence the error that you have posted). To confirm this theory, add the following line in your code
print(tempImage)
after
let tempImage = view.mainImageimage
If it prints the image name, then your variable is a String variable. If it prints something along the lines of UIImage, then it's an actual image and we can further investigate the issue. Let me know what you get in result
Edit:
To store a file locally, use the following code
if let data = UIImagePNGRepresentation(tempImage) {
let filename = getDocumentsDirectory().appendingPathComponent("copy.png")
try? data.write(to: filename)
}
func getDocumentsDirectory() -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let documentsDirectory = paths[0]
return documentsDirectory
}

iOS8 Photo Extension says Unable to save changes

I am trying to create an iOS Photo Extension which compresses images, and this is finishContentEditingWithCompletionHandler function:
func finishContentEditingWithCompletionHandler(completionHandler: ((PHContentEditingOutput!) -> Void)!) {
dispatch_async(dispatch_get_global_queue(CLong(DISPATCH_QUEUE_PRIORITY_DEFAULT), 0)) {
atomically: true)
let url = self.input?.fullSizeImageURL
if let imageUrl = url {
var fullImage = UIImage(contentsOfFile:
imageUrl.path!)
//Just compresses the image
let renderedJPEGData =
UIImageJPEGRepresentation(fullImage, self.comprestionRatioFloat)
var currentFilter = "FakeFilter"
renderedJPEGData.writeToURL(
output.renderedContentURL,
atomically: true)
let archivedData =
NSKeyedArchiver.archivedDataWithRootObject(
currentFilter)
output.adjustmentData = PHAdjustmentData(formatIdentifier:"MyApp.Ext", formatVersion:"1.0", data:archivedData)
}
completionHandler?(output)
// Clean up temporary files, etc.
}
}
When I test it on the device it says "Unable to Save Changes", Is there anything wrong?
Finally I've found out it's because the output image is almost the same as input. A little scaling down the output image fixed the problem.