I have a plist running in my project which contains number of image urls.I am trying to pass the url image to my imageView which is in the same viewController.I found similar questions from github like Loading/Downloading image from URL on Swift , Swift - Read plist , Can't get plist URL in Swift I went through all those answers but no luck so far rather than fatal crash. My partial codes as follows....
Method 1:
override func viewDidLoad() {
super.viewDidLoad()
if let path = Bundle.main.path(forResource: "apps", ofType: "plist"),
let root = (NSArray(contentsOfFile: path))
{
let url = NSURL(string: path)
let data = NSData(contentsOf: url! as URL)
if let imageData = data {
imageView.image = UIImage(data: imageData as Data)
}
**// Swift Console Printinging as Follows**
print(root)
// printing all my plist url links like {icon = "https://xxxxxxxxxxx.com/image/girl_face_freckles_eyes_92358_1920x1080.jpg";}
print(url)
// (/Users/xxxxxx/Library/Developer/CoreSimulator/Devices/52DA3F73-83E0-4C29-9DE1-D8D5F0731C13/data/Containers/Bundle/Applica ... ps.plist)
print(data)
// nil
print(imageView.image)
//nil
} else {
print("Either the file does not exist or the root object is an array")
}
Method 2:
let path = Bundle.main.path(forResource: "apps", ofType: "plist")
let url = NSURL(string: path!)
let imgData = try? Data(contentsOf: url as! URL)
let img = UIImage(data: imgData!)!
print(img) // fatal crash
}
path is the path to your plist, not to your image URL.
The image URL is in the key "icon" in the "root" array.
Get the first item of the array and subscript with the key, you should get your image URL:
if let item = root[0] as? [String:Any] {
if let result = item["icon"] as? String {
print(result)
}
}
The path in (NS)Bundle is a file system path and these paths must be created with URL(fileURLWithPath:)
let path = Bundle.main.path(forResource: "apps", ofType: "plist")
let url = URL(fileURLWithPath: path!)
But why does nobody use the URL related API which is much more convenient
let url = Bundle.main.url(forResource: "apps", withExtension: "plist")
Related
I'm new to swift and I'm trying to load a property from a nsDictionary to vTitle
var nsDictionary: NSDictionary?
if let path = Bundle.main.path(forResource: "AppData", ofType: "plist") {
nsDictionary = NSDictionary(contentsOfFile: path)
}
let vTitle:String = nsDictionary["LbVacationsTitle"]
When I debug I see the right keys in nsDictionary but I can't unwrap the value of just one key
The type of LbVacationsTitle is a string
Depending on your style preference...
var nsDictionary: NSDictionary?
if let path = Bundle.main.path(forResource: "AppData", ofType: "plist") {
nsDictionary = NSDictionary(contentsOfFile: path)
}
if let dict = nsDictionary {
let vTitle = dict["LbVacationsTitle"] as? String
if let vt = vTitle {
// ...
}
}
...or...
var nsDictionary: NSDictionary?
if let path = Bundle.main.path(forResource: "AppData", ofType: "plist") {
nsDictionary = NSDictionary(contentsOfFile: path)
}
guard let dict = nsDictionary else {
print("Couldn't get a valid dictionary")
return
}
let vTitle = dict["LbVacationsTitle"] as? String
guard let vt = vTitle else {
print("Couldn't find a string matching LbVacationsTitle")
return
}
// ...
Please don't use the NSDictionary API in Swift to read a property list.
There is PropertyListSerialization (or even PropertyListDecoder)
let url = Bundle.main.url(forResource: "AppData", withExtension: "plist")!
let data = try! Data(contentsOf: url)
let dictionary = try! PropertyListSerialization.propertyList(from: data, format: nil) as! [String:Any]
let vTitle = dictionary["LbVacationsTitle"] as! String
As the file is immutable in the application bundle any crash reveals a design mistake
I'm trying to write to the plist and I'm using two approaches but none of them work for me.
I'm not getting any errors though and when I print the paths I can see that plist exist, however you can see from the screenshot that the plist it is not getting updated/populated.
let path = Bundle.main.path(forResource: "Employee", ofType: "plist")!
let data : NSDictionary =
["A": [["userid":"1","username":"AAA","usergroupid":"2"], ["userid":"33","username":"ABB","usergroupid":"8"]],
"B": [["userid":"2","username":"BBB","usergroupid":"8"], ["userid":"43","username":"ABC","usergroupid":"8"]] ]
//first approach
let favoritesDictionary = NSDictionary(object: data, forKey: ("Favorites" as NSString?)!)
print(path)
let succeeded = favoritesDictionary.write(toFile: path, atomically: true)
//second approach
let bundlePath = Bundle.main.path(forResource: "Employee", ofType: "plist")!
print(bundlePath)
let dictionary = NSMutableDictionary(contentsOfFile: bundlePath)
dictionary?.setObject(data, forKey: ("Locations" as NSString?)!)
dictionary?.write(toFile: bundlePath, atomically: true)
Can someone please help?
This is a short tutorial.
Create your plist file and put it in the application bundle.
In AppDelegate create a computed property to get the current Documents folder and append the file path
var employeePlistURL : URL {
let documentsFolderURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
return documentsFolderURL.appendingPathComponent("Employee.plist")
}
In AppDelegate applicationWillFinishLaunching register a key-value pair for the firstLaunch flag in UserDefaults and copy the plist into the documents folder if the flag is true
func applicationWillFinishLaunching(_ aNotification: Notification) {
let defaults = UserDefaults.standard
defaults.register(defaults: ["firstLaunch":true])
if defaults.bool(forKey: "firstLaunch") {
let sourceFile = Bundle.main.url(forResource: "Employee", withExtension: "plist")!
try? FileManager.default.copyItem(at: sourceFile, to: employeePlistURL)
defaults.set(false, forKey: "firstLaunch")
}
}
Wherever you need to read and write the property list create also the computed property and add a property for the dictionary
var employees = [String:Any]()
and two methods to load and save the data
func loadEmployees() {
do {
let data = try Data(contentsOf: employeePlistURL)
guard let plist = try PropertyListSerialization.propertyList(from: data, format: nil) as? [String:Any] else { return }
employees = plist
} catch { print(error) }
}
func saveEmployees() {
do {
let data = try PropertyListSerialization.data(fromPropertyList: employees, format: .binary, options: 0)
try data.write(to: employeePlistURL)
} catch { print(error) }
}
A better way is to use structs and PropertyListEncoder/-Decoder but as the literal dictionary and the screenshot in the question are rather different I provide the common Dictionary / PropertyListSerialization way.
let image: UIImage = UIImage(named:"imageView")!
let imageData: NSData = UIImagePNGRepresentation(image)! as NSData
base64String = imageData.base64EncodedString(options: .lineLength64Characters)
print(base64String as Any)
My problem is the variable (image) is null, but I'm sure I have selected the correct imageView
Your code will only work if the image exists in your asset catalog and the asset catalog is member of your target.
Maybe you just added the file to your project and not to the asset catalog? In this case try
guard let url = Bundle.main.url(forResource: "image", withExtension: "jpg") else {
return
}
guard let data = try? Data(contentsOf: url) else {
return
}
print(data.base64EncodedString())
This is how I saved the images
let format = DateFormatter()
format.dateFormat="MMMM-dd-yyyy-ss"
let currentFileName = "\(format.string(from: Date())).img"
print(currentFileName)
// Save Images
let fileMgr = FileManager.default
let dirPath = fileMgr.urls(for: .documentDirectory, in: .userDomainMask)[0]
let imageFileUrl = dirPath.appendingPathComponent(currentFileName)
do {
try UIImagePNGRepresentation(returnedImages)!.write(to: imageFileUrl)
print("Image Added Successfully")
} catch {
print(error)
}
Below is code I am using to retrieve images, But I am getting the URL instead of the image file to populate tableview. Any help would be appreciated
let fileManager = FileManager.default
let imageUrl = fileManager.urls(for: .documentDirectory, in: .userDomainMask) [0].appendingPathComponent("img")
print("Your Images:\(imageUrl)")
It's simply because your image's name is invalid. Use the debugger to find the exact value of imageUrl, I bet it's something like this .../Documents/img. What you want is more like .../Documents/Sep-04-2017-12.img
You'd have to store currentFileName in the view controller so you can reference it later.
Also, your naming strategy is pretty fragile. Many images can end up sharing one name.
If you have a folder full of images, you can iterate on that folder to get back the img files:
let fileManager = FileManager.default
let documentDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]
let directoryContents = try! fileManager.contentsOfDirectory(at: documentDirectory, includingPropertiesForKeys: nil)
for imageURL in directoryContents where imageURL.pathExtension == "img" {
if let image = UIImage(contentsOfFile; imageURL.path) {
// now do something with your image
} else {
fatalError("Can't create image from file \(imageURL)")
}
}
Try using this
func getImage(){
let fileManager = FileManager.default
let imagePAth = (self.getDirectoryPath() as NSString).appendingPathComponent("apple.jpg") // saved image name apple.jpg
if fileManager.fileExists(atPath: imagePAth){
self.lockbackImageview.image = UIImage(contentsOfFile: imagePAth)
}else{
print("No Image")
self.lockbackImageview.image = UIImage.init(named: "1.jpg")
}
}
All of my attempts to read from a plist have resulted in a nil value returned, I've tried this in several ways on both Xcode 6 & Xcode beta 7. Also, there are quite a few similar questions on stack, I've tried many of them, but none of them resolve this issue.
I've added my words.plist by clicking on:
{my project} > targets > build phases > copy Bundle Resources
Then I tried several variations of the following code in my ViewController:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) as NSArray
let documentsDirectory = paths[0] as! String
let path = documentsDirectory.stringByAppendingPathComponent("words.plist")
let fileManager = NSFileManager.defaultManager()
//check if file exists
if(!fileManager.fileExistsAtPath(path)) {
// If it doesn't, copy it from the default file in the Bundle
if let bundlePath = NSBundle.mainBundle().pathForResource("words", ofType: "plist") {
let resultDictionary = NSMutableDictionary(contentsOfFile: bundlePath)
println("Bundle words file is --> \(resultDictionary?.description)") // this is nil!!!
fileManager.copyItemAtPath(bundlePath, toPath: path, error: nil)
} else {
println("words not found. Please, make sure it is part of the bundle.")
}
} else {
println("words already exits at path.")
// use this to delete file from documents directory
//fileManager.removeItemAtPath(path, error: nil)
}
print("entering if-let")
if let pfr = NSBundle.mainBundle().pathForResource("words", ofType: "plist") {
print("\nin let\n")
print(pfr)
print("\nentering dict if-let\n")
if let dict = NSDictionary(contentsOfFile: path) as? Dictionary<String, AnyObject> {
// use swift dictionary as normal
print("\nin let\n")
print(dict)
}
}
}
Question
Why am I getting a nil value and whats the proper way to add a plist file and read from it?
update:
inside my if statement the following is nil:
let resultDictionary = NSMutableDictionary(contentsOfFile: bundlePath)
println("Bundle words file is --> \(resultDictionary?.description)") // this is nil!!!
To me, this would indicate that either Xcode doesn't know about my words.plist file, or that I'm pointing my bundlePath to the wrong location.
the issue:
As #Steven Fisher stated, in the comments. My .plist file was an Array and not an NSDictionary. So I just had to switch two lines from my code:
let resultDictionary = NSMutableDictionary(contentsOfFile: bundlePath)
to
let resultDictionary = NSMutableArray(contentsOfFile: bundlePath)
and also
if let dict = NSDictionary(contentsOfFile: path) { //...
to
if let dict = NSArray(contentsOfFile: path) { //..
final working code:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) as NSArray
let documentsDirectory = paths[0] as! String
let path = documentsDirectory.stringByAppendingPathComponent("words.plist")
let fileManager = NSFileManager.defaultManager()
//check if file exists
if(!fileManager.fileExistsAtPath(path)) {
// If it doesn't, copy it from the default file in the Bundle
if let bundlePath = NSBundle.mainBundle().pathForResource("words", ofType: "plist") {
let resultDictionary = NSMutableArray(contentsOfFile: bundlePath)
println("Bundle words file is --> \(resultDictionary?.description)")
fileManager.copyItemAtPath(bundlePath, toPath: path, error: nil)
} else {
println("words not found. Please, make sure it is part of the bundle.")
}
} else {
println("words already exits at path.")
// use this to delete file from documents directory
//fileManager.removeItemAtPath(path, error: nil)
}
print("entering if-let")
if let pfr = NSBundle.mainBundle().pathForResource("words", ofType: "plist") {
print("\nin let\n")
print(pfr)
print("\nentering dict if-let\n")
if let dict = NSArray(contentsOfFile: pfr) {
// use swift dictionary as normal
print("\nin let\n")
print(dict)
}
}
}