Writing swift dictionary to file
Anyway, when you want to store MyOwnType
to file, MyOwnType
must be a subclass of NSObject
and conforms to NSCoding
protocol. like this:
class MyOwnType: NSObject, NSCoding {
var name: String
init(name: String) {
self.name = name
}
required init(coder aDecoder: NSCoder) {
name = aDecoder.decodeObjectForKey("name") as? String ?? ""
}
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(name, forKey: "name")
}
}
Then, here is the Dictionary
:
var dict = [Int : [Int : MyOwnType]]()
dict[1] = [
1: MyOwnType(name: "foobar"),
2: MyOwnType(name: "bazqux")
]
So, here comes your question:
Writing swift dictionary to file
You can use NSKeyedArchiver
to write, and NSKeyedUnarchiver
to read:
func getFileURL(fileName: String) -> NSURL {
let manager = NSFileManager.defaultManager()
let dirURL = manager.URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: false, error: nil)
return dirURL!.URLByAppendingPathComponent(fileName)
}
let filePath = getFileURL("data.dat").path!
// write to file
NSKeyedArchiver.archiveRootObject(dict, toFile: filePath)
// read from file
let dict2 = NSKeyedUnarchiver.unarchiveObjectWithFile(filePath) as [Int : [Int : MyOwnType]]
// here `dict2` is a copy of `dict`
But in the body of your question:
how can I write/read it to/from a plist file in swift?
In fact, NSKeyedArchiver
format is binary plist. But if you want that dictionary as a value of plist, you can serialize Dictionary
to NSData
with NSKeyedArchiver
:
// archive to data
let dat:NSData = NSKeyedArchiver.archivedDataWithRootObject(dict)
// unarchive from data
let dict2 = NSKeyedUnarchiver.unarchiveObjectWithData(data) as [Int : [Int : MyOwnType]]
Write Dictionary to File in Swift
URL(string
is the wrong API. It requires that the string starts with a scheme like https://
.
In the file system where paths starts with a slash you must use
let url = URL(fileURLWithPath: path)
as WithPath implies.
A swiftier way is PropertyListEncoder
extension Dictionary where Key: Encodable, Value: Encodable {
func writeToURL(_ url: URL) throws {
// archive data
let data = try PropertyListEncoder().encode(self)
try data.write(to: url)
}
}
How to write Dictionary to a file?
First of all filePath?.absoluteString
returns the entire – even percent escaped – string including the file://
scheme and the method expects a path without the scheme (filePath?.path
- the naming is a bit confusing ;-) ).
I recommend to save a [String:String]
dictionary as property list file. It's not necessary to create the file explicitly.
I changed the signatures of the methods slightly in the Swift-3-way. Further there is no need to use any optional type.
func store(dictionary: Dictionary<String, String>, in fileName: String, at directory: String) -> Bool {
let fileExtension = "plist"
let directoryURL = create(directory:directory)
do {
let data = try PropertyListSerialization.data(fromPropertyList: dictionary, format: .xml, options: 0)
try data.write(to: directoryURL.appendingPathComponent(fileName).appendingPathExtension(fileExtension))
return true
} catch {
print(error)
return false
}
}
func create(directory: String) -> URL {
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let directoryURL = documentsDirectory.appendingPathComponent(directory)
do {
try FileManager.default.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil)
} catch let error as NSError {
fatalError("Error creating directory: \(error.localizedDescription)")
}
return directoryURL
}
PS: Instead of returning a Bool
you could make the store method can throw and handle the error in the calling method:
func store(dictionary: Dictionary<String, String>, in fileName: String, at directory: String) throws {
let fileExtension = "plist"
let directoryURL = create(directory:directory)
let data = try PropertyListSerialization.data(fromPropertyList: dictionary, format: .xml, options: 0)
try data.write(to: directoryURL.appendingPathComponent(fileName).appendingPathExtension(fileExtension))
}
Swift 4 How do I write/read dictionaries to/from the document directory?
Dictionary has its own write method which writes a property list representation of the contents of the dictionary to a given URL. You can do it using below code:
Write
// Get application document directory path array
let paths = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.allDomainsMask, true)
let fileName = "users"
if let documentPath = paths.first {
let filePath = NSMutableString(string: documentPath).appendingPathComponent(fileName)
let URL = NSURL.fileURL(withPath: filePath)
let dictionary = NSMutableDictionary(capacity: 0)
dictionary.setValue("valu1", forKey: "key1")
dictionary.setValue("valu2", forKey: "key2")
let success = dictionary.write(to: URL, atomically: true)
print("write: ", success)
}
Read
if let dictionary = NSMutableDictionary(contentsOf: URL){
print(dictionary)
}
Hope, it will work.
How to write an array of dictionaries to a .txt file in Xcode Project
If all you want to do is output your Array of Dictionaries in an "orderly / readable" format, you can use this:
if let orderedKeys = finalDictionary.first?.keys {
var outputString = ""
var i = 1
for d in finalDictionary {
outputString += "Booking \(i):\n"
for k in orderedKeys {
outputString += k + ": " + (d[k] ?? "(no value)") + "\n"
}
outputString += "\n"
i += 1
}
print(outputString)
// or, write outputString to a text file
}
Note: this is just quick code... not necessarily optimal or 100% error free, and assumes all records have the same keys. More of a "here's a direction to go" kinda thingy :)
Edit: If the dictionaries may have variable sets of keys, you can do this:
var outputString = ""
var i = 1
for d in finalDictionary {
outputString += "Booking \(i):\n"
for k in d.keys {
outputString += k + ": " + (d[k] ?? "(no value)") + "\n"
}
outputString += "\n"
i += 1
}
print(outputString)
Edit 2: probably a little "Swiftier" way of doing it:
outputString = ""
i = 1
finalDictionary.forEach {
outputString += "Booking \(i):\n"
$0.forEach { outputString += "\($0): \($1)\n" }
outputString += "\n"
i += 1
}
print(outputString)
Because Dictionaries are unordered, you may end up with:
Booking 1:
EmailAddress: johnmm@gmail.com
PhoneNumber: 94949392
FullName: John
.
Booking 2:
SomeOtherKey: Some Value
FullName: Dave
EmailAddress: dave@gmail.com
.
Booking 3:
FullName: Chris
PhoneNumber: 202583963
SomeOtherKey: Some Other Value
EmailAddress: chris@gmail.com
and so on.
Write dictionary in Bundle's json file, Swift 4, iOS
You cannot edit an embedded file. You have to first copy it from Bundle to Documents
directory and then work ( edit ) on this file
How to save/retrieve dictionary from file using SwiftyJSON
I finally got this to work. The biggest problem was that I couldn't convert collaborationDictionary to JSON. I finally had to convert it to a dictionary of arrays vs dictionary of sets. Here are the 2 methods:
// **************************************************************************
func getUploadedFileSet() {
let documentsURL = URL(string: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0])
let appURL = documentsURL?.appendingPathComponent(APP_DISTINGUISHED_NAME)
let jsonFileURL = appURL?.appendingPathComponent(UPLOADED_ITEMS_DB_JSON)
if FileManager.default.fileExists(atPath: (jsonFileURL?.absoluteString)!) {
do {
let data = try Data(contentsOf: jsonFileURL!, options: .alwaysMapped)
let json = JSON(data: data)
if json != nil {
for (key, subJson) in json[kCollaborations] {
let stringArray:[String] = subJson.arrayValue.map { $0.string! }
let stringSet = Set(stringArray)
collaborationDictionary.updateValue(stringSet, forKey: key)
}
} else {
print("Could not get json from file, make sure that file contains valid json.")
}
} catch let error {
print(error.localizedDescription)
}
}
}
// **************************************************************************
func saveUploadedFilesSet() {
let documentsURL = URL(string: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0])
let appURL = documentsURL?.appendingPathComponent(APP_DISTINGUISHED_NAME)
let jsonFileURL = appURL?.appendingPathComponent(UPLOADED_ITEMS_DB_JSON)
let adjustedJSONFileURL = URL(fileURLWithPath:(jsonFileURL?.absoluteString)!)
do {
let dirExists = FileManager.default.fileExists(atPath: (appURL?.absoluteString)!)
if !dirExists {
try FileManager.default.createDirectory(atPath: (appURL?.absoluteString)!, withIntermediateDirectories: false, attributes: nil)
}
// Convert set elements to arrays
var convertedCollaborationDictionary: [String:[String]] = [:]
for (sessionID, fileNameSet) in collaborationDictionary {
let array = Array(fileNameSet)
convertedCollaborationDictionary.updateValue(array, forKey: sessionID)
}
let json: JSON = JSON(convertedCollaborationDictionary)
let fullJSON: JSON = [kCollaborations:json.object]
let data = try fullJSON.rawData()
try data.write(to: adjustedJSONFileURL, options: .atomic)
} catch let error as NSError {
print(error.localizedDescription);
}
}
How to add Dictionary to Plist in SWIFT
You need to convert Dictionary to NSDictionary
so that you can use writeToFile
Try to cast with this
let succeed = (favoriteGooglelocations as NSDictionary).writeToFile(path, atomically: true)
If succeed is false,check your GoogleItems class,it should conforms to NSCoding
Update,this is the code I test,you may refer to it
import UIKit
import CoreLocation
Set you GoogleItems
conforms to NSCoding
class GoogleItems: NSObject,NSCoding {
override init() {}
var name:String?
var address:String?
var distance : String?
var googleLocation: CLLocation?
var isStar : Bool?
required init(coder aDecoder: NSCoder) {
self.name = aDecoder.decodeObjectForKey("name") as? String
self.address = aDecoder.decodeObjectForKey("address") as? String
self.distance = aDecoder.decodeObjectForKey("distance") as? String
self.googleLocation = aDecoder.decodeObjectForKey("googleLocation") as? CLLocation
self.isStar = aDecoder.decodeObjectForKey("isStar") as? Bool
}
func encodeWithCoder(aCoder: NSCoder) {
if name != nil{ aCoder.encodeObject(name, forKey: "name")}
if address != nil{ aCoder.encodeObject(address, forKey: "address")}
if distance != nil { aCoder.encodeObject(distance, forKey: "distance")}
if googleLocation != nil { aCoder.encodeObject(googleLocation, forKey: "googleLocation")}
if isStar != nil {aCoder.encodeBool(isStar!, forKey: "isStar")}
}
}
Then write to file
var favoriteGooglelocations = [Int:GoogleItems]()
var item = GoogleItems()
item.isStar = true
item.name = "name"
item.address = "address"
item.googleLocation = nil
item.distance = "23"
favoriteGooglelocations[1] = item
let dic = favoriteGooglelocations as NSDictionary
let paths = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)
let documentDirectory = paths[0] as! String
let path = documentDirectory.stringByAppendingPathComponent("Favorites.plist")
let succeed = NSKeyedArchiver.archiveRootObject(dic, toFile: path)
println(succeed)
Read from file
let paths = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)
let documentDirectory = paths[0] as! String
let path = documentDirectory.stringByAppendingPathComponent("Favorites.plist")
let dic = NSKeyedUnarchiver.unarchiveObjectWithFile(path) as? [Int:GoogleItems]
if(dic != nil){
for (key,value) in dic!{
let item = value
println(item.name)
println(item.address)
}
}
Related Topics
How to Change Navigation Bar & Back Button Colour iOS 15
Apple's Description of Reference and Value Types with Multiple Threads
Xcode Beta 6 "Type of Expression Is Ambiguous Without More Context" Navigationlink
Convert Character to Integer in Swift
Swift 4.2 Setter Getter, All Paths Through This Function Will Call Itself
Swift String Interpolation Displaying Optional
Adding Local Dependencies in Xcode11 Using Spm
Parsing a Iso8601 String to Date in Swift
Getting String Name of Objective-C @Objc Enum Value in Swift
How to Cache Images Using Urlsession in Swift
Swift 3: Convert a Null-Terminated Unsafepointer<Uint8> to a String
Swift 5.5: Asynchronously Iterating Line-By-Line Through a File
Value of Optional Type Cgfloat Not Unwrapped Error in Swift
Optional Field Type Doesn't Conform Protocol in Swift 3
Programmatically Creating Constraints Bound to View Controller Margins
How to Create a Window with Transparent Background with Swift on Osx