Swift plist - Reading item 4 - swift

I want to translate my current program to Swift and I am getting hung up on the simplest things.
I am trying to read a plist
But I want to do this one at a time.
i.e. - if we are trying to call the image candy hearts.jpg (we obviously need to add the extension jpeg.
This is what I found on on the net.
var myDict: NSDictionary?
if let path = NSBundle.mainBundle().pathForResource("data", ofType: "plist") {
myDict = NSDictionary(contentsOfFile: path)
// self.data
}
if let dict = myDict {
// Use your dict here
}
This is what I originally used iOS6
NSString *pathOfDataTitle = [[NSBundle mainBundle] pathForResource:#"data" ofType:#"plist"];
NSMutableArray *arrayOfTitle = [[NSMutableArray alloc] initWithContentsOfFile:pathOfDataTitle];
self.Datalist = arrayOfTitle;

This worked
var data: NSArray?
var imagename : String!
func readlist(var filename:String) -> NSArray{
var myDict: NSArray?
let path = NSBundle.mainBundle().pathForResource(filename, ofType: "plist")
if (path != nil){
var val:String
val=path!;
myDict = NSArray(contentsOfFile: val)
var list=myDict;
}
if let dict = myDict {
return myDict!;
}
return myDict!;
}
}

Related

Get file size of PHAsset without loading in the resource?

Is there a way to get the file size on disk of a PHAsset without doing requestImageDataForAsset or converting it to ALAsset? I am loading in previews of a user's photo library - potentially many thousands of them - and it's imperative to my app that they know the size before import.
You can grab the fileSize of a PHAsset and convert it to human readable form like this:
let resources = PHAssetResource.assetResources(for: yourAsset) // your PHAsset
var sizeOnDisk: Int64? = 0
if let resource = resources.first {
let unsignedInt64 = resource.value(forKey: "fileSize") as? CLong
sizeOnDisk = Int64(bitPattern: UInt64(unsignedInt64!))
}
Then use your sizeOnDisk variable and pass it into a method like this...
func converByteToHumanReadable(_ bytes:Int64) -> String {
let formatter:ByteCountFormatter = ByteCountFormatter()
formatter.countStyle = .binary
return formatter.string(fromByteCount: Int64(bytes))
}
A safer solution:
[asset requestContentEditingInputWithOptions:nil completionHandler:^(PHContentEditingInput * _Nullable contentEditingInput, NSDictionary * _Nonnull info) {
NSNumber *fileSize = nil;
NSError *error = nil;
[contentEditingInput.fullSizeImageURL getResourceValue:&fileSize forKey:NSURLFileSizeKey error:&error];
NSLog(#"file size: %#\nerror: %#", fileSize, error);
}];
Swift version:
asset.requestContentEditingInput(with: nil) { (contentEditingInput, _) in
do {
let fileSize = try contentEditingInput?.fullSizeImageURL?.resourceValues(forKeys: [URLResourceKey.fileSizeKey]).fileSize
print("file size: \(String(describing: fileSize))")
} catch let error {
fatalError("error: \(error)")
}
}
Inspired by How to get an ALAsset URL from a PHAsset?
Please try this.
let resources = PHAssetResource.assetResources(for: YourAsset)
var sizeOnDisk: Int64 = 0
if let resource = resources.first {
let unsignedInt64 = resource.value(forKey: "fileSize") as? CLong
sizeOnDisk = Int64(bitPattern: UInt64(unsignedInt64!))
totalSize.text = String(format: "%.2f", Double(sizeOnDisk) / (1024.0*1024.0))+" MB"
}
SWIFT 5.0
Light & Easy:
private static let bcf = ByteCountFormatter()
func getSize(asset: PHAsset) -> String {
let resources = PHAssetResource.assetResources(for: asset)
guard let resource = resources.first,
let unsignedInt64 = resource.value(forKey: "fileSize") as? CLong else {
return "Unknown"
}
let sizeOnDisk = Int64(bitPattern: UInt64(unsignedInt64))
Self.bcf.allowedUnits = [.useMB]
Self.bcf.countStyle = .file
return Self.bcf.string(fromByteCount: sizeOnDisk)
}

Swift Save images (screenshots) to nsuserdefaults

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)
}
}
}

Swift convert Plist(inside NSString) to NSDictionary

Hello I have a NSString that contains Plist data. I need to convert it to NSDictionary. I have found ways to do it with a file but I want to do it directly in memory. How can I do this with swift? thank you for the help.
var plist : NSMutableDictionary
let nsDocumentDirectory = NSSearchPathDirectory.DocumentDirectory
let nsUserDomainMask = NSSearchPathDomainMask.UserDomainMask
if let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true) {
if paths.count > 0 {
if let dirPath = paths[0] as? String {
let readPath = dirPath.stringByAppendingPathComponent("info.plist")
plist = NSMutableDictionary(contentsOfFile: readPath)!
println(plist)
}
}
}
After alot a alot of gooling and playing with it I came across
var data = stringWithPlistInside.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)
var error : NSError?
var dic: AnyObject! = NSPropertyListSerialization.propertyListWithData(data!, options: 0, format: nil, error: &error)
Just in case anyone else happens to be stuck in my situation.

'AnyObject?' does not have a member named 'count' compiler error

First of all, I tried using JSON Serialization in the following code and I tried looping through each items available in the array. However, it must be type casting that I am missing something. Here is the snippet of what I am trying to do:
let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options:
NSJSONReadingOptions.MutableContainers, error: nil) as
NSDictionary
var items = [[String:String]()]
var item: AnyObject
var authorDictionary: AnyObject
for var i = 0; i < jsonResult["items"].count; i++ {
items.append([String:String]())
items[i]["content"] = item["content"] as? NSString
items[i]["title"] = item["title"] as? NSString
items[i]["publishedDate"] = item["published"] as? NSString
authorDictionary = item["author"] as NSDictionary
items[i]["author"] = item["displayName"] as? NSString
}
To resolve your error, change this:
jsonResult["items"]
to this:
(jsonResult["items"] as! NSArray)
Sorry, i am not with my xcode here, but looking the code i think in something about this.
let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options:
NSJSONReadingOptions.MutableContainers, error: nil) as
NSDictionary
var items = [[String:String]()]
var item: AnyObject
var authorDictionary: AnyObject
if let itemsFromJson:NSArray = jsonResult["items"] as? NSArray
{
for itemJson in itemsFromJson {
items.append([String:String]())
items[i]["content"] = itemJson["content"] as? NSString
items[i]["title"] = itemJson["title"] as? NSString
items[i]["publishedDate"] = itemJson["published"] as? NSString
authorDictionary = itemJson["author"] as NSDictionary
items[i]["author"] = itemJson["displayName"] as? NSString
}
}

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)
}