Plist not changing value [swift] - swift

I wanted to implement the in-app version for my app, but I don't understand why is not working? The output showing i was added successfully, but when i open the pList file from my Xcode, its not changing? Why is it? Here is my code:
func version(){
let urlString = jocomAPIKey + "/feed/version"
let CFBundleShortVersionKey = "CFBundleVersion"
//First get the nsObject by defining as an optional anyObject
let nsObject: AnyObject? = NSBundle.mainBundle().infoDictionary!["CFBundleVersion"]
let version = nsObject as! String
print("app version: \(version)")
Alamofire.request(.POST, urlString , parameters: ["os" : "iphone"])
.responseData { response in
let versionXML = SWXMLHash.parse(response.data!)
let versionString = (versionXML["rss"]["channel"]["item"]["version"].element?.text)
print("version string: \(versionString)")
if let plist = Plist(name: "Info") {
let dict = plist.getMutablePlistFile()!
dict[CFBundleShortVersionKey] = versionString
//3
do {
try plist.addValuesToPlistFile(dict)
print("added")
} catch {
print(error)
}
//4
print(plist.getValuesInPlistFile())
} else {
print("Unable to get Plist")
}
}
}

You can't update your (Bundle plist) at run-time. If you want use then you just make one copy on your Document Directory then you can perform any operation.
Write and Read a plist in swift with simple data

Apple not allow to update info.plist update at runtime in app.

Related

Prevent images to download again after going back or terminating app

