Saving Image and Then Loading It in Swift (Ios)

iOS: Open saved image inside the native Files app

After doing some research, I don't think this is unwanted behavior but more like behavior that Snapseed has subscribed to, as this behavior does not happen automatically.

However, there is some bug (I think) that does not let you unsubscribe from this behavior easily.

The place we need to look at is using LSSupportsOpeningDocumentsInPlace and CFBundleDocumentTypes, more on that here and here:

Here I created a small example to save an image from the app main bundle to the documents directory when the user taps a button using your code:

Absolutely no difference to your code

@objc
private func saveImage()
{
if let imageData
= UIImage(named: "dog")?.jpegData(compressionQuality: 1.0)
{
save(imageData: imageData,
toFolder: "image",
withFileName: "dog.jpeg")
}
}

func save(imageData: Data,
toFolder folderName: String,
withFileName fileName: String)
{
DispatchQueue.global().async
{
let manager = FileManager.default
let documentFolder = manager.urls(for: .documentDirectory,
in: .userDomainMask).last
let folder = documentFolder?.appendingPathComponent(folderName)
let file = folder?.appendingPathComponent(fileName)

do {
try manager.createDirectory(at: folder!,
withIntermediateDirectories: true,
attributes: [:])
if let file = file
{
try imageData.write(to: file)
print("file \(fileName) saved")
}
}
catch
{
print(error.localizedDescription)
}
}
}

If you add the following to your info.plist

UIFileSharingEnabled

LSSupportsOpeningDocumentsInPlace

This supports exposing your documents directory in the files app and after this everything works as normal:

Saving UIImage to documents directory showing in Files app iOS Swift

However, if you want your app to be part of a group that opens certain file formats, you modify your info.plist to add the CFBundleDocumentTypes to suggest that your app is able to open specific files, like images in our case.

To do that, we add the following to info.plist

CFBundleDocumentTypes


LSItemContentTypes

public.image

CFBundleTypeName
image
LSHandlerRank
Default


This would allow your app to be listed when some wants to share or open an image and by adding this, then we get the behavior shown in your gif which opens the app instead of the preview in the files app

Opening image inside my app from files app iOS Swift

Unfortunately, it seems once this behavior is assigned to your app, even if you remove the above above key from info.plist, this behavior persists.

The only way I could restore the original behavior was by using a fresh bundle identifier and only using these two keys in the info.plist in order to expose your documents directory to the Files app

UIFileSharingEnabled

LSSupportsOpeningDocumentsInPlace

I believe this is some sort of bug as there should be a simple way to unsubscribe from this behavior.

Hope this helps even though it does not fully solve your problem

Loading an image saved to the Documents Directory only works once, then never loads again

Never store a full path. An app's sandbox changes over time. Just store the file name. When you want to get the full path to the file, calculate it at runtime based on the stored filename and the current value for the Documents folder.

Loading/Downloading image from URL on Swift

Xcode 8 or later • Swift 3 or later

Synchronously:

if let filePath = Bundle.main.path(forResource: "imageName", ofType: "jpg"), let image = UIImage(contentsOfFile: filePath) {
imageView.contentMode = .scaleAspectFit
imageView.image = image
}

Asynchronously:

Create a method with a completion handler to get the image data from your url

func getData(from url: URL, completion: @escaping (Data?, URLResponse?, Error?) -> ()) {
URLSession.shared.dataTask(with: url, completionHandler: completion).resume()
}

Create a method to download the image (start the task)

func downloadImage(from url: URL) {
print("Download Started")
getData(from: url) { data, response, error in
guard let data = data, error == nil else { return }
print(response?.suggestedFilename ?? url.lastPathComponent)
print("Download Finished")
// always update the UI from the main thread
DispatchQueue.main.async() { [weak self] in
self?.imageView.image = UIImage(data: data)
}
}
}

Usage:

override func viewDidLoad() {
super.viewDidLoad()
print("Begin of code")
let url = URL(string: "https://cdn.arstechnica.net/wp-content/uploads/2018/06/macOS-Mojave-Dynamic-Wallpaper-transition.jpg")!
downloadImage(from: url)
print("End of code. The image will continue downloading in the background and it will be loaded when it ends.")
}

Extension:

extension UIImageView {
func downloaded(from url: URL, contentMode mode: ContentMode = .scaleAspectFit) {
contentMode = mode
URLSession.shared.dataTask(with: url) { data, response, error in
guard
let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
let mimeType = response?.mimeType, mimeType.hasPrefix("image"),
let data = data, error == nil,
let image = UIImage(data: data)
else { return }
DispatchQueue.main.async() { [weak self] in
self?.image = image
}
}.resume()
}
func downloaded(from link: String, contentMode mode: ContentMode = .scaleAspectFit) {
guard let url = URL(string: link) else { return }
downloaded(from: url, contentMode: mode)
}
}

Usage:

imageView.downloaded(from: "https://cdn.arstechnica.net/wp-content/uploads/2018/06/macOS-Mojave-Dynamic-Wallpaper-transition.jpg")


Related Topics



Leave a reply



Submit