This question already has answers here:
How to open file and append a string in it, swift
(5 answers)
Closed 7 years ago.
I'm working on application where I need to append a new line when i touch the button on iPhone.
I did this all but every time it writes over what i have written.
How to append a new line each time I press the button save.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var myAge: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// get the documents folder url
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func writeNow(sender: UIButton) {
let documentDirectoryURL = try! NSFileManager.defaultManager().URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: true)
let fileDestinationUrl = documentDirectoryURL.URLByAppendingPathComponent("fileForIphoneAbdulla.txt")
var text = myAge.text
do {
try text!.writeToURL(fileDestinationUrl, atomically: true, encoding: NSUTF8StringEncoding)
print("file is saved succefully")
do {
let mytext = try String(contentsOfURL: fileDestinationUrl, encoding: NSUTF8StringEncoding)
print(mytext) // "some text\n"
} catch let error as NSError {
print("error loading from url \(fileDestinationUrl)")
print(error.localizedDescription)
}
} catch let error as NSError {
print("error writing to url \(fileDestinationUrl)")
print(error.localizedDescription)
}
}
}
The high level file methods are made to write the entire file.
To append you can use an NSFileHandle. It has methods like seekToEndOfFile to move the file pointer to the end and then writeData to add data to the end of the file.
BTW, your sample code is in Swift but you're using the jquery tag. I assume you're looking for help in Swift?
Related
I created a stop button that can collect data, which will be saved to the defined path after clicking the stop button. However, if I want to continue collecting after clicking the stop button, the data will be added to the original text file. (This makes senses as I only know how to define one path)
My question is: Would it be possible to ask the user and input a new file name and save as a new text file after each stop so that the data is not added to the original file?
Below is what I have for one defined path and stacking up the data:
#IBAction func stopbuttonTapped(_ btn: UIButton) {
do {
let username:String = user_name.text!
fpsTimer.invalidate() //turn off the timer
let capdata = captureData.map{$0.verticesFormatted}.joined(separator:"") //convert capture data to string
let dir: URL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last! as URL
let url = dir.appendingPathComponent("testing.txt") //name the file
try capdata.appendLineToURL(fileURL: url as URL)
let result = try String(contentsOf: url as URL, encoding: String.Encoding.utf8)
}
catch {
print("Could not write to file")
}
}
And the extension I use for string and data:
extension String {
func appendLineToURL(fileURL: URL) throws {
try (self).appendToURL(fileURL: fileURL)
}
func appendToURL(fileURL: URL) throws {
let data = self.data(using: String.Encoding.utf8)!
try data.append(fileURL: fileURL)
}
func trim() -> String
{
return self.trimmingCharacters(in: CharacterSet.whitespaces)
}
}
extension Data {
func append(fileURL: URL) throws {
if let fileHandle = FileHandle(forWritingAtPath: fileURL.path) {
defer {
fileHandle.closeFile()
}
fileHandle.seekToEndOfFile()
fileHandle.write(self)
}
else {
try write(to: fileURL, options: .atomic)
}
}
}
Do I need to set a default file name (maybe texting.txt) and then popped up a user input for saving the text file? (That's where I am not too sure how to integrate to what I already have). I thank you so much for your time and suggestions in advance.
You could generate unique names.
For example:
let url = dir.appendingPathComponent("testing-\(Date.timeIntervalSinceReferenceDate).txt")
or
let url = dir.appendingPathComponent("testing-\(UUID().uuidString).txt")
I got an error while reading json content from remote url and printing on main interface in iOS Simulator.
MBP13"2016 && Mojave 10.14.6 && xcode 10.3(10G8) && swift 5
Here is the code sample. I had change a bit from "https://www.simplifiedios.net/swift-json-tutorial/"
import UIKit
class ViewController: UIViewController {
//the json file url
let URL_POSTS = "https://demo.ghost.io/ghost/api/v2/content/posts/?key=22444f78447824223cefc48062";
//A string array to save all the names
var nameArray = [String]()
//the label we create
#IBOutlet weak var labelTest: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//calling the function that will fetch the json
getJsonFromUrl();
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//this function is fetching the json from URL
func getJsonFromUrl(){
//creating a NSURL
let url = NSURL(string: URL_POSTS)
//fetching the data from the url
URLSession.shared.dataTask(with: (url as URL?)!, completionHandler: {(data, response, error) -> Void in
if let jsonObj = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary {
//printing the json in console
print(jsonObj.value(forKey: "posts")!)
//getting the companies tag array from json and converting it to NSArray
if let heroeArray = jsonObj.value(forKey: "posts") as? NSArray {
//looping through all the elements
for heroe in heroeArray{
//converting the element to a dictionary
if let heroeDict = heroe as? NSDictionary {
//getting the name from the dictionary
if let name = heroeDict.value(forKey: "name") {
//adding the name to the array
self.nameArray.append((name as? String)!)
}
}
}
}
OperationQueue.main.addOperation({
//calling another function after fetching the json
//it will show the names to label
self.showNames()
})
}
}).resume()
}
func showNames(){
//looing through all the elements of the array
for name in nameArray{
//appending the names to label
labelTest.text = labelTest.text! + name + "\n";
}
}
}
From the result above, it seems ok that I had found content result in console while fetching json from remote url , but nothing shows up on main interface with iOS simulator.
json result in console
main interface nothing show up in IOS Simulator
I've had some kind of this problem with JSON keys and NSDictionary usage. What worked out for me was just using Decodable protocol. I would recommend using this new API for parsing instead of JSONSerialization.
Here's a few good reads:
https://medium.com/swiftly-swift/swift-4-decodable-beyond-the-basics-990cc48b7375
https://medium.com/xcblog/painless-json-parsing-with-swift-codable-2c0beaeb21c1
I am new to IOS development. And I am working to get page load time of web on IOS emulators.
Since, I am targeting top 100 alexa websites, it would be difficult to load each url and record time for load. So, I am planning to automate it.
Below is my code:
import UIKit
class ViewController: UIViewController, UIWebViewDelegate{
var startTime = Date()
#IBOutlet weak var WebView: UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// let URL = Foundation.URL(string: "http://www.youtube.com")
startTime = Date()
print(startTime)
for url in returnURLArray(){
WebView.delegate = self
WebView.loadRequest(URLRequest(url: URL(string: url)!))
print("I am trying to load...."+url)
sleep(100)
print("back to execute other urls")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func webViewDidFinishLoad(_ WebView: UIWebView)
{
//Check here if still webview is loding the content
if (WebView.isLoading){
print("I am loading now...")
return;
}
//after code when webview finishes
let finishTime = Date()
print(finishTime.timeIntervalSince(startTime))
NSLog("Webview loding finished")
}
func returnURLArray() -> [String] {
let file = "topalexawebsites.txt" //this is the file. we will write to and read from it
var text2 = String()
var urlArr = [String]()
if let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
let path = dir.appendingPathComponent(file)
do{
print(path)
text2 = try String(contentsOf: path, encoding: String.Encoding.utf8).trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
urlArr = text2.components(separatedBy: "\n")
// print(text2)
// for urls in urlArr{
// print(urls)
//}
}
catch let err as NSError{
print(err)
}
}
return urlArr
}
}
I want to execute all urls one by one in my topalexawebsites.txt and record load time for it.
But, here I am seeing only one cycle is working, and since urls are in a loop, only the also one gets loaded.
Trying to create a simple example code block in Swift 2.0 on iOS 9.1 using Xcode 7.1. Tried this article in techotopia; which I suspect is based on swift 1.2.
Made a few tiny changes so that it would compile & run, but although it appears to work, it doesn't seem to save my string into the file. Is there capability or something subtle I have missed here.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var textBox: UITextField!
var fileMgr: NSFileManager = NSFileManager.defaultManager()
var docsDir: String?
var dataFile: String?
var string: String = ""
override func viewDidLoad() {
super.viewDidLoad()
let dirPaths = NSSearchPathForDirectoriesInDomains(
.DocumentDirectory, .UserDomainMask, true)
docsDir = dirPaths[0] as String
let dataFile = NSURL(fileURLWithPath: docsDir!).URLByAppendingPathComponent("datafile.dat")
string = "\(dataFile)"
print(string)
if fileMgr.fileExistsAtPath(string) {
let databuffer = fileMgr.contentsAtPath(string)
let datastring = NSString(data: databuffer!,
encoding: NSUTF8StringEncoding)
textBox.text = datastring as? String
}
}
#IBAction func saveText(sender: AnyObject) {
let databuffer = (textBox.text)
let data = databuffer?.dataUsingEncoding(NSUTF8StringEncoding)
fileMgr.createFileAtPath(string, contents: data,
attributes: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
More testing; noticed I get this error when I try and create the file... error in __connection_block_invoke_2: Connection interrupted; which explains why it isn't working, if only I could workout what it is talking about?
Continued to try to debug; added UIFileSharingEnabled but cannot see Documents directory; added more code to test its presence, and create it if missing; fails telling me it is already there... even if it is evidently invisible...
When you do this, string ends up being a string representation of the file URL, e.g. file://.... That file:// prefix is a URL "scheme" (like http:// or ftp://). But including the scheme at the start of the string means that this is not a valid path. You have to remove the scheme.
The easiest way to do this is to use the path method to get the path from a NSURL without that scheme. I'd also use URLForDirectory to get the URL for the documents folder nowadays.
class ViewController: UIViewController {
#IBOutlet weak var textBox: UITextField!
lazy var fileMgr = NSFileManager.defaultManager()
var path: String!
override func viewDidLoad() {
super.viewDidLoad()
let documents = try! fileMgr.URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: false)
path = documents.URLByAppendingPathComponent("datafile.dat").path
if fileMgr.fileExistsAtPath(path) {
if let data = fileMgr.contentsAtPath(path) {
textBox.text = String(data: data, encoding: NSUTF8StringEncoding)
}
}
}
#IBAction func saveText(sender: AnyObject) {
let data = textBox.text?.dataUsingEncoding(NSUTF8StringEncoding)
fileMgr.createFileAtPath(path, contents: data, attributes: nil)
}
}
Or I might stay entirely in the world of URLs, retiring paths altogether, also using methods that throw meaningful error messages:
class ViewController: UIViewController {
#IBOutlet weak var textBox: UITextField!
lazy var fileMgr = NSFileManager.defaultManager()
var fileURL: NSURL!
override func viewDidLoad() {
super.viewDidLoad()
do {
let documents = try fileMgr.URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: false)
fileURL = documents.URLByAppendingPathComponent("datafile.dat")
var reachableError: NSError?
if fileURL.checkResourceIsReachableAndReturnError(&reachableError) {
textBox.text = try String(contentsOfURL: fileURL)
}
} catch {
print(error)
}
}
#IBAction func saveText(sender: AnyObject) {
do {
try textBox.text?.writeToURL(fileURL, atomically: true, encoding: NSUTF8StringEncoding)
} catch {
print(error)
}
}
}
Thanks for the help in my last question. This time I would like to ask for help again for an application whose contents need to be downloaded and cached when it's opened for the first time.
Indeed it's a web app where the view controller consists of a WebView. In order to cache the whole website (which consists of "index.htm", "first.htm, "second.htm" and etc), I have scraped the whole site using the Kanna library and hence generated numerous links (generatedURL). Then I write the HTML of each link into a single file using the approach answered here. Read and write data from text file
Here is my code in the didFinishLaunchingWithOptions of AppDelegate.swift.
// get the documents folder url
let documentDirectoryURL = try! NSFileManager.defaultManager().URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: true)
for index in 0..<generatedURL.count {
let fileDestinationUrl = documentDirectoryURL.URLByAppendingPathComponent(String(index)+".htm")
cachedURL[index] = fileDestinationUrl //store the cached urls
let fileURL = NSURL(string: generatedURL[index])
//if (NSFileManager.defaultManager().fileExistsAtPath(fileDestinationUrl)) {
let data = NSData(contentsOfURL: fileURL!)
if (data != nil) {
//writing to disk
data?.writeToURL(fileDestinationUrl, atomically: true)
// saving was successful. any code posterior code goes here
//reading from disk
do {
let mytext = try String(contentsOfURL: fileDestinationUrl, encoding: NSUTF8StringEncoding)
print(fileDestinationUrl)
print(mytext) // "some text\n"
} catch let error as NSError {
print("error loading from url \(fileDestinationUrl)")
print(error.localizedDescription)
}
}
// } else {
// print("The files already exist")
// //reading from disk
// do {
// let mytext = try String(contentsOfURL: fileDestinationUrl, encoding: NSUTF8StringEncoding)
// //print(fileDestinationUrl)
// //print(mytext) // "some text\n"
// } catch let error as NSError {
// print("error loading from url \(fileDestinationUrl)")
// print(error.localizedDescription)
// }
//
// }
}
When running the program, the HTMLs of all the links are stored locally in those files. There's no problems in loading the HTML and thereby displaying the cached page in the WebView.
file:///var/mobile/Containers/Data/Application/.../Documents/0.htm
file:///var/mobile/Containers/Data/Application/.../Documents/1.htm
file:///var/mobile/Containers/Data/Application/.../Documents/2.htm
.
.
.
However, the current problem is that I lost the linkage between the cached pages. For example, in the website, there is a button on "index.htm" that links to "first.htm".
Now after loading the cached "index.htm" which is now "file:///var/....../0.htm", I won't be able to go to the cached "first.htm" because "file:///var/....../1.htm" is not in the HTML of the button.
So how do I retrieve the cached files in their original urls? Should I change the approach of generating the file or just create a new version of the website with all the cached file paths?
Thanks for reading my question.
OK, i think I can answer my own question now. Using the following function in the ViewController.swift containing the webView object, I can prompt the webView to load the cached url if the original url is clicked.
func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
if navigationType == UIWebViewNavigationType.LinkClicked {
if (request.URL!.absoluteString == generatedURL[index] {
let requestObj = NSMutableURLRequest(URL: appDelegate.cachedURL[index]!);
webView.loadRequest(requestObj)
//return false
}
}
return true
}