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)
}
}
}
Related
I am looking for a way to create separate files. For example: I have an application which has a form such as name, date etc. and when I press save, I want to save all the information in that form of the user into a file. Each time the form is filled in, I want a new file to be created for each user. The file will be saved in the documents directory of the application. The name of the file should come from the 'name' textfield of the form. I have looked at ways to create files in swift but the filename is always hardcoded in the path url and every time the form is filled out the information gets saved on the same file. I don't want to do this. Any solution or advice is appreciated, I can't seem to find any solution to this online (only how to create a file with a hardcoded file name, which I am already able to do).
import UIKit
class FormViewController: UIViewController {
#IBOutlet weak var organdonationno: UITextField!
#IBOutlet weak var donorage: UITextField!
#IBOutlet weak var snodname: UITextField!
#IBOutlet weak var donorshospital: UITextField!
#IBOutlet weak var date: UITextField!
#IBOutlet weak var ID: UITextField!
var fileMgr: FileManager = FileManager.default
var docsDir: String?
var dataFile: String?
override func viewDidLoad() {
super.viewDidLoad()
let filemgr = FileManager.default
let dirPaths = filemgr.urls(for: .documentDirectory, in: .userDomainMask)
dataFile = dirPaths[0].appendingPathComponent("path").path
if filemgr.fileExists(atPath: dataFile!) {
let databuffer1 = filemgr.contents(atPath: dataFile!)
let databuffer2 = filemgr.contents(atPath: dataFile!)
let databuffer3 = filemgr.contents(atPath: dataFile!)
let databuffer4 = filemgr.contents(atPath: dataFile!)
let databuffer5 = filemgr.contents(atPath: dataFile!)
let datastring1 = NSString(data: databuffer1!, encoding: String.Encoding.utf8.rawValue)
let datastring2 = NSString(data: databuffer2!, encoding: String.Encoding.utf8.rawValue)
let datastring3 = NSString(data: databuffer3!, encoding: String.Encoding.utf8.rawValue)
let datastring4 = NSString(data: databuffer4!, encoding: String.Encoding.utf8.rawValue)
let datastring5 = NSString(data: databuffer5!, encoding: String.Encoding.utf8.rawValue)
organdonationno.text = datastring1 as? String
donorage.text = datastring2 as? String
snodname.text = datastring3 as? String
donorshospital.text = datastring4 as? String
date.text = datastring5 as? String
}
// Do any additional setup after loading the view.
}
#IBAction func savdata(_ sender: Any) {
let databuffer1 = (organdonationno.text)!.data(using: String.Encoding.utf8)
let databuffer2 = (donorage.text)!.data(using: String.Encoding.utf8)
let databuffer3 = (snodname.text)!.data(using: String.Encoding.utf8)
let databuffer4 = (donorshospital.text)!.data(using: String.Encoding.utf8)
let databuffer5 = (date.text)!.data(using: String.Encoding.utf8)
fileMgr.createFile(atPath: dataFile!, contents: databuffer1, attributes: nil)
fileMgr.createFile(atPath: dataFile!, contents: databuffer2, attributes: nil)
fileMgr.createFile(atPath: dataFile!, contents: databuffer3, attributes: nil)
fileMgr.createFile(atPath: dataFile!, contents: databuffer4, attributes: nil)
fileMgr.createFile(atPath: dataFile!, contents: databuffer5, attributes: nil)
}
}
It only saves dates because you are creating file for every field by the same name. It overwritten by new file every time. Last file u create is of date, hence only date persists.If you want to save all form data for one user you can do it as below. You need to create dynamic filename for every form so it is overwritten.
#IBAction func savdata(_ sender: Any) {
let fileName="\(organdonationno.text) + \(donorage.text) + \(snodname.text) + \(donorshospital.text) + (date.text) "
let file = "\(fileName).txt" //this is the file. we will write to and read from it
let text = fileName //just a text
if let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
let path = dir.appendingPathComponent(file)
//writing
do {
try text.write(to: path, atomically: false, encoding: String.Encoding.utf8)
}
catch {/* error handling here */}
//reading
do {
let text2 = try String(contentsOf: path, encoding: String.Encoding.utf8)
}
catch {/* error handling here */}
}
------EDIT--------
To remove Optional word to need to force unwrap the value by putting exclamation mark (!) after the object to need to unwrap.
var canBeNil : String? = "shubhra"
print(canBeNil) // output Optional("shubhra")
print(canBeNil!) // output shubhra
if you need title as organdonationno just edit :
let fileName="\(organdonationno.text)"
let text = "\(organdonationno.text) + \(donorage.text) + \(snodname.text) + \(donorshospital.text) + (date.text) "
Just make sure your filename is unique so that is not overwritten.
Hope it helps. Happy Coding!!
My solution seems similar above. You'd better use the info your user input from textfields to organize a unique name of your file. And I strongly recommend you to write a model to stored your property, and do the saving to disk job for your coding clearness' sake. I can give you an example I just wrote on my playground:
struct User {
let organdonationno: NSString
let donorage: NSString
let snodName: NSString
let donorshospital: NSString
let date: NSString
let ID: NSString
func saveToJSONFile() {
guard let documentDirectoryUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
let pathComponent = ID.appending(".json")
let array = NSArray(array: [organdonationno, donorage, snodName, donorshospital, date, ID])
let fileURL = documentDirectoryUrl.appendingPathComponent(pathComponent)
do {
let data = try JSONSerialization.data(withJSONObject: array, options: [])
try data.write(to: fileURL, options: .atomic)
}
catch {
print(error)
}
}
}
The code I'm writing is put the struct's properties into an NSArray that it can be easily serialized as a JSON object. JSON is a type of well-displayed data structure. When it's written to your disk file, you can open the .json file to see inside. It will be like [ string1, string2, string3, string4, string5, string6] thing, which is pretty easy to understand.
and you can use it this way:
let user = User(organdonationno: datastring1, donorage: datastring2, snodName: datastring3, donorshospital: datastring4, date: datastring5, ID: datastring6)
user.saveToJSONFile()
If you're using swift 4, there is a convenient way to turn the User struct in to JSON object. You just need to let the User conform to Codable, and the apple's underlying implementation will do all of the work.
Struct User: Codable {
I have created an app group group.com.example.FoodTracker-qg, In the main view controller of the app I am downloading an image and storing inside the shared container but I am unable to use the same image in image view. I am getting the following error
fatal error: 'try!' expression unexpectedly raised an error: Error Domain=NSCocoaErrorDomain Code=256 "The file “EC700C58-E9C9-480D-8EE2-B88A570A5728image.jpg” couldn’t be opened." UserInfo={NSURL=/private/var/mobile/Containers/Shared/AppGroup/EC700C58-E9C9-480D-8EE2-B88A570A5728image.jpg}: file /Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-800.0.63/src/swift/stdlib/public/core/ErrorType.swift, line 178
Below is my code for writing and reading from shared container
// ViewController.swift
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var mealName: UILabel!
#IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
let urlString = "https://static.pexels.com/photos/3247/nature-forest-industry-rails.jpg"
try? Data(contentsOf: URL(string: urlString)!).write(to: getSharedFileUrl("image.jpg"))
imageView.image = UIImage(data: try! Data(contentsOf: getSharedFileUrl("image.jpg")))
}
func getSharedFileUrl(_ fileName: String) -> URL {
let fileManager = FileManager.default
let url = fileManager.containerURL(forSecurityApplicationGroupIdentifier: "group.com.example.FoodTracker-qg")
return URL(string: url!.path.appending(fileName))!
}
}
Code seems to be correct, your problem might be due to file name itself....
check url in error
/private/var/mobile/Containers/Shared/AppGroup/EC700C58-E9C9-480D-8EE2-B88A570A5728image.jpg
there's no "/"...it should look like 728/image.jpg
In getSharedFileUrl(_ fileName: String) -> URL why deconstruct the newly retrieved url from containerURL(.. just to create a new one?
You could do something like this instead:
return url!.appending(fileName)
This should fix your problem with the missing /.
I'm not sure I agree with those force wraps, or even with possibly blocking the Main Thread with Data(contentsOf: ..) though!
You append the filename. As a result, you are missing the slash / in between the path components, as JJAAXX44 correctly pinpointed.
Use appendingPathComponentinstead:
func getSharedFileUrl(_ fileName: String) -> URL {
let fileManager = FileManager.default
guard let url = fileManager.containerURL(forSecurityApplicationGroupIdentifier: "group.com.example.FoodTracker-qg")
else {
fatalError() // handle error appropriate to your needs instead
}
return url.appendingPathComponent(fileName)
}
Your write action is not correct.So you can not be read you image form your shared container.
let appGroupIdentifier = "group.mark3"
extension Data {
func writeToGroupContainer(filePath: String, completeHandle: #escaping (Bool, String) -> Void) {
let url = Data.appGroupContainerURL(filePath: filePath)
let result = FileManager.default.createFile(atPath: url.path, contents: self, attributes: nil)
completeHandle(result, url.path)
}
static func appGroupContainerURL(filePath: String) -> URL {
let fileManager = FileManager.default
let groupURL = fileManager.containerURL(forSecurityApplicationGroupIdentifier: appGroupIdentifier)
let url = groupURL!.appendingPathComponent(filePath)
try? FileManager.default.createDirectory(atPath: url.deletingLastPathComponent().path, withIntermediateDirectories: true, attributes: nil)
return url
}
}
us this extension to write a data to your disk correct.
override func viewDidLoad() {
super.viewDidLoad()
let filePath = "images/group/mark3/avatar.png"
let data: Data = UIImagePNGRepresentation(UIImage(named: "image.png")!)!
data.writeToGroupContainer(filePath: filePath) { (done, file) in
print(filePath, done)
}
}
I am a beginner to Swift and FMDB, I got the code below from resources in the internet, and tried my best to understand the code. I have put comments below statements stating what I think it is doing. The ones with question marks I do not understand.
class ViewController: UIViewController {
#IBOutlet weak var name: UITextField!
#IBOutlet weak var specialty: UITextField!
//Defines name and specialty as contents of text fields
var dbpath = String()
//defines the database path
func getPath(fileName: String) -> String {
let documentsURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
//finds document and returns an array of paths
let fileURL = documentsURL.URLByAppendingPathComponent(fileName)
print(fileName)
//finds path to fileName with URLByAppendingPathComponent
print("File Path Is : \(fileURL)")
return fileURL.path!
//returns the fileURL in path format?????
}
//Button "Add Shop" definition
override func viewDidLoad() {
super.viewDidLoad()
let dirPaths =
NSSearchPathForDirectoriesInDomains(.DocumentDirectory,
.UserDomainMask, true)
//creates search paths for directories, then ?????
let docsDir = dirPaths[0]
let dbPath: String = getPath("shopdata.db")
//assigns string "shopdata.db" to dbPath
let fileManager = NSFileManager.defaultManager()
//easier access for NSFileManager, returns shared file for the process when called
if !fileManager.fileExistsAtPath(dbPath as String) {
//if there is already a database, do the following
let contactDB = FMDatabase(path: dbPath as String)
//contact database with path identified in function getPath
if contactDB == nil {
print("Error: \(contactDB.lastErrorMessage())")
//If there is no database
}
if contactDB.open() {
let sql_stmt = "CREATE TABLE IF NOT EXISTS CONTACTS (ID INTEGER PRIMARY KEY AUTOINCREMENT, SPECIALTY TEXT, NAME TEXT)"
if !contactDB.executeStatements(sql_stmt)
//executes a create table statement as defined above
{
print("Error: \(contactDB.lastErrorMessage())")
//if cannot execute statement, display error from fmdb
}
contactDB.close()
//close connection
} else {
print("Error: \(contactDB.lastErrorMessage())")
//if contact cannot be made, display error from fmdb
}
}
}
#IBAction func addShop(sender: AnyObject) {
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
This Function will get the file path of the give fileName from DocumentDirectory and return it back.
func getPath(fileName: String) -> String {
let documentsURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
//finds document and returns an array of paths
let fileURL = documentsURL.URLByAppendingPathComponent(fileName)
print(fileName)
//finds path to fileName with URLByAppendingPathComponent
print("File Path Is : \(fileURL)")
return fileURL.path!
//returns the fileURL in path format?????
}
And these line of code is not needed in here at all. This code also get the file path from DocumentDirectory of the application. Which is done in the getPath: function.
let dirPaths =
NSSearchPathForDirectoriesInDomains(.DocumentDirectory,
.UserDomainMask, true)
//creates search paths for directories, then ?????
let docsDir = dirPaths[0]
DocumentDirectory is where the application save the database.
Sorry for bad English. Hope it helps :)
I'm getting data from sensors using bluetooth, I want to append the string of data I get to the end of file.
When I tried the regular approach
if let dir = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.AllDomainsMask, true).first {
let path = NSURL(fileURLWithPath: dir).URLByAppendingPathComponent(self.file)
do {
try text.writeToURL(path, atomically: false, encoding: NSUTF8StringEncoding)
}
catch {/* error handling here */}
My app started to slow down until even labels were not updating anymore.
Tried using dispatch_async to do in background thread but still it was slowing down my app.
What approach should I use? I read sth about stream but failed to find some solutions in swift I could rely on
Probably your bluetooth is reading data faster than you are performing your file operations. You can optimize it by appending the text to the file instead of reading all the content on each write operation. You could also reuse the file handler between writes and keep the file open.
This sample is extracted from this answer:
struct MyStreamer: OutputStreamType {
lazy var fileHandle: NSFileHandle? = {
let fileHandle = NSFileHandle(forWritingAtPath: self.logPath)
return fileHandle
}()
lazy var logPath: String = {
let path : NSString = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.AllDomainsMask, true).first!
let filePath = (path as NSString).stringByAppendingPathComponent("log.txt")
if !NSFileManager.defaultManager().fileExistsAtPath(filePath) {
NSFileManager.defaultManager().createFileAtPath(filePath, contents: nil, attributes: nil)
}
print(filePath)
return filePath
}()
mutating func write(string: String) {
print(fileHandle)
fileHandle?.seekToEndOfFile()
fileHandle?.writeData(string.dataUsingEncoding(NSUTF8StringEncoding)!)
}
}
Then, you can create a single streamer and reuse it in different writes:
var myStream = MyStreamer()
myStream.write("First of all")
myStream.write("Then after")
myStream.write("And, finally")
In this case, you have the bonus that MyStreamer is also a OutputStreamType, so you can use it like this:
var myStream = MyStreamer()
print("First of all", toStream: &myStream )
print("Then after", toStream: &myStream)
print("And, finally", toStream: &myStream)
Finally I'd recommend you to move 'log.txt' string to a instance variable and pass it as a constructor parameter:
var myStream = MyStreamer("log.txt")
More info about file handler in the Apple Docs.
update #redent84's, to work in Swift 5
Code:
struct MyStreamer{
lazy var fileHandle = FileHandle(forWritingAtPath: logPath)
lazy var logPath: String = {
let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .allDomainsMask, true)[0]
let filePath = path + "/log.txt"
if FileManager.default.fileExists(atPath: filePath) == false{
FileManager.default.createFile(atPath: filePath, contents: nil, attributes: nil)
}
print(filePath)
return filePath
}()
mutating func write(_ string: String) {
print(fileHandle?.description ?? "呵呵")
fileHandle?.seekToEndOfFile()
if let data = string.data(using: String.Encoding.utf8){
fileHandle?.write(data)
}
}
}
Usage:
var myStream = MyStreamer()
myStream.write("First of all")
myStream.write("Then after")
myStream.write("And, finally")
try to write file like this..
var paths: [AnyObject] = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
let filePath = paths[0].stringByAppendingString("/filename.mov")
do
{
try NSFileManager.defaultManager().removeItemAtURL(outputURL)
}
catch
{
error as NSError
}
do {
try text.writeToURL(path, atomically: false, encoding: NSUTF8StringEncoding)
}
I mean to say at last is you have to remove first.. If any query you can ask me
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?