ChallengerGuy
ChallengerGuy

Reputation: 2395

Create CSV file in Swift and write to file

I have an app I've made that has a UITableView with todoItems as an array for it. It works flawlessly and I have an export button that creates a CSV file from the UITableView data and emails it out:

// Variables
var toDoItems:[String] = []
var convertMutable: NSMutableString!
var incomingString: String = ""
var datastring: NSString!

// Mail alert if user does not have email setup on device
func showSendMailErrorAlert() {
    let sendMailErrorAlert = UIAlertView(title: "Could Not Send Email", message: "Your device could not send e-mail.  Please check e-mail configuration and try again.", delegate: self, cancelButtonTitle: "OK")
    sendMailErrorAlert.show()
}
// MARK: MFMailComposeViewControllerDelegate Method
func mailComposeController(controller: MFMailComposeViewController, didFinishWithResult result: MFMailComposeResult, error: NSError?) {
    controller.dismissViewControllerAnimated(true, completion: nil)
}

// CSV Export Button
@IBAction func csvExport(sender: AnyObject) {
    // Convert tableView String Data to NSMutableString
    convertMutable = NSMutableString();
    for item in toDoItems
    {
        convertMutable.appendFormat("%@\r", item)
    }

    print("NSMutableString: \(convertMutable)")

    // Convert above NSMutableString to NSData
    let data = convertMutable.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
    if let d = data { // Unwrap since data is optional and print
        print("NSData: \(d)")
    }

    //Email Functions
    func configuredMailComposeViewController() -> MFMailComposeViewController {
        let mailComposerVC = MFMailComposeViewController()
        mailComposerVC.mailComposeDelegate = self
        mailComposerVC.setSubject("CSV File Export")
        mailComposerVC.setMessageBody("", isHTML: false)
        mailComposerVC.addAttachmentData(data!, mimeType: "text/csv", fileName: "TodoList.csv")

        return mailComposerVC
    }

    // Compose Email
    let mailComposeViewController = configuredMailComposeViewController()
    if MFMailComposeViewController.canSendMail() {
        self.presentViewController(mailComposeViewController, animated: true, completion: nil)
    } else {
        self.showSendMailErrorAlert() // One of the MAIL functions
    }
}

My question is how do I create the same CSV file, but instead of emailing, save it to file? I'm new to programming and still learning Swift 2. I understand that the section of code (data!, mimeType: "text/csv", fileName: "TodoList.csv") creates the file as an attachment. I've looked online for this and trying to understand paths and directories is hard for me. My ultimate goal is to have another UITableView with a list of these 'saved' CSV files listed. Can someone please help? Thank you!

I added the following IBAction to my project:

// Save Item to Memory
@IBAction func saveButton(sender: UIBarButtonItem) {
    // Convert tableView String Data to NSMutableString
    convertMutable = NSMutableString();
    for item in toDoItems
    {
        convertMutable.appendFormat("%@\r", item)
    }

    print("NSMutableString: \(convertMutable)")

    // Convert above NSMutableString to NSData
    let data = convertMutable.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
    if let d = data { // Unwrap since data is optional and print
        print("NSData: \(d)")
    }

    let path = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as NSString

    func writeToFile(_: convertMutable, path: String, atomically useAuxiliaryFile: Bool, encoding enc: UInt) throws {

    }

}

Upvotes: 2

Views: 9343

Answers (2)

Johnson
Johnson

Reputation: 171

I was struggling to find a decent simple answer to this for ages.

Here is the best way that I've found to create a csv file and even the directory you want it to be it and write to it.

//First make sure you know your file path, you can get it from user input or whatever
//Keep the path clean of the name for now
var filePath = "/Users/Johnson/Documents/NEW FOLDER/"
//then you need to pick your file name
let fileName = "AwesomeFile.csv"
//You should probably have some data to put in it
//You can even convert your array to a string by appending all of it's elements
let fileData = "This,is,just,some,dummy,data"

// Create a FileManager instance this will help you make a new folder if you don't have it already
let fileManager = FileManager.default 

//Create your target directory
do {
   try fileManager.createDirectory(atPath: filePath!, withIntermediateDirectories: true, attributes: nil)
   //Now it's finally time to complete the path
   filePath! += fileName!
        }
        catch let error as NSError {
            print("Ooops! Something went wrong: \(error)")
        }

//Then simply write to the file
do {
   // Write contents to file
   try fileData.write(toFile: filePath!, atomically: true, encoding: String.Encoding.utf8)
   print("Writing CSV to: \(filePath!)")
}
        catch let error as NSError {
            print("Ooops! Something went wrong: \(error)")
        }

PS. Just noticed that question is from year ago, but I hope it helps a struggling newbie like myself when they inevitably stumble upon it like I did.

Upvotes: 3

theMikeSwan
theMikeSwan

Reputation: 4729

convertMutable can be easily written to disk with either fun writeToFile(_ path: String, atomically useAuxiliaryFile: Bool, encoding enc: UInt) throws or func writeToURL(_ url: NSURL, atomically useAuxiliaryFile: Bool, encoding enc: UInt) throws. All you have to do is create a path or URL to write the string out to. If you are using iCloud things will be more challenging but for locally stored files you can use let path = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as NSString to get the root path of the documents directory.

Update: Based on you first comment below here is some added info: The first issue is that it appears as though you are looking for code you can just paste int your project without really understanding what it does. My appologies if I'm wrong, but if I'm right this is not a good route to take as you will have many issues down the road when things change.

At the bottom of your last code section you are trying to create a function inside a function which is not going to do what you want. The above mentioned functions are the declarations of two NSString functions not functions that you need to create. As NSMutableString is a subclass of NSString you can use those functions on your convertMutable variable.

Another thing you need to deal with is creating a name for the file you want to save, currently you have pasted in the line above that gets the Documents directory but does not have a file name. You will need to devise a way to create a unique filename each time you save a CSV file and add that name to the end of path. Then you can use writeToFile… or writeToURL… to write the string to the desired location.

If you find you don't fully comprehend the code you are adding then consider getting a book or finding classes about Swift (Coursera.org has a class that may be of use). There are plenty of resources out there learn the basics of software development and Swift, it will take effort and time but it will be worth it in the end if this is something you want to pursue.

Upvotes: 1

Related Questions