I have function that receives image as DATA
I can convert it to:
let uiImage: UIImage = UIImage(data: thumb)!
But how do I set it to UNNotificationAttachment? It expects url...
let attachment = UNNotificationAttachment(identifier : "image", url: fileUrl, options: nil)
content.attachment = [attachment]
You need to save the image to the filesystem, for example to .cachesDirectory, and create UNNotificationAttachment using the file URL.
Related
When the user selects an item from their photo library, the app saves the UIImage to the app's directory using the following code:
let imageUUID = UUID()
let filename = getDocumentsDirectory().appendingPathComponent("\(imageUUID)")
guard let uiImage = newImage else { return}
if let jpegData = uiImage.jpegData(compressionQuality: 0.8) {
try? jpegData.write(to: filename, options: [.atomicWrite, .completeFileProtection])
}
How can I use the saved image in a view?
Thanks!
First, you should save the UUID somewhere so that you can read the file later. This could be in UserDefaults, a JSON file, CoreData, or whatever, depending on your needs.
Suppose you have the path to the file stored in filePath, you can create an Image like this:
Image(uiImage: UIImage(contentsOfFile: filePath) ?? UIImage())
This will use an empty image as fallback if it failed to read the file.
Following code convert UIImage to Image in SwiftUI:
let uiImage = UIImage(contentsOfFile: path) ?? UIImage() // Access it from your storage
let image = Image(uiImage: uiImage)
There are different UIImage initializer methods available, you can use any one of them to get UIImage instance.
https://developer.apple.com/documentation/swiftui/image
You will like this tutorial of Coredata with SwiftUI: https://www.raywenderlich.com/9335365-core-data-with-swiftui-tutorial-getting-started
I have Image cache mechanism in my app. I also need to show local notifications with images. I have a problem. When I try to set UNNotificationAttachment with an image, I get an image from my cache or, if an image doesn't exist, I download it and cache. Then I build a URL to Caches directory, but when I pass this URL to UNNotificationAttachment, I get an error: NSLocalizedDescription=Invalid attachment file URL. What do I make wrong?
if let diskUrlString = UIImageView.sharedImageCache.diskUrlForImageUrl(imageUrl) {
if let diskUrl = URL(string: diskUrlString) {
do {
res = try UNNotificationAttachment(identifier: imageUrlString, url: diskUrl, options: nil)
} catch (let error) {
print("error", error)
// Invalid attachment file URL
}
}
}
func diskUrlForImageUrl(_ imageUrl: URL) -> String? {
let urlRequest = URLRequest(url: imageUrl)
return ImageCache.cacheDirectory.appending("/\(ImageCache.imageCacheKeyFromURLRequest(urlRequest))")
}
static fileprivate var cacheDirectory: String = { () -> String in
let documentsDirectory = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).first!
let res = documentsDirectory.appending("/scAvatars")
let isExist = FileManager.default.fileExists(atPath: res, isDirectory: nil)
if !isExist {
try? FileManager.default.createDirectory(atPath: res, withIntermediateDirectories: true, attributes: nil)
}
return res
}()
I have found that if I add a prefix file:///private to diskUrlString, then URL builds like expected. But I didn't still understand, how to build the url without hardcoding this prefix. So now I can use both cache and UNNotificationAttachment!
The problem here is that you are using a path, not a URL. A path is a string, like "/var/log/foo.log". A URL is semantically more complex than a path. You need a URL that describes the location of the image file on the device file system.
Build a properly constructed URL to the image file and the attachment may work. The attachment may also need a type identifier hint to tell iOS what kind of data is in the file.
You do not have to use urls. You can use image data with UNNotificationAttachment.
Here is a sample code.
let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
let imageURL = URL(fileURLWithPath: paths.first!).appendingPathComponent("\(fileName).jpg")
let image = UIImage(contentsOfFile: imageURL.path)
let imageData = image?.pngData()
if let unwrappedImageData = imageData, let attachement = try? UNNotificationAttachment(data: unwrappedImageData, options: nil) {
content.attachments = [attachement]
}
I'm saving a UIImage to Core Data. So first, I convert it to NSData, then save it.
I need to get the URL for the image after it's saved. I'm doing this because I want to schedule a local notification with an attachment, and the only way to do it, AFAIK, is to with a URL.
Here is my code:
//my image:
var myImage: UIImage?
var imageData: NSData?
if let image = myImage {
imageData = UIImageJPEGRepresentation(image, 0.5)! as NSData
}
myEntity.setValue(imageData, forKey: "image")
And that's how I should add an attachment to the notification:
UNNotificationAttachment.init(identifier: String, url: URL>, options: [AnyHashable : Any]?)
I'm saving the image and scheduling the notification manually when the user taps on a button to save the image.
Please let me know if you need extra info.
You can't get the URL. If you configured this property to use external storage then yes, technically there could be a file URL. Maybe. But there's no documented way to get it, and anyway it might not exist after all-- because the external storage setting doesn't require Core Data to use external storage, it just allows it to do so.
If you didn't use that setting then there's never any URL since the image is saved as part of the SQLIte file.
If you need a file URL for the image, save the image to a file separately from Core Data and save the file name as an entity property. Then the file URL is wherever you saved the file.
And an implementation of how I saved it and then got the URL in practice when I had the same challenge:
Swift 5:
func getImageURL(for image: UIImage?) -> URL {
let documentsDirectoryPath:NSString = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString
let tempImageName = "tempImage.jpg"
var imageURL: URL?
if let image = image {
let imageData:Data = image.jpegData(compressionQuality: 1.0)!
let path:String = documentsDirectoryPath.appendingPathComponent(tempImageName)
try? image.jpegData(compressionQuality: 1.0)!.write(to: URL(fileURLWithPath: path), options: [.atomic])
imageURL = URL(fileURLWithPath: path)
try? imageData.write(to: imageURL!, options: [.atomic])
}
return imageURL!
}
Using this code, I extract an image from a Share Extension and I write it to a directory I created in an App Group.
let content = self.extensionContext!.inputItems[0] as! NSExtensionItem
let contentType = kUTTypeImage as String
for attachment in content.attachments as! [NSItemProvider] {
if attachment.hasItemConformingToTypeIdentifier(contentType) {
attachment.loadItem(forTypeIdentifier: contentType, options: nil) { data, error in
// from here
if error == nil {
let url = data as! NSURL
let originalFileName = url.lastPathComponent
if let imageData = NSData(contentsOf: url as URL) {
let img = UIImage(data:imageData as Data)
if let data = UIImagePNGRepresentation(img!) {
// write, etc.
}
}
}
}
Anything is working fine.
What I'd like to know is if it is possible to reduce some code: in particular, after if error == nil, I:
cast data to NSURL;
use NSURL to get a NSData;
use NSData to get a UIImage;
use UIImage to get a UIImagePNGRepresentation;
Aside from avoiding the creation of the imageData variable, isn't there a way to (safely) achieve the same goal with fewer steps?
First of all you need to use native Data and URL instead of NSData & NSURL also if you want to write file in DocumentDirectory then you can directly use that imageData no need to make UIImage object from it and then convert it to data using UIImagePNGRepresentation.
if let url = data as? URL, error == nil {
let originalFileName = url.lastPathComponent
if let imageData = try? Data(contentsOf: data) {
// write, etc.
var destinationURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
destinationURL.appendPathComponent("fileName.png")
try? imageData.write(to: destinationURL)
}
}
I am developing some tumblr client. And i want to download all photos in dashboard and save them to documentDirectory. Some of them is jpg some are gif. I have successfully download jpg images with this code:
do {
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let fileURL = documentsURL.appendingPathComponent(fname)
if let pngImageData = UIImagePNGRepresentation(myImage!) {
try pngImageData.write(to: fileURL, options: .atomic)
print(fname + " - saved")
}
} catch {print(fname + " - not saved") }
myImage is the image which is downloaded from URL. This code is work for jpg images but not good for gifs.
And lastly i read these files with this:
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let filePath = documentsURL.appendingPathComponent(fname).path
if FileManager.default.fileExists(atPath: filePath) {
return UIImage(contentsOfFile: filePath)!
}
It's work for jpg but not for gifs. when i try this writing/reading codes on gifs images, i got just image not animation.
Can you show me the way pls with an example will be awesome.
Thank you.
I personally used this tinnie tool for GIFs. Simply add the iOSDevCenters+GIF.swift file directly to your project and use it as shown below
1. Online URL
let imageURL = UIImage.gifImageWithURL(gifURL)
let yourImageView = UIImageView(image: imageURL)
2. Local Filename
let imageName = UIImage.gifImageWithName(imageName)
let yourImageView = UIImageView(image: imageName)
3. Using Data
let imageData = NSData(contentsOf: Bundle.main.url(forResource: "name", withExtension: ".gif"))
let imageData = UIImage.gifImageWithData(imageData)
let yourImageView = UIImageView(image: imageData)
Actually your first code is not working correctly either.
What you are receiving is the image. You do not need or want to translate it into some other kind of image (UIImagePNGRepresentation). Just save the data. Later, read the data directly into a UIImage.
I think there is no problem with downloading and you are just trying to present it as a png; instead try to present this saved data with this library, for example
Try this:
#import AssetsLibrary
let lib = ALAssetsLibrary()
let data = NSData(contentsOfURL: getCurrentGIFURL)
lib.writeImageDataToSavedPhotosAlbum(data, metadata: nil) { (url, err) in
}