raginggoat
raginggoat

Reputation: 3600

CSV Not Formatted Correctly

I am saving a CSV file and emailing it. It works but the CSV is not formatted correctly. Some of the items that should be moved to a new row are included the the second column of an existing row. There are 5 columns, id, name, model, ups, and price. Is there something I've done wrong when creating the file?

func openActions() {
        actions = UIAlertController(title: "", message: "", preferredStyle: .ActionSheet)
        let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel, handler: {
            (action) -> Void in
            self.actions?.dismissViewControllerAnimated(true, completion: nil)
        })

        let saveCSV = UIAlertAction(title: "Save as CSV", style: .Default, handler: {
            (action) -> Void in
            self.getAndSaveItems()
            self.actions?.dismissViewControllerAnimated(true, completion: nil)
        })

        let viewFile = UIAlertAction(title: "View CSV", style: .Default, handler: {
            (action) -> Void in
            self.viewFile()
            self.actions?.dismissViewControllerAnimated(true, completion: nil)
        })

        let emailFile = UIAlertAction(title: "Email CSV", style: .Default, handler: {
            (action) -> Void in
            self.emailFile()
            self.actions?.dismissViewControllerAnimated(true, completion: nil)
        })

        actions?.addAction(cancelAction)
        actions?.addAction(emailFile)
        actions?.addAction(viewFile)
        actions?.addAction(saveCSV)
        self.presentViewController(actions!, animated: true, completion: nil)
    }

    func dataFilePath() -> String {
        let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
        let documentsDirectory = paths[0]
        let titleString = self.title!.stringByReplacingOccurrencesOfString(" ", withString: "_")
        let string = String(format: "/%@.csv", titleString)
        return documentsDirectory.stringByAppendingString(string)
    }

    func getAndSaveItems() {
        let getLimit = 1000
        var getSkip = 0

        activityIndicator?.startAnimating()

        let query = PFQuery(className: "Items")
        query.whereKey("user", equalTo: PFUser.currentUser()!)
        query.whereKey("collection", equalTo: self.title!)
        query.orderByAscending("item")
        query.limit = getLimit
        query.skip = getSkip
        query.findObjectsInBackgroundWithBlock( {
            (objects, error) -> Void in
            if error == nil {
                if let objects = objects as [PFObject]! {
                    for object in objects {
                        self.names.append(object["item"] as! String)
                        self.upcs.append(object["upc"] as! String)
                        self.ids.append(object.objectId!)
                        self.models.append(object["model"] as! String)
                        self.prices.append(object["pricePaid"] as! String)
                        if object["notes"] == nil {
                            self.notes.append(nil)
                        } else {
                            self.notes.append(object["notes"] as? String)
                        }
                    }
                }

                if objects!.count == getLimit {
                    getSkip += getLimit
                    self.getAndSaveItems()
                }
            } else {
                // Show error...
            }
        })

        writeToFile()
    }

    func writeToFile() {
        if (!NSFileManager.defaultManager().fileExistsAtPath(self.dataFilePath())) {
            NSFileManager.defaultManager().createFileAtPath(self.dataFilePath(), contents: nil, attributes: nil)
        }

        // var writeString = NSMutableString(capacity: 0)
        var writeString = String()
        writeString.reserveCapacity(0)

        for (var i = 0; i < names.count; i++) {
            let stringToWrite = String(format: "%@,%@,%@,%@,%@, \n", ids[i], names[i], models[i], upcs[i], prices[i])
            writeString.appendContentsOf(stringToWrite)
        }

        let handle = NSFileHandle(forWritingAtPath: self.dataFilePath())
        handle?.truncateFileAtOffset((handle?.seekToEndOfFile())!)
        handle?.writeData(writeString.dataUsingEncoding(NSUTF8StringEncoding)!)

        self.activityIndicator?.stopAnimating()
    }

    func viewFile() {
        if (NSFileManager.defaultManager().fileExistsAtPath(dataFilePath())) {
            let fileData = NSData(contentsOfFile: dataFilePath())

            let fileVC = FileViewController(nibName: "FileViewController", bundle: nil)
            let navController = NavController(rootViewController: fileVC)
            fileVC.fileData = fileData
            self.presentViewController(navController, animated: true, completion: nil)
        } else {
            // File doesn't exist...
            print("File doesn't exist")
        }
    }

    func emailFile() {
        if (NSFileManager.defaultManager().fileExistsAtPath(dataFilePath())) {
            if MFMailComposeViewController.canSendMail() {
                let mailComposer = MFMailComposeViewController()
                mailComposer.mailComposeDelegate = self
                let subject = String(format: "My %@ Collection", self.title!)
                mailComposer.setSubject(subject)
                mailComposer.setMessageBody("", isHTML: false)

                let fileData = NSData(contentsOfFile: dataFilePath())
                let titleString = self.title!.stringByReplacingOccurrencesOfString(" ", withString: "_")
                let fileNameString = String(format: "%@.csv", titleString)
                mailComposer.addAttachmentData(fileData!, mimeType: "text/csv", fileName: fileNameString)

                self.presentViewController(mailComposer, animated: true, completion: nil)
            } else {
                // Can't send mail...
            }
        } else {
            // File doesn't exist...
            print("File doesn't exist")
        }
    }

    func mailComposeController(controller: MFMailComposeViewController, didFinishWithResult result: MFMailComposeResult, error: NSError?) {
        self.dismissViewControllerAnimated(true, completion: nil)
    }

enter image description here

Upvotes: 0

Views: 74

Answers (1)

rmaddy
rmaddy

Reputation: 318804

This line:

let stringToWrite = String(format: "%@,%@,%@,%@,%@, \n", ids[i], names[i], models[i], upcs[i], prices[i])

is what you are using to create each line of the CSV file. This will only work in simple cases.

Here are the CSV rules:

  1. If a field value contains the field separator (comma in your case), a quote, whitespace at either end of the value, or any newlines, the field value must be enclosed in quotes (usually double-quotes).
  2. If a field value contains any quote characters, you must escape each quote character with another quote character.

Creating and parsing CSV files is much more involved than most people realize.

Either modify your logic to add any needed quoting or find a 3rd party CSV library that properly handles all of these cases.

Upvotes: 1

Related Questions