I'm trying to save my struct array. And I read all posts I could find for this topic. Finally I used one example and stripped it a little bit, but it doesn't work. It does not save, so it could not read back:
func path() -> String {
return URL(fileURLWithPath: "/Volumes/MacOS/fasttemp/Test.TXT").absoluteString
}
struct Movie {
let name: String
let releaseYear: Int
}
protocol Dictionariable {
func dictionaryRepresentation() -> NSDictionary
init?(dictionaryRepresentation: NSDictionary?)
}
extension Movie: Dictionariable {
func dictionaryRepresentation() -> NSDictionary {
let representation: [String: AnyObject] = [
"name": name as AnyObject,
"releaseYear": releaseYear as AnyObject
]
return representation as NSDictionary
}
init?(dictionaryRepresentation: NSDictionary?) {
guard let values = dictionaryRepresentation else {return nil}
if let name = values["name"] as? String,
let releaseYear = values["releaseYear"] as? Int {
self.name = name
self.releaseYear = releaseYear
} else {
return nil
}
}
}
func extractStructureFromArchive<T: Dictionariable>() -> T? {
guard let encodedDict = NSKeyedUnarchiver.unarchiveObject(withFile: path()) as? NSDictionary else {return nil}
return T(dictionaryRepresentation: encodedDict)
}
func archiveStructure<T: Dictionariable>(structure: T) {
let encodedValue = structure.dictionaryRepresentation()
NSKeyedArchiver.archiveRootObject(encodedValue, toFile: path())
}
func extractStructuresFromArchive<T: Dictionariable>() -> [T] {
guard let encodedArray = NSKeyedUnarchiver.unarchiveObject(withFile: path()) as? [AnyObject] else {return []}
return encodedArray.map{$0 as? NSDictionary}.flatMap{T(dictionaryRepresentation: $0)}
}
func archiveStructureInstances<T: Dictionariable>(structures: [T]) {
let encodedValues = structures.map{$0.dictionaryRepresentation()}
NSKeyedArchiver.archiveRootObject(encodedValues, toFile: path())
}
let movies = [
Movie(name: "Avatar", releaseYear: 2009),
Movie(name: "The Dark Knight", releaseYear: 2008)
]
// this fails!
archiveStructureInstances(structures: movies)
let someArray: [Movie] = extractStructuresFromArchive()
print("\(someArray[0].name)")
No file is created. The folder exist. What's wrong with it?
I'm using XCode 8.2.1 with Swift 3.
Added: I took this example from another question/answer. I also reduced it from 3 to 2 struct members. And I updated it for Swift 3. But it still doesn't work! That's the reason I'm asking.
Your path is wrong. You do not archive to a file. You have to use a directory.
Changing the path() methode to this (from this answer):
func path() -> String {
let documentsPath = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).first
let path = documentsPath! + "/Movie"
return path
}
your code prints Avatar as expected.
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)
}
}
honestly i am a noob in swift, but i have a project to do with this language, so, here is the code:
import Foundation
//let dirs = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true) as? [String];
//let dir = dirs![0];
var dir = "/home/guillermo/Escritorio/";
var file = "tokens";
var path = dir.stringByAppendingPathComponent(file);
if let archivoTokens = try? NSString(contentsOfFile: path as String, encoding: NSUTF8StringEncoding){
print(archivoTokens)
}
var lineasArchivoTokens:[String] = archivoTokens!.componentsSeparatedByString("\n");
var TOKENS:[String] = [String]();
var count = lineasArchivoTokens.count;
for index in 0..<count{
var tmpList:[String] = (lineasArchivoTokens[index]).componentsSeparatedByString(" ");
}
and this is the error:
error: use of unresolved identifier 'archivoTokens'
var lineasArchivoTokens = archivoTokens!.componentsSeparatedByString("\n");
As #thefredelement said, you cannot use archivoTokens outside the if let archivoTokens = ... { ... } block.
You can reflow your code like this:
if let archivoTokens = try? NSString(contentsOfFile: path as String, encoding: NSUTF8StringEncoding){
// note that archivoTokens here is not an Optional because it is garenteed not to be nil
// so the ! suffix is not needed
print(archivoTokens)
var lineasArchivoTokens:[String] = archivoTokens.componentsSeparatedByString("\n")
}
Or, in a more natural way, use guard:
guard let archivoTokens = try? NSString(contentsOfFile: path as String, encoding: NSUTF8StringEncoding) else {
// guard statement requires exit when condition not satisfied
// you can also use `return` if it's inside a function
fatalError("failed to read archivoTokens from file")
}
// now archivoTokens is a normal non-nil NSString
var lineasArchivoTokens:[String] = archivoTokens.componentsSeparatedByString("\n");
var TOKENS:[String] = [String]();
var count = lineasArchivoTokens.count;
for index in 0..<count{
var tmpList:[String] = (lineasArchivoTokens[index]).componentsSeparatedByString(" ");
}
I want to check whether my File is exist with just prefix of file name in SWIFT.
E.g
My file name is like Companies_12344
So after _ values are dynamic but "Companies_" is static.
How can i do that?
I have already done split filename code below
How can i check through NSFileManager for is exist file name with "Companies_"
My code below For split
func splitFilename(str: String) -> (name: String, ext: String)? {
if let rDotIdx = find(reverse(str), "_")
{
let dotIdx = advance(str.endIndex, -rDotIdx)
let fname = str[str.startIndex..<advance(dotIdx, -1)]
println("splitFilename >> Split File Name >>\(fname)")
}
return nil
}
I think this code you need:
let str = "Companies_12344"
if str.hasPrefix("Companies") {
println("Yes, this one has 'Companies' as a prefix")
let compos = str.componentsSeparatedByString("_")
if let file = compos.first {
println("There was a code after the prefix: \(file)")
var paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as! String
var yourPath = paths.stringByAppendingPathComponent("\(file)_")
var checkValidation = NSFileManager.defaultManager()
if (checkValidation.fileExistsAtPath(yourPath))
{
println("FILE AVAILABLE");
}
else
{
println("FILE NOT AVAILABLE");
}
}
}
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)
}