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 */
}
}
Related
I'm trying to make a file downloader application using swift and cocoa. I am using plist for the download history. Reading the data works however, writing the data will erase the previous data and replace it for the new data.
Here is the code
let newdownloaditem = downloadList(root: [downloadListt(downloadURL: response.url!.absoluteString, fileName: response.suggestedFilename!)])
// This is a codeable method
let encoder = PropertyListEncoder()
encoder.outputFormat = .xml
let pListFilURL = uniqueDataDir()?.appendingPathComponent("downloads.plist")
do {
let data = try encoder.encode(newdownloaditem)
try data.write(to: pListFilURL!)
// Here is the problem
} catch {
print(error)
}
// Here is the codeable
public struct downloadList: Codable {
let root: [downloadListt]
}
public struct downloadListt: Codable {
let downloadURL: String
let fileName: String
}
Here is an image of what happens
The contents got erased
Thanks!
You are indeed replacing the previous data with your new one.
You need to retrieve the previous data.
Append your new data to it.
Save that combination
let newItem = downloadListt(downloadURL: response.url!.absoluteString,
fileName: response.suggestedFilename!)
var allItems: [downloadListt] = []
allItems.append(contentsOf: previousList.root)
allitems.append(newItem)
let newList = downloadList(root: allItems)
...
let data = try encoder.encode(newList)
try data.write(to: pListFilURL!)
Unrelated but recommended (it's convention):
You should starting naming your struct/classes with an uppercase: downloadList => DownloadList
I would avoid naming downloadListt, it's unredable it's hard at first look to make a difference between downloadListt and downloadList. Instead, name it maybe DownloadItem. More readable.
This question already has answers here:
Read and write a String from text file
(21 answers)
File write with [NSBundle mainBundle] fails
(5 answers)
Closed 5 years ago.
I got a problem when I was trying to write data into a csv file.
The app reads data from a csv file, and during the application running, the data will change, and when it changes, it will call a function to write the data back to the csv file, but it doesn't work. thanks a lot!!
The code is listed below
var content = ""
if content != ""{
print("have content")
}
for tag in tags{
let line = "\(tag.birdIndex),\(tag.isSeen)\n"
content = content + line
}
if let path = Bundle.main.path(forResource: "birdLogVersion1", ofType: "csv"){
do {
let data = try String(contentsOfFile: path, encoding: String.Encoding.utf8)
print(data)
}catch{
}
//let url = NSURL(fileURLWithPath: path)
do {
try content.write(toFile: path, atomically: true, encoding: String.Encoding.utf8)
//(to: url as URL, atomically: true, encoding: String.Encoding.utf8)
print("tried write")
}catch{
//error
}
}else{
}
when I call this function again, print(data) shows that data has already been written into this csv file, but when I close the app, and restart it again, the csv file still be the same, didn't change at all.
Do I have to do something like save file after write data into a csv file?
Is it right to write info into the csv file in app?
Thanks again!
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.
I want to write log file at my extension, and read it at my app.
For this purpose, I'm using shared groups (so both the app and the extension would be able to read from the same file)
I wrote the following code:
Extension:
let fileManager = NSFileManager.defaultManager()
let containerUrl = fileManager.containerURLForSecurityApplicationGroupIdentifier("group.MyCompany.MyProj")
let extensionLogDirectory = containerUrl?.path?.stringByAppendingString("AppExtensionLogs")
let logFileManager = DDLogFileManagerDefault(logsDirectory: extensionLogDirectory)
PacketTunnelProvider.fileLogger = DDFileLogger(logFileManager: logFileManager)
PacketTunnelProvider.fileLogger!.rollingFrequency = 60*60*12
PacketTunnelProvider.fileLogger!.logFileManager.maximumNumberOfLogFiles = 1
DDLog.addLogger(PacketTunnelProvider.fileLogger)
App (just to read the log file):
let fileManager = NSFileManager.defaultManager()
let containerUrl = fileManager.containerURLForSecurityApplicationGroupIdentifier("group.MyCompany.MyProj")
if let extensionLogDirectory = containerUrl?.path?.stringByAppendingString("AppExtensionLogs") {
do {
let directoryContents = try fileManager.contentsOfDirectoryAtPath(extensionLogDirectory)//always fails
for file in directoryContents {
let path = extensionLogDirectory.stringByAppendingString(file)
do {
let fileContents = try NSString(contentsOfFile: path, encoding: NSUTF8StringEncoding)
NSLog("file: \(fileContents)")
}
catch {/* error handling here */
}
}
}
catch {/* error handling here */
NSLog("nope!")
}
But, something now right - it's seems like contentsOfDirectoryAtPath always fails with "no such file" error
What's wrong in this code?
The problem is unrelated to app extensions or CocoaLumberjack.
stringByAppendingString just concatenates strings, so that the path
separator "/" is missing in the generated directory name.
There was a dedicated method stringByAppendingPathComponent, which however
has been deprecated in Objective-C and is no longer available in Swift.
You should operate on the URL by using URLByAppendingPathComponent
instead:
let extensionLogDirectory = containerUrl?.URLByAppendingPathComponent("AppExtensionLogs").path
I've put a "restaurants.txt" file in the same directory with MenuViewController.swift.
Then I put this code in viewDidLoad() of MenuViewController to read the file.
if let path = NSBundle.mainBundle().pathForResource("restaurants", ofType: "txt") {
if let content = try? String(contentsOfFile: path, encoding: NSUTF8StringEncoding) {
print(content)
} else {
print("error1")
}
} else {
print("error")
}
Then I get "error1" in my console.
How can I read the file in the same directory of the swift file?
Or do I have to put my "restaurants.txt" file somewhere else?
Add the file to the target:
Select the file
In the Project Navigator command1 select the file icon
Under "Target Membership" click the checkbox for the file.
1) If you wish to read content of a text file, even without inserting it to your project, you may also use NSFileMananger. The following code works, the only drawback - you need to state full path.
let fullPath = "/your/full/path/restaurants.txt"
if NSFileManager.defaultManager().fileExistsAtPath(fullPath){
do
{
let content = try String(contentsOfFile: fullPath, encoding: NSUTF8StringEncoding)
print("\(content)")
}
catch let error as NSError
{
print("error reading file: \(error)")
}
}
2) If you do mind the hardcode, and want it more generic - you will have to change Settings in the Build in Xcode. See here:
Locate source project directory in Swift