First of all I want to say, I didn't find anyting about it on this site.
I want to save NSURL into NSUserDefaults to prevent loading image on every app open or the view open. I tried to achieve it like this but for some reason I can't. At least I think thats what should do it:
let productImageref = productsValue[indexPath.row]["Products"] as? String
cell.snusProductImageView.image = nil
if let url = self.productsValue[indexPath.row]["imageUrl"] as? NSURL {
cell.snusProductImageView.kf_showIndicatorWhenLoading = true
var storedUrl = defaults.objectForKey("imageUrl") as? NSURL
cell.snusProductImageView.kf_setImageWithURL(storedUrl)
}
else {
FIRStorage.storage().reference().child("\(productImageref!).png").downloadURLWithCompletion({(url, error)in
if error != nil{
print(error)
return
}else{
self.productsValue[indexPath.row]["imageUrl"] = url
self.defaults.setURL(url, forKey: "imageUrl")
self.productstable.reloadData()
dispatch_async(dispatch_get_main_queue(), {
})
}
})
}
If I am wrong then please correct me. Should saving the url-s into userdefaults do the trick?
What exactly I am doing wrong here? Maybe it is better to save it into CoreData but I thought that it would be overkill.
Why the KingFisher or SDWebImages library is not caching it?
Two things that might help: use synchronize after saving something to defaults and use URLForKey to get it back.
(And check that the thing you're saving really is a NSURL.)
Here's sample code that works in a project; compare it to yours (NSUserDefaults is broken for playgrounds.):
let defaults = NSUserDefaults.standardUserDefaults()
if let components = NSURLComponents(string: "http://localhost/"),
url = components.URL {
defaults.setURL(url, forKey: "imageUrl")
defaults.synchronize()
if let fetchedURL = defaults.URLForKey("imageUrl") {
print("\(fetchedURL) returned")
} else {
print("No URL in defaults")
}
} else {
print("Bad components")
}
print(defaults.dictionaryRepresentation())
You have to store URL like below:
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setURL(yourURL:NSURL?, forKey: "imageURL")
And read NSUserDefaults like this:
let defaults = NSUserDefaults.standardUserDefaults()
defaults.URLForKey("imageURL")
Hope this helps.
As i understood, you want to save multiple images url in NSUserDefaults
let url = NSURL(string: "http://localhost/")
var array = [NSString]()
array.append((url?.absoluteString)!)
NSUserDefaults.standardUserDefaults().setValue(array, forKey: "image")
NSUserDefaults.standardUserDefaults().synchronize()
if let fetchedURL = NSUserDefaults.standardUserDefaults().valueForKey("image") as! [String]? {
print("\(fetchedURL[0]) returned")
let fileUrl = NSURL(string: fetchedURL[0])
print(fileUrl)
} else {
print("No URL in defaults")
}
print results
http://localhost/ returned
Optional(http://localhost/)

Can't get plist URL in Swift

I'm really confused on this one. There are dozens of questions around the web asking "How do I get info from my plist file in Swift?" and the same answer is posted everywhere:
let path = NSBundle.mainBundle().pathForResource("Config", ofType: "plist")
However, this line produces always produces nil for me. I have replaced Config with other components found in the default plist file, but get nil as well.
I am trying to access my custom ProductIdentifiers Array like so:
let url = NSBundle.mainBundle().URLForResource("ProductIdentifiers", withExtension: "plist")!
var productArray = NSArray(contentsOfURL: url) as! [[String:AnyObject!]]
I get a crash stating fatal error: unexpectedly found nil while unwrapping an Optional value on productArray. I have also tried this with other default plist values in place of ProductIdentifiers.
Does anyone know why this is not working for me even though there are so many posts around of people using this successfully?
I've never heard of the OP's approach working before. Instead, you should open the Info.plist file itself, then extract values from it, like so:
Swift 3.0+
func getInfoDictionary() -> [String: AnyObject]? {
guard let infoDictPath = Bundle.main.path(forResource: "Info", ofType: "plist") else { return nil }
return NSDictionary(contentsOfFile: infoDictPath) as? [String : AnyObject]
}
let productIdentifiers = getInfoDictionary()?["ProductIdentifiers"]
Swift 2.0
func getInfoDictionary() -> NSDictionary? {
guard let infoDictPath = NSBundle.mainBundle().pathForResource("Info", ofType: "plist") else { return nil }
return NSDictionary(contentsOfFile: infoDictPath)
}
let productIdentifiers = getInfoDictionary()?["ProductIdentifiers"]
Resource represents the file name of the plist rather than its contents.
The root object of the plist is probably a dictionary.
Replace MyPlist with the real file name.
This code prints the contents of the plist
if let url = NSBundle.mainBundle().URLForResource("MyPlist", withExtension: "plist"),
root = NSDictionary(contentsOfURL: url) as? [String:AnyObject]
{
print(root)
} else {
print("Either the file does not exist or the root object is an array")
}

Converting [NSURL] into [String] for NSUserDefaults?

I'm saving images into Parse Data as an array of NSURL's. Once I have them back into my app I would like to convert them to [String] so my app can temporarily store them. Any ideas?
Here is my code....
// Saving like This....
vc.videoImageArry = defaults.setObjectForKey("vidImages)
//Retrieving like This....
vc.vidImageArray = defaults.objectForKey("vidImages") as! [NSURL]
Using NSData
You can convert each NSURL to NSData in order to save it
func save(urls: [NSURL]) {
let urlsData = urls.map { $0.dataRepresentation }
NSUserDefaults.standardUserDefaults().setObject(urlsData, forKey: "urlsData")
}
Later on you can retrieve the NSData array and convert it back to [NSURL]
func load() -> [NSURL]? {
let retrievedData = NSUserDefaults.standardUserDefaults().arrayForKey("urlsData") as? [NSData]
return retrievedData?.map { NSURL(dataRepresentation: $0, relativeToURL: nil) }
}
Using String
Alternatively you can save the urls as String(s)
func save(urls: [NSURL]) {
let urlsData = urls.map { $0.absoluteString }
NSUserDefaults.standardUserDefaults().setObject(urlsData, forKey: "urlsData")
}
func load() -> [NSURL?]? {
let retrievedData = NSUserDefaults.standardUserDefaults().arrayForKey("urlsData") as? [String]
return retrievedData?.map { NSURL(string: $0) }
}
As discussed in the comments below, if data is written to NSUserDefaults exclusively with the save function, we know that every element of the array is a String representing a valid NSURL.
So we can change the return type of load from [NSURL?]? to [NSURL]? using this alternate version of load.
func load() -> [NSURL]? {
let retrievedData = NSUserDefaults.standardUserDefaults().arrayForKey("urlsData") as? [String]
return retrievedData?.flatMap { NSURL(string: $0) }
}
To convert from NSURL to String:
String(url)
To convert from String to NSURL:
NSURL(string: string)
Here's a fully working example that converts an array both ways:
import Cocoa
let urls = [NSURL(string: "http://www.swift.org/")!, NSURL(string: "http://www.apple.com/")!]
let strings = urls.map { String($0) }
let backToUrls = strings.map { NSURL(string: $0)! }
I believe that the above answers your specific question.
Having said that, the line for saving doesn't look right to me. You may want to look further into NSUserDefaults or ask a separate question if you're having difficulty with that line. You would need to paste some more context like lines above and below and exact error messages you're getting if any.

Swift NSDirectoryEnumerator Generator [duplicate]

I'm quite new to programming a Swift and I'm trying to iterate through the files in a folder.
I took a look at the answer here and tried to translate it to Swift syntax, but didn't succeed.
let fileManager = NSFileManager.defaultManager()
let enumerator:NSDirectoryEnumerator = fileManager.enumeratorAtPath(folderPath)
for element in enumerator {
//do something
}
the error I get is:
Type 'NSDirectoryEnumerator' does not conform to protocol 'SequenceType'
My aim is to look at all the subfolders and files contained into the main folder and find all the files with a certain extension to then do something with them.
Use the nextObject() method of enumerator:
while let element = enumerator?.nextObject() as? String {
if element.hasSuffix("ext") { // checks the extension
}
}
Nowadays (early 2017) it's highly recommended to use the – more versatile – URL related API
let fileManager = FileManager.default
do {
let resourceKeys : [URLResourceKey] = [.creationDateKey, .isDirectoryKey]
let documentsURL = try fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
let enumerator = FileManager.default.enumerator(at: documentsURL,
includingPropertiesForKeys: resourceKeys,
options: [.skipsHiddenFiles], errorHandler: { (url, error) -> Bool in
print("directoryEnumerator error at \(url): ", error)
return true
})!
for case let fileURL as URL in enumerator {
let resourceValues = try fileURL.resourceValues(forKeys: Set(resourceKeys))
print(fileURL.path, resourceValues.creationDate!, resourceValues.isDirectory!)
}
} catch {
print(error)
}
I couldn't get pNre's solution to work at all; the while loop just never received anything. However, I did come across this solution which works for me (in Xcode 6 beta 6, so perhaps things have changed since pNre posted the above answer?):
for url in enumerator!.allObjects {
print("\((url as! NSURL).path!)")
}
my two cents from previously anwers.. more swifty and with optionals:
let enumerator = FileManager.default.enumerator(atPath: folderPath)
while let element = enumerator?.nextObject() as? String {
print(element)
if let fType = enumerator?.fileAttributes?[FileAttributeKey.type] as? FileAttributeType{
switch fType{
case .typeRegular:
print("a file")
case .typeDirectory:
print("a dir")
}
}
}
returns all files in a directory + in subdirectories
import Foundation
let path = "<some path>"
let enumerator = FileManager.default.enumerator(atPath: path)
while let filename = enumerator?.nextObject() as? String {
print(filename)
}
Swift3 + absolute urls
extension FileManager {
func listFiles(path: String) -> [URL] {
let baseurl: URL = URL(fileURLWithPath: path)
var urls = [URL]()
enumerator(atPath: path)?.forEach({ (e) in
guard let s = e as? String else { return }
let relativeURL = URL(fileURLWithPath: s, relativeTo: baseurl)
let url = relativeURL.absoluteURL
urls.append(url)
})
return urls
}
}
Based on code from #user3441734
Swift 3
let fd = FileManager.default
fd.enumerator(atPath: "/Library/FileSystems")?.forEach({ (e) in
if let e = e as? String, let url = URL(string: e) {
print(url.pathExtension)
}
})
In case that you are getting the
'NSDirectoryEnumerator?' does not have a member named 'nextObject' error
the while loop should be:
while let element = enumerator?.nextObject() as? String {
// do things with element
}
It has something to do with optional chaining
SWIFT 3.0
Returns all files with extension in the Directory passed & its subdirectories
func extractAllFile(atPath path: String, withExtension fileExtension:String) -> [String] {
let pathURL = NSURL(fileURLWithPath: path, isDirectory: true)
var allFiles: [String] = []
let fileManager = FileManager.default
let pathString = path.replacingOccurrences(of: "file:", with: "")
if let enumerator = fileManager.enumerator(atPath: pathString) {
for file in enumerator {
if #available(iOS 9.0, *) {
if let path = NSURL(fileURLWithPath: file as! String, relativeTo: pathURL as URL).path, path.hasSuffix(".\(fileExtension)"){
let fileNameArray = (path as NSString).lastPathComponent.components(separatedBy: ".")
allFiles.append(fileNameArray.first!)
}
} else {
// Fallback on earlier versions
print("Not available, #available iOS 9.0 & above")
}
}
}
return allFiles
}
Updating for Swift 3:
let fileManager = FileManager() // let fileManager = NSFileManager.defaultManager()
let en=fileManager.enumerator(atPath: the_path) // let enumerator:NSDirectoryEnumerator = fileManager.enumeratorAtPath(folderPath)
while let element = en?.nextObject() as? String {
if element.hasSuffix("ext") {
// do something with the_path/*.ext ....
}
}
Adding to vadian's response -- the Apple docs mention that Path-based URLs are simpler in some ways, however file reference URLs have the advantage that the reference remains valid if the file is moved or renamed while your app is running.
From the documentation for "Accessing Files and Directories":
"Path-based URLs are easier to manipulate, easier to debug, and are generally preferred by classes such as NSFileManager. An advantage of file reference URLs is that they are less fragile than path-based URLs while your app is running. If the user moves a file in the Finder, any path-based URLs that refer to the file immediately become invalid and must be updated to the new path. However, as long as the file moved to another location on the same disk, its unique ID does not change and any file reference URLs remain valid."
https://developer.apple.com/library/content/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/AccessingFilesandDirectories/AccessingFilesandDirectories.html
If you want to categorically check whether an element is a file or a subdirectory:
let enumerator = FileManager.default.enumerator(atPath: contentsPath);
while let element = enumerator?.nextObject() as? String {
if(enumerator?.fileAttributes?[FileAttributeKey.type] as! FileAttributeType == FileAttributeType.typeRegular){
//this is a file
}
else if(enumerator?.fileAttributes?[FileAttributeKey.type] as! FileAttributeType == FileAttributeType.typeDirectory){
//this is a sub-directory
}
}
Recently struggled with this when handling an array of urls, whether they be a directory or not (eg. drag and drop). Ended up with this extension in swift 4, may be of use
extension Sequence where Iterator.Element == URL {
var handleDir: [URL] {
var files: [URL] = []
self.forEach { u in
guard u.hasDirectoryPath else { return files.append(u.resolvingSymlinksInPath()) }
guard let dir = FileManager.default.enumerator(at: u.resolvingSymlinksInPath(), includingPropertiesForKeys: nil) else { return }
for case let url as URL in dir {
files.append(url.resolvingSymlinksInPath())
}
}
return files
}
}
Avoid reference URLs, while they do have some advantages as stated above, they eat system resources and if you’re enumerating a large filesystem (not that large actually) your app will hit a system wall quickly and get shutdown by macOS.

Iterate through files in a folder and its subfolders using Swift's FileManager

I'm quite new to programming a Swift and I'm trying to iterate through the files in a folder.
I took a look at the answer here and tried to translate it to Swift syntax, but didn't succeed.
let fileManager = NSFileManager.defaultManager()
let enumerator:NSDirectoryEnumerator = fileManager.enumeratorAtPath(folderPath)
for element in enumerator {
//do something
}
the error I get is:
Type 'NSDirectoryEnumerator' does not conform to protocol 'SequenceType'
My aim is to look at all the subfolders and files contained into the main folder and find all the files with a certain extension to then do something with them.
Use the nextObject() method of enumerator:
while let element = enumerator?.nextObject() as? String {
if element.hasSuffix("ext") { // checks the extension
}
}
Nowadays (early 2017) it's highly recommended to use the – more versatile – URL related API
let fileManager = FileManager.default
do {
let resourceKeys : [URLResourceKey] = [.creationDateKey, .isDirectoryKey]
let documentsURL = try fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
let enumerator = FileManager.default.enumerator(at: documentsURL,
includingPropertiesForKeys: resourceKeys,
options: [.skipsHiddenFiles], errorHandler: { (url, error) -> Bool in
print("directoryEnumerator error at \(url): ", error)
return true
})!
for case let fileURL as URL in enumerator {
let resourceValues = try fileURL.resourceValues(forKeys: Set(resourceKeys))
print(fileURL.path, resourceValues.creationDate!, resourceValues.isDirectory!)
}
} catch {
print(error)
}
I couldn't get pNre's solution to work at all; the while loop just never received anything. However, I did come across this solution which works for me (in Xcode 6 beta 6, so perhaps things have changed since pNre posted the above answer?):
for url in enumerator!.allObjects {
print("\((url as! NSURL).path!)")
}
my two cents from previously anwers.. more swifty and with optionals:
let enumerator = FileManager.default.enumerator(atPath: folderPath)
while let element = enumerator?.nextObject() as? String {
print(element)
if let fType = enumerator?.fileAttributes?[FileAttributeKey.type] as? FileAttributeType{
switch fType{
case .typeRegular:
print("a file")
case .typeDirectory:
print("a dir")
}
}
}
returns all files in a directory + in subdirectories
import Foundation
let path = "<some path>"
let enumerator = FileManager.default.enumerator(atPath: path)
while let filename = enumerator?.nextObject() as? String {
print(filename)
}
Swift3 + absolute urls
extension FileManager {
func listFiles(path: String) -> [URL] {
let baseurl: URL = URL(fileURLWithPath: path)
var urls = [URL]()
enumerator(atPath: path)?.forEach({ (e) in
guard let s = e as? String else { return }
let relativeURL = URL(fileURLWithPath: s, relativeTo: baseurl)
let url = relativeURL.absoluteURL
urls.append(url)
})
return urls
}
}
Based on code from #user3441734
Swift 3
let fd = FileManager.default
fd.enumerator(atPath: "/Library/FileSystems")?.forEach({ (e) in
if let e = e as? String, let url = URL(string: e) {
print(url.pathExtension)
}
})
In case that you are getting the
'NSDirectoryEnumerator?' does not have a member named 'nextObject' error
the while loop should be:
while let element = enumerator?.nextObject() as? String {
// do things with element
}
It has something to do with optional chaining
SWIFT 3.0
Returns all files with extension in the Directory passed & its subdirectories
func extractAllFile(atPath path: String, withExtension fileExtension:String) -> [String] {
let pathURL = NSURL(fileURLWithPath: path, isDirectory: true)
var allFiles: [String] = []
let fileManager = FileManager.default
let pathString = path.replacingOccurrences(of: "file:", with: "")
if let enumerator = fileManager.enumerator(atPath: pathString) {
for file in enumerator {
if #available(iOS 9.0, *) {
if let path = NSURL(fileURLWithPath: file as! String, relativeTo: pathURL as URL).path, path.hasSuffix(".\(fileExtension)"){
let fileNameArray = (path as NSString).lastPathComponent.components(separatedBy: ".")
allFiles.append(fileNameArray.first!)
}
} else {
// Fallback on earlier versions
print("Not available, #available iOS 9.0 & above")
}
}
}
return allFiles
}
Updating for Swift 3:
let fileManager = FileManager() // let fileManager = NSFileManager.defaultManager()
let en=fileManager.enumerator(atPath: the_path) // let enumerator:NSDirectoryEnumerator = fileManager.enumeratorAtPath(folderPath)
while let element = en?.nextObject() as? String {
if element.hasSuffix("ext") {
// do something with the_path/*.ext ....
}
}
Adding to vadian's response -- the Apple docs mention that Path-based URLs are simpler in some ways, however file reference URLs have the advantage that the reference remains valid if the file is moved or renamed while your app is running.
From the documentation for "Accessing Files and Directories":
"Path-based URLs are easier to manipulate, easier to debug, and are generally preferred by classes such as NSFileManager. An advantage of file reference URLs is that they are less fragile than path-based URLs while your app is running. If the user moves a file in the Finder, any path-based URLs that refer to the file immediately become invalid and must be updated to the new path. However, as long as the file moved to another location on the same disk, its unique ID does not change and any file reference URLs remain valid."
https://developer.apple.com/library/content/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/AccessingFilesandDirectories/AccessingFilesandDirectories.html
If you want to categorically check whether an element is a file or a subdirectory:
let enumerator = FileManager.default.enumerator(atPath: contentsPath);
while let element = enumerator?.nextObject() as? String {
if(enumerator?.fileAttributes?[FileAttributeKey.type] as! FileAttributeType == FileAttributeType.typeRegular){
//this is a file
}
else if(enumerator?.fileAttributes?[FileAttributeKey.type] as! FileAttributeType == FileAttributeType.typeDirectory){
//this is a sub-directory
}
}
Recently struggled with this when handling an array of urls, whether they be a directory or not (eg. drag and drop). Ended up with this extension in swift 4, may be of use
extension Sequence where Iterator.Element == URL {
var handleDir: [URL] {
var files: [URL] = []
self.forEach { u in
guard u.hasDirectoryPath else { return files.append(u.resolvingSymlinksInPath()) }
guard let dir = FileManager.default.enumerator(at: u.resolvingSymlinksInPath(), includingPropertiesForKeys: nil) else { return }
for case let url as URL in dir {
files.append(url.resolvingSymlinksInPath())
}
}
return files
}
}
Avoid reference URLs, while they do have some advantages as stated above, they eat system resources and if you’re enumerating a large filesystem (not that large actually) your app will hit a system wall quickly and get shutdown by macOS.