Swift Save images (screenshots) to nsuserdefaults - swift

I have a program, where the user "creates" an image, and then the program takes a screenshot of the screen. I would then like to save this screenshot to a database, prefferebly nsuserdefaults, since I am accessing it later in a table view. Any other suggestions on how to approach this, are more than welcome :)
The code is like this
let screenshot = getScreenshot() // saves the screenshot
var imagePaths = [String]()
// get the array of previous screenshots
if let _ = NSUserDefaults.standardUserDefaults().objectForKey(theKey)
{
imagePaths = NSUserDefaults.standardDefaults().objectForKey(theKey) as! [String]
}
// then I want to get a path to the image something like
let imagePath = screenshot.getPath() // although this is not a valid method, this is basically what I want
// add the imagePath
imagePaths.append(imagePath)
// finally I save the image
NSUserDefaults.standardUserDefaults().setObject(imagePaths, forKey: theKey)

You can create directory in Documents and save there screenshots as usual files. Filename can be generated from date and time for uniqueness.
func saveImage(imageData: NSData)
{
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "dd-MM-yyyy hh.mm.ss"
let filename = "\(dateFormatter.stringFromDate(NSDate())).png"
let documentsDirectory = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first as! String
let imagesDirectory = documentsDirectory.stringByAppendingPathComponent("Images")
let filePath = imagesDirectory.stringByAppendingPathComponent(filename)
if !NSFileManager.defaultManager().fileExistsAtPath(imagesDirectory)
{
var error: NSError?
NSFileManager.defaultManager().createDirectoryAtPath(imagesDirectory, withIntermediateDirectories: false, attributes: nil, error: &error)
if error != nil
{
println("\(error!.localizedDescription)")
return
}
}
imageData.writeToFile(filePath, atomically: true)
}
func getImagesPaths() -> [String]?
{
let documentsDirectory = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first as! String
let imagesDirectory = documentsDirectory.stringByAppendingPathComponent("Images")
if let filenames = NSFileManager.defaultManager().contentsOfDirectoryAtPath(imagesDirectory, error: nil)
{
let imagePaths = filenames.map{"\(imagesDirectory)/\($0)"}.filter(){$0.pathExtension == "png"}
return imagePaths.count > 0 ? imagePaths : nil
}
return nil
}
To save image simply use saveImage(data). To get images paths use getImagesPaths().
If you need array of UIImage, you can get it by follow way:
var images : [UIImage] = [ ]
if let imagePaths = getImagesPaths()
{
for path in imagePaths
{
if let image = UIImage(contentsOfFile: path)
{
images.append(image)
}
}
}

Related

save current location to plist when the user enters Geofencing radius

i am working on a geofencing app. i need to save user current location to a plist when the user enters geofencing radius which i have extracted them from a db file. How to do it? i am new to this. please Help.. i extracted from db file like this..
func readvalues(){
let path = Bundle.main.path(forResource: "NewFile", ofType: "db")
let database = FMDatabase(path: path)
if !database.open() {
print("Unable to open database")
return
}
if let rs = database.executeQuery("SELECT * FROM myowntable", withArgumentsIn: []) {
while rs.next() {
print("%#", rs);
geotifications = []
let longitude = rs.string(forColumn: "longitude") as! String
let latitude = rs.string(forColumn: "latitude") as! String
let note = rs.string(forColumn: "note") as! String
let radiusstring = rs.string(forColumn: "radius") as! String
let radius = (radiusstring as NSString).doubleValue
let identifier = rs.string(forColumn: "identifier") as! String
let event = Int(rs.string(forColumn: "eventType")!)!
let eventType: EventType = (event == 0) ? .onEntry : .onExit
let location:CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: (latitude as NSString).doubleValue, longitude: (longitude as NSString).doubleValue)
self.addGeotificationViewController(didAddCoordinate: location, radius: radius, identifier: identifier, note: note, eventType: eventType)
}
} else {
print("executeQuery failed: \(database.lastErrorMessage())")
}
database.close()
}
You not able to change apps plist file, so you need create your own plist file
let fileManager = FileManager.default
let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let path = documentDirectory.appending("/geo.plist")
if(!fileManager.fileExists(atPath: path)){
print(path)
let data : [String: String] = [
"geoPointX": "20",
"geoPointY": "30"
]
let someData = NSDictionary(dictionary: data)
let isWritten = someData.write(toFile: path, atomically: true)
print("is the file created: \(isWritten)")
} else {
print("file exists")
}

