I am using NSUserDefaults.standardUserDefaults() to save my JSON String got from WebService to iPhone memory. When I load it to use with my parser function, the processing speed is so slow. I don't want to use RealmIO or any database because that thing doesn't necessary. I would like to ask is there any way faster than NSUserDefaults? Please check my JSON file (I need store more than 20 files like that)
Instead of saving your data to NSUserDefaults, you should save it to a different file, this will be much more efficient.
Here is how you can do it :
// Build file url
let documentsURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).last!
let fileURL = documentsURL.URLByAppendingPathComponent("file_1.json", isDirectory: false)
// Write
let jsonString = "..."
let jsonData = jsonString.dataUsingEncoding(NSUTF8StringEncoding)
do {
try jsonData?.writeToURL(fileURL, options: NSDataWritingOptions())
} catch {
NSLog("Writing file to `\(fileURL)` failed with error : \(error)")
}
// Read
do {
let jsonData = try NSData(contentsOfURL: fileURL, options: NSDataReadingOptions())
let jsonString = String(data: jsonData, encoding: NSUTF8StringEncoding)
} catch {
NSLog("Reading file at url `\(fileURL)` failed with error : \(error)")
}
NSUserDefaults is not a database. If your JSON documents are more than 100 KB altogether store them into separate documents. Note that every time you change any user default, all the user defaults have to be written to a file. If you have 20 JSON documents of 1MB each, that's writing 20MB or more for every user default the you change.
Related
I am trying to save an array of CKRecords to the documents directory in
order to have fast startup and offline access.
Downloading the CKRecords from CloudKit works fine and I am able to use the CKAsset in each record without issue. However, when I save the array of CKRecords that I downloaded to a local file, the CKAsset is not included in the data file. I can tell this from the size of the file saved to the documents directory. If I reconstitute the disk file into an array of CKRecords, I can retrieve all of the fields except the CKAsset. Other than the system fields, and the CKAsset field, all of the fields are Strings.
For testing - I have 10 CloudKit records each with six small String fields
and a CKAsset which is about 500KB. When I check the size of the
resulting file in documents the file size is about 15KB.
Here's the function to save the array. AppDelegate.ckStyleRecords is a
static array of the downloaded CKRecords.
func saveCKStyleRecordsToDisk() {
if AppDelegate.ckStyleRecords.count != 0 {
let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let docsDirectoryURL = urls[0]
let ckStyleURL = docsDirectoryURL.appendingPathComponent("ckstylerecords.data")
do {
let data : Data = try NSKeyedArchiver.archivedData(withRootObject: AppDelegate.ckStyleRecords, requiringSecureCoding: true)
try data.write(to: ckStyleURL, options: .atomic)
print("data write ckStyleRecords successful")
} catch {
print("could not save ckStyleRecords to documents directory")
}
}//if count not 0
}//saveCKStyleRecordsToDisk
Here is the function to reconstitute the array.
func checkForExistenceOfCKStyleRecordsInDocuments(completion: #escaping ([CKRecord]) -> Void) {
let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let docsDirectoryURL = urls[0]
let ckStyleURL = docsDirectoryURL.appendingPathComponent("ckstylerecords.data")
var newRecords : [CKRecord] = []
if FileManager.default.fileExists(atPath: ckStyleURL.path) {
do {
let data = try Data(contentsOf:ckStyleURL)
//yes, I know this has been deprecated, but I can't seem to get the new format to work
if let theRecords: [CKRecord] = try NSKeyedUnarchiver.unarchiveObject(with: data) as? [CKRecord] {
newRecords = theRecords
print("newRecords.count is \(newRecords.count)")
}
} catch {
print("could not retrieve ckStyleRecords from documents directory")
}
}//if exists
completion(newRecords)
}//checkForExistenceOfckStyleRecordsInDocuments
Calling the above:
kAppDelegate.checkForExistenceOfCKStyleRecordsInDocuments { (records) in
print("in button press and records.count is \(records.count)")
//this is just for test
for record in records {
print(record.recordID.recordName)
}
AppDelegate.ckStyleRecords = records
}//completion block
Upon refreshing the tableView that uses the ckStyleRecords array, all data
seems correct except the CKAsset (which in this case is a SceneKit
scene) is of course missing.
Any guidance would be appreciated.
A CKAsset was just a file reference. the fileURL property of the CKAsset is where the actual file is located. If you save a SKAsset then you only save the reference to the file. When doing that you do have to remember that this url is on a cache location which could be cleared if you are low on space.
You could do 2 things.
1. when reading your backup CKAsset, then also check if the file is located at the fileURL location. If the file is not there, then read it again from CloudKit.
2. Also backup the file from the fileURl to your documents folder. When you read your CKAsset from your backup, then just don't read the file from fileURL but the location where you have put it in your documents filter.
Can I get storyboard XML source code programmatically somehow and send it to server?
For example:
let sourceCode = storyboard.getCode()
Or maybe can I send the whole file to server and have the server parse it by itself?
Well you can read the content of the storyboard-file if you know its name.
let filePath = NSBundle.mainBundle().URLForResource("MyStoryboard", withExtension: "storyboard")
let fileURL = NSURL(string: filePath)
do {
let filecontent = try String(contentsOf: fileURL, encoding: .utf8)
//submit filecontent to your server
}
catch {/* error handling here */}
This will give you the filecontent as a string. If you need to parse it as XML you will need to use some kind of XML-parser like e.g. NSXMLParser.
I'm struggling to get an image file loaded in Swift 3.
Here is the code:
do {
let imageData = try Data(contentsOf: imageUrl2.asURL())
} catch {
print ("loading image file error")
}
And the current Url String is:
file:///Users/veikoherne/Library/Developer/CoreSimulator/Devices/889A08D5-B8CC-458C-99FF-643A4BA1A806/data/Containers/Data/Application/F64ED326-7894-4EE7-AA3B-B1BB10DF8259/Documents/img2017-03-23 17:39:24.jpg
and obviously I have checked that this file exists and is valid image. It always ends up telling me "loading image file error". Anyone have experiences loading local data in Swift 3?
The answer mentioned was using NSData object and probably Swift 2. Current Swift 3 refuses to bridge NSData to Data, that's why I have to use Data.
Loading data from local file you should use "contentsOfFile:" method.
Reference link: https://www.hackingwithswift.com/example-code/strings/how-to-load-a-string-from-a-file-in-your-bundle
So in case of reading data you can use:
Data(contentsOf: <URL>, options: <Data.ReadingOptions>)
Reading a plain text as a String, use:
String(contentsOfFile: <LocalFileDirPath>)
Reading an image from document directory, use:
UIImage(contentsOfFile: <LocalFileDirPath>)
Hope this would be helpful!
I experienced the same issue when trying to retrieve a file that I just downloaded. If you have saved a file from some url like I did, this should work:
let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
let localUrl = documentDirectory.appendingPathComponent("somefile.txt")
if FileManager.default.fileExists(atPath: localUrl.path){
if let cert = NSData(contentsOfFile: localUrl.path) {
return cert as Data
}
}
Swift 5 version.
func loadFileFromLocalPath(_ localFilePath: String) ->Data? {
return try? Data(contentsOf: URL(fileURLWithPath: localFilePath))
}
I've some irregular huge amount of data in a text file, and by some crucial logics I've made it just like the way I needs. Now on every time the app loads the whole process if could be performed will cost deficiency up to remarkable extent.
So what should I do in order to store this regular data for one permanent time, so then through that I should retrieve when and where I got need of it?
Data is an unmodifiable pure in text format.
Okay your question is very vague, but to give you a head start you can save data to a file and then read from it like this:
let file = "file.txt" //this is the file we will write to and read from it
let text = "some text" //your data/text
if let dir : NSString = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.AllDomainsMask, true).first {
let path = dir.stringByAppendingPathComponent(file);
//writing
do {
try text.writeToFile(path, atomically: false, encoding: NSUTF8StringEncoding)
}
catch {
/* error handling here */
}
//reading
do {
let textOut = try NSString(contentsOfFile: path, encoding: NSUTF8StringEncoding)
//use textOut - your content
}
catch {
/* error handling here */
}
}
So my app is using a JSON API response to read some data from server. Since the API is not yet ready i want to use a sample JSON file which is stored in my app itself. Since all my code uses SwiftyJSON i want to stick to it for the local file.
I am doing the following in my ViewDidLoad method and somehow this end result is a blank array. Do you know what should i change in this code to make it work?
//Direct JSON
if let path = NSBundle.mainBundle().pathForResource("XXYYXZZfile", ofType: "json")
{
do{
let pathAsData = try NSData(contentsOfFile: path, options: NSDataReadingOptions.DataReadingMappedIfSafe)
let json = JSON(pathAsData)
print(json)
} catch{
print("Some error")
}
}
When using SwiftyJSON with NSData you have to specify the "data:" parameter for the JSON initializer:
let json = JSON(data: pathAsData)
otherwise it tries to use other available initializers and will fail with an error or even, like in your case, will fail silently with a misinterpreted input and wrong output.