I need to capture image from image picker controller and save image with its custom name and later on fetch all the images with their respective names .
try this
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let filePath = "\(paths[0])/MyImageName.png"
// Save image.
UIImagePNGRepresentation(image)?.writeToFile(filePath, atomically: true)```
I assume you already have the picker working in order to get an image from the gallery, and only wants to save and get it from the app folder.
I was able to make this with the following code:
Class ImagePersistance.swift
open class ImagePersistance: NSObject {
var fileManager: FileManager
var documentsURL: URL
var documentPath: String
public override init() {
self.fileManager = FileManager.default
self.documentsURL = self.fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!
self.documentPath = documentsURL.path
}
public func saveImage(image: UIImage?, name: String) throws {
if image != nil {
let filePath = documentsURL.appendingPathComponent("\(String(name)).png")
if let currentImage = image {
if let pngImageData = UIImagePNGRepresentation(currentImage) {
try pngImageData.write(to: filePath, options: .atomic)
}
}
}
}
public func getImage(name: String) -> UIImage? {
let filePath = documentsURL.appendingPathComponent("\(String(name)).png")
if FileManager.default.fileExists(atPath: filePath.path) {
if let image = UIImage(contentsOfFile: filePath.path) {
return image
}
}
return nil
}
}
How to use it:
Save image:
do {
try ImagePersistance().saveImage(image: IMAGE_HERE, name: "IMAGE_NAME")
catch {
print("image error")
}
Get saved image
let image:UIImage = ImagePersistance().getImage(name: "IMAGE_NAME")
Related
I have already copy the file absolute path and paste in simulator browser, the image can be opened. But the fileExists is fail, i dont know why..... Can anyone help
let defaultImage = "302C3FA1-E4E1-4CD8-B6DF-2FF4E4E24C11.jpeg"
loadImage(at: defaultImage)
func fileExists(at path: String) -> Bool {
return FileManager.default.fileExists(atPath: path)
}
func loadImage(at path: String) -> UIImage? {
let tempPath = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
let imagePath = "\(tempPath)\(path.trimmingCharacters(in: .whitespacesAndNewlines))"
guard fileExists(at: imagePath) else { return nil }
guard let image = UIImage(contentsOfFile: imagePath) else { return nil }
return image
}
You need split filename and extension filename.
If you use main bundle. you can follow this code
let stringPath = Bundle.main.path(forResource: "your_filename", ofType: "txt")
let urlPath = Bundle.main.url(forResource: "your_filename", withExtension: "txt")
or you can use my code.
func readConfigFromBundle(fileExtension: String) -> TCBConfigure? {
let bundle = Bundle.main
if let resPath = bundle.resourcePath {
do {
let dirContents = try FileManager.default.contentsOfDirectory(atPath: resPath)
let filteredFiles = dirContents.filter { $0.contains(fileExtension) }
for fileName in filteredFiles {
let sourceURL = bundle.bundleURL.appendingPathComponent(fileName)
let data: NSData? = NSData.init(contentsOf: sourceURL)
if let fileData = data {
// implement your logic
}
}
} catch {
// implement when error
}
}
return nil
}
I want to store a couple of images locally in my app on the user's device.
What I was using until now (it's still in development):
static func filePath(forKey key: String) -> URL? {
let fileManager = FileManager.default
guard let documentURL = fileManager.urls(for: .documentDirectory,
in: FileManager.SearchPathDomainMask.userDomainMask).first else { return nil }
return documentURL.appendingPathComponent(key + ".png")
}
static func savePhoto(imageKey: String) {
if let filePath = Helpers.filePath(forKey: imageKey) {
do {
try Constants.PHOTO_DATA.write(to: filePath, options: .atomic)
} catch {
print("error")
}
} else {
print(" >>> Error during saving photo. Filepath couldn't be created.")
}
}
static func getPhoto(imageKey: String) -> (image: UIImage, placeholder: Bool) {
if let filePath = Helpers.filePath(forKey: imageKey),
let fileData = FileManager.default.contents(atPath: filePath.path),
let image = UIImage(data: fileData) {
// Retrieve image from device
return (image, false)
}
return (UIImage(named: "placeholder")!, true)
}
Now, during testing I realized that it is not working (but I'm almost 100% sure it was working until now, strange..). It is changing the App's container directory upon every launch.
E.g.
Path:
/var/mobile/Containers/Data/Application/1F3E812E-B128-481C-9724-5E39049D6C81/Documents/D5F14199-CFBF-402A-9894-3487976C4C74.png
Restarting the app, then the path it gives (and where it does not find the image):
/var/mobile/Containers/Data/Application/0A9FCE45-1ED4-46EB-A91B-3ECD56E6A31B/Documents/D5F14199-CFBF-402A-9894-3487976C4C74.png
I read a bit and as far as I see it is 'expected' that it is not working, as the app's directory can change any time the user restarts the app. I should use bookmarkData of the URL class.
My problem is that I couldn't get it working with bookmarkData as I don't really see how should I use it, and couldn't understand its behavior based on some example codes/articles I found. Until now I was simply using URLs to store/retrieve the photo but now I should go with this bookmarkData which is a Data type, which confuses me.
I'm not sure what you want your code means, since both Helper and Constants.PHOTO_DATA are unknown. The code that will definitely will save a UIImage in the documents directory is here:
class ImageSaver {
private let imageStore = FileManager().urls(for: .documentDirectory, in: .userDomainMask).first
//Make this static variable to allow access from all objects without instantiating this class
static var shared : AuxiliaryObjects {
return AuxiliaryObjects()
}
/**
Declaration: save(image : UIImage, with fileName: String, and imageName: String?)
Description: This method saves the received image to the persistent store in the documents directory of the user.
- Parameter image: The UIImage object that must be stored in the documents directory.
- Parameter fileName: A string with the name under which the image must be stored.
- Parameter imageName: The name of the image if needed.
*/
func save(image: UIImage, with fileName: String, and imageName: String?) {
let fileStore = imageStore?.appendingPathComponent(fileName)
let imageData = UIImagePNGRepresentation(image)
do {
try imageData?.write(to: fileStore!)
} catch {
print("Couldn't write the image to disk.")
}
}
/**
Declaration: getImage(with fileName: String, with rectangle: CGRect) -> UIImage?
Description: This method retrieves the image with the specified file name and a given size.
- Parameter fileName: a string with the file name to retrieve.
- Parameter rectangle: the size of the image to return.
- Returns: UIImage?, the image retrieved from the documents directory.
*/
func getImage(with fileName: String, with rectangle: CGRect) -> UIImage? {
var returnImage : UIImage?
var imageRectangle = rectangle
do {
imageStoreArray = try FileManager.default.contentsOfDirectory(at: imageStore!, includingPropertiesForKeys: resourceKeys, options: .skipsHiddenFiles) as [NSURL]
} catch {
return returnImage
}
for url in imageStoreArray {
let urlString = url.lastPathComponent
if urlString == fileName {
let retrievedImage = UIImage(contentsOfFile: url.path!)
//When there is no size set, the original size image is returned
if (rectangle.size.height > 0) || (rectangle.size.width > 0) {
let imageWidth = retrievedImage?.size.width
let imageHeight = retrievedImage?.size.height
if imageWidth! > imageHeight!
{
//The picture is wider than it is high
imageRectangle.size.height *= (imageHeight! / imageWidth!)
} else {
imageRectangle.size.width *= (imageWidth! / imageHeight!)
}
UIGraphicsBeginImageContextWithOptions(imageRectangle.size, false, UIScreen.main.scale)
retrievedImage?.draw(in: imageRectangle)
returnImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
} else {
returnImage = retrievedImage
}
}
}
return returnImage
}
}
Let me know if this works for you.
Kind regards,
MacUserT
I'm trying to let the user take or select an image in an ImagePickerController, and I want to save a reference (as efficiently as possible) in my app to recall when the app loads again. Is saving the image's file URL the best approach for this?
import UIKit
import SwiftUI
struct ImagePicker: UIViewControllerRepresentable {
var sourceType: UIImagePickerController.SourceType = .photoLibrary
#Binding var selectedImage: UIImage
#Environment(\.presentationMode) private var presentationMode
func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
let imagePicker = UIImagePickerController()
imagePicker.allowsEditing = false
imagePicker.sourceType = sourceType
imagePicker.delegate = context.coordinator
return imagePicker
}
func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext<ImagePicker>) {
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
final class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
var parent: ImagePicker
init(_ parent: ImagePicker) {
self.parent = parent
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
parent.selectedImage = image
}
if let imgUrl = info[UIImagePickerController.InfoKey.imageURL] as? URL{
let imgName = imgUrl.lastPathComponent
let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first
let localPath = documentDirectory?.appending(imgName)
let image = info[UIImagePickerController.InfoKey.originalImage] as! UIImage
let data = image.pngData()! as NSData
data.write(toFile: localPath!, atomically: true)
//let imageData = NSData(contentsOfFile: localPath!)!
let photoURL = URL.init(fileURLWithPath: localPath!)//NSURL(fileURLWithPath: localPath!)
print(photoURL)
//TODO save this url in my app as a reference to look up
}
parent.presentationMode.wrappedValue.dismiss()
}
}
}
URL of image of camera roll may change , better way is to save your image to filesystem in the app sandbox and the you can save given name or something to retrieve it when u needed back
//MARK: save and retrive Images
extension UIImage {
func saveImage(imageName: String) {
guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
let fileName = imageName
let fileURL = documentsDirectory.appendingPathComponent(fileName)
guard let data = self.jpegData(compressionQuality: 1) else { return }
//Checks if file exists, removes it if so.
if FileManager.default.fileExists(atPath: fileURL.path) {
do {
try FileManager.default.removeItem(atPath: fileURL.path)
print("Removed old image")
} catch let removeError {
print("couldn't remove file at path", removeError)
}
}
do {
try data.write(to: fileURL)
} catch let error {
print("error saving file with error", error)
}
}
static 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
}
static func removeImage(fileName: String){
guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
let fileURL = documentsDirectory.appendingPathComponent(fileName)
if FileManager.default.fileExists(atPath: fileURL.path) {
do {
try FileManager.default.removeItem(atPath: fileURL.path)
print("Removed image")
} catch let removeError {
print("couldn't remove file at path", removeError)
}
}
}
}
Usage
yourImage.saveImage(imageName: "imageNameToSave")//<-save
UIImage.loadImageFromDiskWith(fileName: "ImageNameToRetrive")//<-retrive
UIImage.removeImage(fileName: "ImageNameToRemove")//<-remove
Edit:
You can definitely do FileManager as well, it truly depends on how many files you will be saving, and to where you want to save them. If it is a measly 1 file, that doesn't need to be secured and is public to the app, UserDefaults is the way to go. If you want to add a bit more control of that file, FileManager would be the way to go.
UserDefaults is your way to go to store locally.
Store Image Data
func locallyStoreImgData(image: UIImage, key:String) {
if let pngRepresentation = image.pngData() {
UserDefaults.standard.set(pngRepresentation, forKey: key)
}
else {
//Was unable to create png representation
}
}
Retrieve Image Data
func obtainImg(key:String) -> UIImage? {
if let imageData = UserDefaults.standard.object(forKey: key) as? Data,
let image = UIImage(data: imageData) {
return image
}
return nil
}
Use Case
locallyStoreImgData(image: myImage, key: "myImageKey")
if let image = obtainImg(key: "myImageKey") {
//Do something with image
}
else {
//Was unable to recreate image
}
I've a CKRecord type created in the CloudKit backend with some properties related to that class.
I've String properties, Bytes and I have a Asset List property, so store some images (multiple images related to a single record).
Now I'm trying so store some images and then fill the property and then trying to save it to CloudKit, but it's not working.
Code goes as it follows:
var images_array = [CKAsset]()
// append the an image to the array
images_array.append(CKAsset(fileURL: writeImage(image: selectedImage) as URL))
let record = CKRecord(recordType: recordName)
record["class_title"] = someString as CKRecordValue
record["class_body"] = someString as CKRecordValue
record["images_array"] = images_array as CKRecordValue
saveRecord(record)
func saveRecord(_ xrecord: CKRecord) {
let publicData = CKContainer.default().publicCloudDatabase
let record: [CKRecord] = [xrecord]
let saveOperation = CKModifyRecordsOperation.init(recordsToSave: record, recordIDsToDelete: nil)
saveOperation.perRecordCompletionBlock = {(record, error) -> Void in
if (error != nil) {
print("error")
}
}
publicData.add(saveOperation)
}
func writeImage(image: UIImage) -> URL {
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let fileURL = NSURL(fileURLWithPath: documentsURL.absoluteString).appendingPathComponent(".jpg")
if let imageData = image.lowestQualityJPEGNSData {
do {
try imageData.write(to: fileURL!)
} catch {
print("ERRO 001 = \(error.localizedDescription)")
}
}
return fileURL!
}
extension UIImage {
var uncompressedPNGData: Data? { return UIImagePNGRepresentation(self) }
var highestQualityJPEGNSData: Data? { return UIImageJPEGRepresentation(self, 1.0) }
var highQualityJPEGNSData: Data? { return UIImageJPEGRepresentation(self, 0.75) }
var mediumQualityJPEGNSData: Data? { return UIImageJPEGRepresentation(self, 0.5) }
var lowQualityJPEGNSData: Data? { return UIImageJPEGRepresentation(self, 0.25) }
var lowestQualityJPEGNSData:Data? { return UIImageJPEGRepresentation(self, 0.0) }
}
If I only save the strings, everything works perfectly but with images it doesn't save the record.
I know there might be any issue with the appending, or I have to save the array in other way, or I shouldn't save it as CKRecordValue.
Do you have any tip on how to achieve this?
Thanks
When you create your local asset file you should do so with the atomic write option. This will ensure that the file is completely written before CloudKit attempts to upload the asset.
This is the asset file creation function I use in the Seam 3 library:
fileprivate func createAsset(data: Data) -> CKAsset? {
var returnAsset: CKAsset? = nil
let tempStr = ProcessInfo.processInfo.globallyUniqueString
let filename = "\(tempStr)_file.bin"
let baseURL = URL(fileURLWithPath: NSTemporaryDirectory())
let fileURL = baseURL.appendingPathComponent(filename, isDirectory: false)
do {
try data.write(to: fileURL, options: [.atomicWrite])
returnAsset = CKAsset(fileURL: fileURL)
} catch {
print("Error creating asset: \(error)")
}
return returnAsset
}
You have to take Array of CKAsset for images.
var imageUrls = [CKAsset]()
Now get all images using for-loop. And save CKAsset of images.
for images in self.arrayImageSelected{
var myImage = UIImage()
if (images.isKindOfClass(PHAsset)){
let imageC = images as? PHAsset
myImage = self.getAssetThumbnail(imageC!)
}else if (images.isKindOfClass(UIImage)){
myImage = (images as? UIImage)!
}
let imagePath = self.storeImageAtDocumentDirectory(myImage, titleName: self.strTitle)
myPAth.append(imagePath)
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as String
let FbPath = paths.stringByAppendingString("/Custom")
let filePathToWrite = "\(FbPath)" + imagePath
let urls = NSURL(fileURLWithPath: filePathToWrite)
let imageAsset = CKAsset(fileURL: urls)
imageUrls.append(imageAsset)
}
Set Your array.
record.setObject(imageUrls, forKey: "images_array")
I would like to save images from a URL and then use them inside my app.
I saved them in variables but how can I make them persist until the user deletes the app ?
Here is the code for saving images in variables
let backgroundURL:NSURL? = NSURL(string: "http://i.imgur.com/4AiXzf8.jpg")
DispatchQueue.global(qos: .userInitiated).async {
let backgroundData:NSData? = NSData(contentsOf: backgroundURL as! URL)
DispatchQueue.main.async {
if (backgroundData != nil) {
background = UIImage(data: backgroundData! as Data
}
}
}
How can I save the background image to persist ?
Thank you!
For Swift 3
// Assuming background is UIImage
if let image = background {
if let data = UIImagePNGRepresentation(image) {
let filename = getDocumentsDirectory().appendingPathComponent("copy.png")
try? data.write(to: filename)
}
}
That call to getDocumentsDirectory()
func getDocumentsDirectory() -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let documentsDirectory = paths[0]
return documentsDirectory
}
I would suggest you to store image in your document directory the below code you will be able to use after you downloaded image and converted as UIImage from NSData
Swift 2.3
let documentsDirectoryURL = try! NSFileManager().URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: true)
// Name of Image you want to store
let fileURL = documentsDirectoryURL.URLByAppendingPathComponent("ImageName.jpg")
if !NSFileManager.defaultManager().fileExistsAtPath(fileURL.path!) {
if UIImageJPEGRepresentation(image, 1.0)!.writeToFile(fileURL.path!, atomically: true) {
print("Image saved")
} else {
print("error saving Image")
}
} else {
print("Image name already exists")
}
And here is how you can get image
let fileManager = NSFileManager.defaultManager()
let imagePAth = (self.getDirectoryPath() as NSString).stringByAppendingPathComponent("imageName.jpg")
if fileManager.fileExistsAtPath(imagePAth){
let myImage: UIImage = UIImage(contentsOfFile: imagePAth)
}
else{
print("No Such Image")
}