Issue with file manager in swift 3.1

I have the below code that worked fine, up until the release of swift 3.1.
func loadImage() {
id = userPhotoModel.id
let fileManager = FileManager.default
let imagePath = (self.getDirectoryPath() as NSString).appendingPathComponent(photoName)
if fileManager.fileExists(atPath: imagePath){
let imageFromPath = resizeImage(named: (contentsOfFile: imagePath))
print("name of photo retrieved: \(photoName)")
self.userPhoto.image = imageFromPath
}else{
print("No Image")
}
}
Now, swift 3.1 wants to add as! String to:
let imageFromPath = resizeImage(named: (contentsOfFile: imagePath) as! String)
However, when I run the app, it crashes in this location with no error message as per the below image.
What is causing this?
EDIT: Here is the resizeImage func
fileprivate func resizeImage(named name: String) -> UIImage
{
var image = UIImage(named: name)
if image!.size.height > image!.size.width
{
self.userPhoto.contentMode = .scaleAspectFit
}
else
{
image = image!.resizeTo(self.userPhoto.bounds)
}
return image!
}
The issue is the confusing syntax in the line:
let imageFromPath = resizeImage(named: (contentsOfFile: imagePath))
That should simply be:
let imageFromPath = resizeImage(named: imagePath)
No cast required and correct in any Swift 3.x.

Swift: Copy folder/directory without files

I want to copy a folder without the files inside to a new location. If I create it newly simply by name, I would miss all attribues. Is there a function to copy it (including access rights, color tags, etc...)?
You can set the attributes for the clones when you create them:
let targetPath = "..." as NSString
let destinationPath = "..." as NSString
let fileManager = NSFileManager.defaultManager()
let enumerator = fileManager.enumeratorAtPath(targetPath as String)!
for item in enumerator {
let fullTargetPath = targetPath.stringByAppendingPathComponent(item as! String)
let attributes = try! fileManager.attributesOfItemAtPath(fullTargetPath)
if let fileType = attributes[NSFileType] as? String where fileType == NSFileTypeDirectory {
let fullDestinationPath = destinationPath.stringByAppendingPathComponent(item as! String)
try! fileManager.createDirectoryAtPath(fullDestinationPath, withIntermediateDirectories: true, attributes: attributes)
}
}

Rewriting metadata into .jpg file by using Swift 2.2 OS X

I'm try rewrite metadata from JPG file. I want addition one keyword to metadata. Xcode don't give any errors, but file not changed.
Here is my code:
var pathToOpenFile:NSURL?
Next I write path from file to variable "pathToOpenFile".
If user pushed ENTER button into NSTextField, then work action:
#IBAction func endEditKeys(sender: AnyObject) {
if (pathToOpenFile != nil) {
let imageSource = CGImageSourceCreateWithURL(pathToOpenFile!, nil)
let imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource!, 0, nil)! as NSDictionary;
let exifDict = imageProperties.valueForKey("{IPTC}") as! NSDictionary;
var Keywords:[String] = exifDict.valueForKey("Keywords") as! [String];
Keywords.append("ANY")
exifDict.setValue(Keywords, forKey: "Keywords")
let type = CGImageSourceGetType(imageSource!)
let count = CGImageSourceGetCount(imageSource!)
let mutableData = NSMutableData(contentsOfURL: pathToOpenFile!)
let destination = CGImageDestinationCreateWithData(mutableData!, type!, count, nil)
let removeExifProperties: CFDictionary = exifDict
for i in 0..<count {
CGImageDestinationAddImageFromSource(destination!, imageSource!, i, removeExifProperties)
}
CGImageDestinationFinalize(destination!)
}
}
Can you help me, why it isn't work (not change metadata)? Thank you!
Thanks all!
It is working, just need rewrite .JPG file in end.
if let _ = try? mutableData!.writeToURL(pathToOpenFile!, options: NSDataWritingOptions.AtomicWrite) {
// if you need, do anything. For example "print ("Savig file")"
}

