I'm creating a CSV file from CoreData using CHCSVWriter, so far so good and the file is creating perfectly. My problem is when i'm trying to send the CSV file that i've created i'm running in some issues, I open the CSV file with Excel and instead of Hebrew text I see gibberish.
While using NSLog to print the result of the CSV string, I see the Hebrew just fine. Even after converting it from NSString to NSData and back again.
This is what I get:
יוסי צפר
This is my code:
- (void)createCSV
{
CHCSVWriter *writer = [[CHCSVWriter alloc] initForWritingToCSVFile:[self csvFilePath]];
for (EWDBUsers *user in self.users)
{
[writer writeField:user.name];
[writer writeField:user.company];
[writer writeField:user.email];
[writer writeField:user.telephone];
[writer finishLine];
}
[writer closeStream];
}
- (NSString*)csvFilePath
{
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *filename = #"users.csv";
return [documentsDirectory stringByAppendingPathComponent:filename];
}
- (void)showMailComposerController
{
if ([MFMailComposeViewController canSendMail]) {
MFMailComposeViewController* controller = [[MFMailComposeViewController alloc] init];
controller.mailComposeDelegate = self;
[controller setSubject:#"Users List"];
[controller setMessageBody:#"Attachment." isHTML:NO];
NSError *error;
NSString *csvFileString = [NSString stringWithContentsOfFile:[self csvFilePath] encoding:NSUTF8StringEncoding error:&error];
NSLog(#"%#", csvFileString);
NSData *csv = [csvFileString dataUsingEncoding:[NSString defaultCStringEncoding]];
[controller addAttachmentData:csv mimeType:#"text/cvs" fileName:#"users.csv"];
if (controller) {
[self presentModalViewController:controller animated:YES];
}
} else {
return;
}
}
Can someone please tell me what am I doing wrong?
Thanks in advance.
You're seeing gibberish because you're converting Unicode to ASCII. [csvFileString dataUsingEncoding:[NSString defaultCStringEncoding]] should be [csvFileString dataUsingEncoding:NSUTF8StringEncoding].
Also, mimeType:#"text/cvs" should be mimeType:#"text/csv".
EDIT: The issue seems to be Excel. Opening the received CSV file in TextEdit displays just fine.
My solution is adding BOM chars at start to solve a bug in Excel when try to open CSV files and save the UTF-8 Encoding.
cvsStringFile = [[NSString alloc] initWithFormat:#"\357\273\277%#", cvsStringFile];
NSData *myXLSData = [cvsStringFile dataUsingEncoding:NSUTF8StringEncoding];
[picker addAttachmentData:myXLSData mimeType:#"text/csv;charset=utf-8" fileName:#"Report.csv"];
I suspect, there is an encoding issue. You are sending generated file with an invalid MIME type: text/cvs.
I would use text/plain with an explicit charset parameter, e.g.
text/plain; charset=utf-8
and explicitly encode the csv string into an NSData using that encoding (e.g. NSUTF8StringEncoding).
Edit:
Actually, there is a MIME type text/csv
So, you may also use: text/csv; charset=utf-8
Related
I have an iOS app that gets data from a form and then sends it as a csv via email. currently it saves the csv as a static file name but i want it to start saving it as <date><other app defined variable>.csv. If its easier to redefine a new variable for the date then so be it, have tried it but couldn't make that work either.
Currently it looks like this:
NSString* fileName = #"file.csv";
I have tried this, among other minor syntax changes with no luck:
NSString* fileName = #"%#%# .csv",FromEmail,dateString;
FromEmail is defined in HomeView.m like this:
NSString *FromEmail = [userDefault objectForKey:#"FromEmail"];
This is the whole csv making part:
//CSV function
NSMutableString *csv = [NSMutableString stringWithString:#""];
//add your content to the csv
[csv appendFormat:#"From,Date,Client,Time,Notes,Hardware,AfterHours,NoCharge\n%#,%#,%#,%#,%#,%#,%#,%#",FromEmail,dateString,txtClient.text,txtTime.text,txtNotes.text,txtHardware.text,isAfterHours,isDiscount];
NSString* filePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
//NSString* fileName = #"file.csv";
NSString* fileName = #"%#%# .csv",FromEmail,dateString;
NSString* fileAtPath = [filePath stringByAppendingPathComponent:fileName];
if (![[NSFileManager defaultManager] fileExistsAtPath:fileAtPath]) {
[[NSFileManager defaultManager] createFileAtPath:fileAtPath contents:nil attributes:nil];
}
BOOL res = [[csv dataUsingEncoding:NSUTF8StringEncoding] writeToFile:fileAtPath atomically:NO];
if (!res) {
[[[UIAlertView alloc] initWithTitle:#"Error Creating CSV" message:#"Check your permissions to make sure this app can create files so you may email the app data" delegate:nil cancelButtonTitle:#"Okay" otherButtonTitles: nil] show];
}else{
NSLog(#"Data saved! File path = %#", fileName);
[picker addAttachmentData:[NSData dataWithContentsOfFile:fileAtPath]
mimeType:#"text/csv"
fileName:#"file.csv"];
[self presentViewController:picker animated:YES completion:nil];
}
//END CSV Function
I suspect its something really simple but its just had me stumped for ages
Hot Licks was right, I had the format of the date wrong. It was throwing an error because the date section was formatted with forward slashes which was changing the path it was being saved at.
I added a separate date formatter for the CSV file name with this code:
NSDate *csvDate = [NSDate date];
NSDateFormatter *csvFormatter = [[NSDateFormatter alloc] init];
[csvFormatter setDateFormat:#"ddMMyy-Hm"];
NSString *csvDateString = [csvFormatter stringFromDate:csvDate ];
And changed the line making the filename variable to this:
NSString* fileName = [NSString stringWithFormat:#"%#%#.csv",FromEmail,csvDateString];
Thanks for the help
I have some NSData variables which contain .m4a/.mp4 file formats. I've tried looking at ways to convert the NSData into file format, with no luck. I need to convert it into a file, change some of it's metadata, and convert it back to NSData. Here's some code:
// This JSON basically gets the url of a .m4a
NSArray *preview = [JSON valueForKeyPath:#"results.previewUrl"];
_previewData = [NSMutableData dataWithContentsOfURL:[NSURL URLWithString:[preview objectAtIndex:0]]];
[_previewData writeToFile:path atomically:YES];
ANMovie* file = [[ANMovie alloc] initWithFile:_previewData]; // or .mp4
NSData* jpegCover = UIImageJPEGRepresentation(artworkImage, 1.0);
ANMetadata* metadata = [[ANMetadata alloc] init];
metadata.albumCover = [[ANMetadataImage alloc] initWithImageData:jpegCover type:ANMetadataImageTypeJPG];
[file setMovieMetadata:metadata];
[file close];
I'm guessing [[ANMovie alloc] initWithFile:] expects a file, and you've passed it, I'm also assuming here, an NSData*.
Try:
ANMovie* file = [[ANMovie alloc] initWithFile:path];
So I have had this problem for sometime and just cant get it working! I have been building a survey app that users simply enter information in and its saved to a csv file. Im now at the stage where I need to attached the csv file within the app to an email address...
I have the code as follows, it should work but for some reason is attaching a 'blank' csv file instead of the one with the data in. Im guessing it must be something to do with the file path however cant get it working!!
- (IBAction)send:(id)sender {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *savedFilePath = [documentsDirectory stringByAppendingPathComponent:#"results.csv"];
NSData *csvData = [NSData dataWithContentsOfFile:savedFilePath];
MFMailComposeViewController *mailcomposer = [[MFMailComposeViewController alloc] init];
[mailcomposer addAttachmentData:csvData mimeType:#"text/csv" fileName:#"results.csv"];
[mailcomposer setToRecipients:#[#"gpsflighttrial#gcap.eu"]];
[mailcomposer setSubject:self.subject.text];
[mailcomposer setMessageBody:self.message.text isHTML:NO];
}
UPDATE:
So I just tested this on my new i-phone and there is no attachment when the email is delivered? Its there in the mail app and in the simulator, however when the message is received the attachment has gone? Can anyone help??
I've just tried your code and it seems ok. Add this code to see what you actually have in that csv file.
NSString *strData = [[NSString alloc]initWithData:csvData encoding:NSUTF8StringEncoding];
NSLog(#"Output %#",strData);
Try to check csvData contents, it's probably empty.
Part of my iOS app will include exporting a .csv file.
I have the file made and data is added to each row as I would like.
I would like to add a header row so if a file is emailed to a person they will know what each column is for.
How do I implement something like this?
Here is my csv code below:
- (NSString *)dataFilePath
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:#"myfile.csv"];
}
- (IBAction)save:(id)sender
{
if (![[NSFileManager defaultManager] fileExistsAtPath:[self dataFilePath]]) {
[[NSFileManager defaultManager] createFileAtPath: [self dataFilePath] contents:nil attributes:nil];
}
NSString * writeString = [NSString stringWithFormat:#"%#,%#,%#\n", self.nameTextField.text, self.cityTextField.text, self.stateTextField.text];
NSFileHandle *handle;
handle = [NSFileHandle fileHandleForWritingAtPath: [self dataFilePath]];
[handle truncateFileAtOffset:[handle seekToEndOfFile]];
[handle writeData:[writeString dataUsingEncoding:NSUTF8StringEncoding]];
self.nameTextField.text = #"";
self.cityTextField.text = #"";
self.stateTextField.text = #"";
[self.stateTextField resignFirstResponder];
}
Questions 2:
How can I write over a row? Currently if I update any of the UITextFields a new row is added.
A header row in a CSV file is simply fixed data in the first row - it is up to the program that reads the CSV file to treat the first row as a header row - Excel has an option in the file open/import dialog, for example.
In your code you can simply write the header when you first create the file -
- (IBAction)save:(id)sender
{
NSString *headerRow;
if (![[NSFileManager defaultManager] fileExistsAtPath:[self dataFilePath]]) {
[[NSFileManager defaultManager] createFileAtPath: [self dataFilePath] contents:nil attributes:nil];
headerRow=#"name,city,state";
}
NSString * writeString = [NSString stringWithFormat:#"%#,%#,%#\n", self.nameTextField.text, self.cityTextField.text, self.stateTextField.text];
NSFileHandle *handle;
handle = [NSFileHandle fileHandleForWritingAtPath: [self dataFilePath]];
[handle truncateFileAtOffset:[handle seekToEndOfFile]];
if (headerRow != nil) {
[handle writeData:[headerRow dataUsingEncoding:NSUTF8StringEncoding]];
}
[handle writeData:[writeString dataUsingEncoding:NSUTF8StringEncoding]];
self.nameTextField.text = #"";
self.cityTextField.text = #"";
self.stateTextField.text = #"";
[self.stateTextField resignFirstResponder];
}
Right away you need to know that adding something to a CSV file makes it not a CSV file. Exporting with comments will work if you're also the only one importing it - in which case it is your custom data format based on CSV. There is a defined IETF standard for CSV - RFC4180.
To make it work you would need to define some escape character that informs your format parser that a line is a comment. I suggest "", - this will never normally appear in a RFC4180-compliant CSV file.
In practical terms, add your comment lines after each CSV-compliant line. In your save: method you have only one line so it will work to write a comment line and then the RFC-compliant line or in the opposite order, as long as you preserve the integrity of the RFC lines. If you ever want to save a longer string with many lines, you would need to split the NSString you are saving with something like
- (NSArray *)componentsSeparatedByString:(NSString *)separator
where your separator is a "\n", and then loop through the resulting array to write each line, adding your comments as required.
You can use a regular CSV parser still (like CHCSVParser) if you preprocess the file and strip out the lines beginning with your special comment marker. Again you would need to split the incoming file by line break and discard the comments before trying to parse it.
THANK YOU Dave DeLong for CHCSVParser! I use it often.
I have an iOS application which is download from server an XML file encoded in Windows 1252.
I am using the following code to save it to my document folder :
NSString *path = #"http://server/file.xml";
NSURL *URL = [NSURL URLWithString:[path stringByAddingPercentEscapesUsingEncoding:NSWindowsCP1252StringEncoding]];
NSData *xmlData = [NSData dataWithContentsOfURL:URL];
if(xmlData == nil) {
// Error - handle appropriately
NSLog(#"ERROR");
}
NSString *applicationDocumentsDir=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *storePath=[applicationDocumentsDir stringByAppendingPathComponent:#"annonces.xml"];
[xmlData writeToFile:storePath atomically:TRUE];
NSLog(#"write xml");
It doesn't work, I've got an nil response when I try to read it with the parser. How could I do to get it properly. I cannot change the encoding of te xml which is on the server. If I change the XML encoding manually, I've got a correct response.
This is how I pass the XML string to parse it with XML Dictionnary class :
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *finalPath = [path stringByAppendingPathComponent:#"annonces.xml"];
NSString *string = [[NSString alloc] initWithContentsOfFile:finalPath encoding:NSWindowsCP1252StringEncoding error:NULL];
NSLog(#"string: %#", string);
NSDictionary *items = [NSDictionary dictionaryWithXMLString:string];
NSLog(#"dictionary: %#", items);
Your URL variable contains the url address of the location of the XML file you want to download.
However you are applying NSWindowsCP1252StringEncoding to the url, not to the content of the file.
xmlData is nil because dataWithContentsOfURL: cannot find the file at the location you have specified within URL.
You need to download the file first, then once its downloaded then you can be concerned about what encoding its in and how to parse it.
The way you are using NSWindowsCP1252StringEncoding has got nothing to do with the content of the file.