Write to plist file in Swift

I have a sample plist-file, favCities.plist. This is my sample code:
override func viewDidLoad() {
super.viewDidLoad()
let path = NSBundle.mainBundle().pathForResource("favCities", ofType: "plist")
var plistArray = NSArray(contentsOfFile: path) as [Dictionary<String, String>]
var addDic: Dictionary = ["ZMV": "TEST", "Name": "TEST", "Country": "TEST"]
plistArray += addDic
(plistArray as NSArray).writeToFile(path, atomically: true)
var plistArray2 = NSArray(contentsOfFile: path)
for tempDict1 in plistArray2 {
var tempDict2: NSDictionary = tempDict1 as NSDictionary
var cityName: String = tempDict2.valueForKey("Name") as String
var cityZMV: String = tempDict2.valueForKey("ZMV") as String
var cityCountry: String = tempDict2.valueForKey("Country") as String
println("City: \(cityName), ZMV: \(cityZMV), Country: \(cityCountry)")
}
At first glance, everything works well. The output looks like this:
City: Moscow, ZMV: 00000.1.27612, Country: RU
City: New York, ZMV: 10001.5.99999, Country: US
City: TEST, ZMV: TEST, Country: TEST
But when I interrupt the app, I see that my file favCities.plist has not changed. There are still two values. These values ​​- City: TEST, ZMV: TEST, Country: TEST - were not added. If I restarted the application, then again I see 3 lines of output, although there should be 4.
What is wrong?
UPDATED:
I was changed code to this:
override func viewDidLoad() {
super.viewDidLoad()
let fileManager = (NSFileManager.defaultManager())
let directorys : [String]? = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory,NSSearchPathDomainMask.AllDomainsMask, true) as? [String]
if (directorys! != nil){
let directories:[String] = directorys!;
let dictionary = directories[0];
let plistfile = "favCities.plist"
let plistpath = dictionary.stringByAppendingPathComponent(plistfile);
println("\(plistpath)")
var plistArray = NSArray(contentsOfFile: plistpath) as [Dictionary<String, String>]
var addDic: Dictionary = ["ZMV": "TEST", "Name": "TEST", "Country": "TEST"]
plistArray += addDic
(plistArray as NSArray).writeToFile(plistpath, atomically: false)
var plistArray2 = NSArray(contentsOfFile: plistpath)
for tempDict1 in plistArray2 {
var tempDict2: NSDictionary = tempDict1 as NSDictionary
var cityName: String = tempDict2.valueForKey("Name") as String
var cityZMV: String = tempDict2.valueForKey("ZMV") as String
var cityCountry: String = tempDict2.valueForKey("Country") as String
println("City: \(cityName), ZMV: \(cityZMV), Country: \(cityCountry)")
}
}
else {
println("ERROR!")
}
}
Now when you run the application the number of rows in the output increases:
City: Moscow, ZMV: 00000.1.27612, Country: RU
City: New York, ZMV: 10001.5.99999, Country: US
City: TEST, ZMV: TEST, Country: TEST
City: TEST, ZMV: TEST, Country: TEST
........
BUT! If view the file favCities.plist, which is located in the project folder (Project Navigator in Xcode), it still remains unchanged - there are two lines!
If walk along the path, which is stored in the variable plistpath - /Users/admin/Library/Developer/CoreSimulator/Devices/55FD9B7F-78F6-47E2-9874-AF30A21CD4A6/data/Containers/Data/Application/DEE6C3C8-6A44-4255-9A87-2CEF6082A63A/Documents/
Then there is one more file favCities.plist. It contains all the changes that make the application. What am I doing wrong? How can I see all the changes in a file that is located in the project folder (Project Navigator)?
Mostly, people want to store a list of something, so here is my share on how to do this, also, here I don't copy the plist file, I just create it. The actual saving/loading is quite similar to the answer from Rebeloper
xcode 7 beta, Swift 2.0
saving
func SaveItemFavorites(items : Array<ItemFavorite>) -> Bool
{
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) as NSArray
let docuDir = paths.firstObject as! String
let path = docuDir.stringByAppendingPathComponent(ItemFavoritesFilePath)
let filemanager = NSFileManager.defaultManager()
let array = NSMutableArray()
for var i = 0 ; i < items.count ; i++
{
let dict = NSMutableDictionary()
let ItemCode = items[i].ItemCode as NSString
dict.setObject(ItemCode, forKey: "ItemCode")
//add any aditional..
array[i] = dict
}
let favoritesDictionary = NSDictionary(object: array, forKey: "favorites")
//check if file exists
if(!filemanager.fileExistsAtPath(path))
{
let created = filemanager.createFileAtPath(path, contents: nil, attributes: nil)
if(created)
{
let succeeded = favoritesDictionary.writeToFile(path, atomically: true)
return succeeded
}
return false
}
else
{
let succeeded = notificationDictionary.writeToFile(path, atomically: true)
return succeeded
}
}
Little note from the docs:
NSDictionary.writeToFile(path:atomically:)
This method recursively validates that all the contained objects are property list objects (instances of NSData, NSDate, NSNumber, NSString, NSArray, or NSDictionary) before writing out the file, and returns NO if all the objects are not property list objects, since the resultant file would not be a valid property list.
So whatever you set at dict.SetObject() should be one of the above mentioned types.
loading
private let ItemFavoritesFilePath = "ItemFavorites.plist"
func LoadItemFavorites() -> Array<ItemFavorite>
{
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) as NSArray
let docuDir = paths.firstObject as! String
let path = docuDir.stringByAppendingPathComponent(ItemFavoritesFilePath)
let dict = NSDictionary(contentsOfFile: path)
let dictitems : AnyObject? = dict?.objectForKey("favorites")
var favoriteItemsList = Array<ItemFavorite>()
if let arrayitems = dictitems as? NSArray
{
for var i = 0;i<arrayitems.count;i++
{
if let itemDict = arrayitems[i] as? NSDictionary
{
let ItemCode = itemDict.objectForKey("ItemCode") as? String
//get any additional
let ItemFavorite = ItemFavorite(item: ItemCode)
favoriteItemsList.append(ItemFavorite)
}
}
}
return favoriteItemsList
}
Apart from the fact that the application bundle is read-only (for obvious reasons), since Swift 4 there is PropertyListDecoder/Encoder to read and write property lists without the bridged Objective-C APIs.
First create a struct for the model conforming to Codable
struct FavCity : Codable {
let city, zmv, country: String
}
Then specify two URLs, the url of the default file in the bundle and one URL in the documents directory to be able to modify the file
let fileManager = FileManager.default
let applicationBundleFileURL = Bundle.main.url(forResource: "favCities",
withExtension: "plist")!
let documentsFileURL = try! fileManager.url(for: .documentDirectory,
in: .userDomainMask,
appropriateFor: nil,
create: false)
.appendingPathComponent("favCities.plist")
the try! doesn't matter because the system makes sure that the folder Documents exists.
Create a new favorite city
let newCity = FavCity(city: "TEST", zmv: "TEST", country: "TEST")
Now read the file in the documents directory. If it doesn't exist read the file in the bundle. Finally append the new city and write the property list data back to the documents directory
let data : Data
do {
data = try Data(contentsOf: documentsFileURL)
} catch {
data = try! Data(contentsOf: applicationBundleFileURL)
}
do {
var favCities = try PropertyListDecoder().decode([FavCity].self, from: data)
favCities.append(newCity)
let newData = try PropertyListEncoder().encode(favCities)
try newData.write(to: documentsFileURL)
} catch {
print(error)
}