I want to attach a .csv file to an email so the user can send it. At this point, I know the file is actually created when I'm working in the simulator (I can find and open it), and it appears to be attached to the email which is created. However, since I can't send email from the sim, that's as far as I get there.
Deployed on my iPad, the indications are that the file is created and attached to the email (correctly titled file icon included in body of email). I can email it, but no file is attached when the email is received.
Here's the code I'm using to create and write to the file:
-(NSString *)dataFilePath
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSLog(#"dataFilePath created");
return [documentsDirectory stringByAppendingPathComponent:#"WMDGstats.csv"];
NSLog(#"File path: %#", documentsDirectory);
}
-(void) createCSVFile
{
NSLog(#"Top of createCSVFile");
// [[NSFileManager defaultManager] removeItemAtPath:pngFilePath error:&error];
if (![[NSFileManager defaultManager] fileExistsAtPath:[self dataFilePath]])
{
[[NSFileManager defaultManager] createFileAtPath: [self dataFilePath] contents:nil attributes:nil];
NSLog(#"File created");
}
...yada, yada data creation
NSFileHandle *handle;
handle = [NSFileHandle fileHandleForWritingAtPath: [self dataFilePath] ];
[handle truncateFileAtOffset:0];
[handle writeData:[fullString dataUsingEncoding:NSUTF8StringEncoding]];
[self mailCSV];
}
And here's the way I attach it to the email:
-(void) mailCSV
{
if ([MFMailComposeViewController canSendMail])
{
MFMailComposeViewController *mail = [[MFMailComposeViewController alloc] init];
mail.mailComposeDelegate = self;
[mail setSubject:#"Subject"];
[mail setMessageBody:#"message body" isHTML:NO];
[mail setToRecipients:#[#"testingEmail#example.com"]];
NSData *WMDGData = [NSData dataWithContentsOfFile:#"WMDGstats.csv"];
[mail addAttachmentData:WMDGData mimeType:#"text.csv" fileName:#"WMDGstats.csv"];
[self presentViewController:mail animated:YES completion:NULL];
}
else
{
NSLog(#"This device cannot send email");
}
}
Any ideas?
Thanks for looking!
Edit:
Per rmaddy's kind guidance in his comment below, I supplied the full file path for the file:
NSData *WMDGData = [NSData dataWithContentsOfFile:[documentsDirectory stringByAppendingPathComponent:#"WMDGstats.csv"]];
And it works perfectly--file created, attached, mailed and received!
Thanks rmaddy!!!
The following line:
NSData *WMDGData = [NSData dataWithContentsOfFile:#"WMDGstats.csv"];
is probably returning nil because you need to specify the proper path to the file. Since you are saving the file in the Documents folder, you need to specify that full path.
Related
I am trying to attach a video to an email. It is not a local video so I write it to a file. Then I get the data from that file and attach to the email. When the email comes up, the only thing there is the file name "video.mp4" with no video.
MFMailComposeViewController *controller = [[MFMailComposeViewController alloc] init];
controller.mailComposeDelegate = self;
NSData *videoData = [NSData dataWithContentsOfURL:url];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *tempPath = [documentsDirectory stringByAppendingFormat:#"/video.mp4"];
NSLog(#"writing");
BOOL success = [videoData writeToFile:tempPath atomically:NO];
if (success){
NSError *error = nil;
NSData *data = [NSData dataWithContentsOfFile:tempPath];
NSLog(#"path: %#", tempPath);
if (error) {
NSLog(#"%#", [error localizedDescription]);
}else{
[controller addAttachmentData:data mimeType:#"video/mp4" fileName:#"video.mp4"];
NSLog(#"Data has loaded successfully.");
}
}
I think only approach to try is formatting mail as HTML. Otherwise you will always sending an attachment.
NSString *messageBody = #" /*Some HTML format containing HTML5 video player*/";
[controller setMessageBody:messageBody isHTML:YES];
So you need to look for suitable CSS and HTML tags for your video. But don't forget to test it with most e-mail clients as you may see different looks.
I am getting bytes of a file from a webservice, my goal is to get these bytes, put them into NSData and then write into a file, once the writeToFile is 100% complete, I would then display the file (its a PDF, can be up to 6 pages) in QLPreviewController.
I am able to get the bytes from the webservice and put them into NSData like so:
NSArray *byteArray = [dataSource.areaData GetPDFFileData:[NSString stringWithFormat:#"%#",encodedUrlStr]];
NSData *data;
for (NSDictionary *dict in byteArray) {
NSString *base64 = dict[#"data"];
data = [[NSData alloc] initWithBase64EncodedString:base64 options:0];
}
Now I am trying to write the file like so:
if (data)
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
filePath = [NSString stringWithFormat:#"%#/%#", documentsDirectory,PDFFile];
[data writeToFile:filePath atomically:YES];
}
Now what I am trying to do is have some sort of completion handler so nothing else happens until the file is completely written. What would be the best way to do this?
I am also starting to look into NSFileHandle, would that be a better solution?
use NSFilePresenter to monitor for changes to the file
and use a NSFileCoordinator to write to it
in mock code:
NSFilePresenter *p = [NSFilePresenter presenterWithFile:f];
[p signupForChangeNotificationsOrSetDelegateOrWhatever....];
//write
NSFileCoordinator *c = [NSFileCoordinator coordinatorWithPresenter:p];
[c coordinate writeData....]
... called back by p cause we changed
No need to worry about completion if this runs in main queue it's serial , but it's better to save in background queue and read content to verify and present in main Queue
DispatchQueue.global(qos: .background).async {
print("This is run on the background queue")
if (![[NSFileManager defaultManager] fileExistsAtPath:filePath])
{
NSLog(#"not exists");
NSData *fileContents = [#"" dataUsingEncoding:NSUTF8StringEncoding];
[[NSFileManager defaultManager] createFileAtPath:filePath
contents:fileContents
attributes:nil];
}
else
{
NSLog(#"exists");
}
NSFileHandle *myHandle = [NSFileHandle fileHandleForWritingAtPath:filePath];
[myHandle seekToEndOfFile];
NSString*strTowrite=[NSString stringWithFormat:#"%# || %# --- %# \r\n",dt,acion,name];
[myHandle writeabilityHandler];
[myHandle writeData:[strTowrite dataUsingEncoding:NSUTF8StringEncoding]];
DispatchQueue.main.async {
NSString*content=[[NSString alloc] initWithContentsOfFile:filePath
encoding:NSUTF8StringEncoding
error:&error];
if(content)
{
NSLog(#"content is : %# Err : %#",content,error);
// [self sendfile];
}
else
{
NSLog(#"Failed to store the file. Error = %#", error);
}
}
}
I have implemented the module for sending the email from my iPhone device to server. When it comes to the implementation, there is no email file attach instead. I sweared I have added the attACHMENT . THE below is my working
-(IBAction)sendMail:(id)sender{
NSArray *toRecipents = [NSArray arrayWithObjects:#"birth.lo#sdsdfsfsdf-hk.com",#"jason.li#asdasdad-hk.com",#"lo.sdad#gmail.com",nil];
mailComposer = [[MFMailComposeViewController alloc]init];
if ([MFMailComposeViewController canSendMail]) {
mailComposer.mailComposeDelegate = self;
[mailComposer setSubject:#"NOXAV testing"];
[mailComposer setToRecipients:toRecipents] ;
[mailComposer setMessageBody:#"Testing message for the test mail" isHTML:NO];
NSArray *paths = NSSearchPathForDirectoriesInDomains
(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
//make a file name to write the data to using the documents directory:
NSString *fileName = [NSString stringWithFormat:#"%#/textfile.txt",
documentsDirectory];
NSData *fileData = [NSData dataWithContentsOfFile:fileName];
NSString *mimeType = #"text/html";
[mailComposer addAttachmentData:fileData mimeType:mimeType fileName:fileName];
[self presentModalViewController:mailComposer animated:YES ];
}
}
-(void)mailComposeController:(MFMailComposeViewController *)controller
didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error{
if (result) {
NSLog(#"Result : %d",result);
}
if (error) {
NSLog(#"Error : %#",error);
}
[self dismissModalViewControllerAnimated:YES];
}
The MIME type for text file should be text/plain,So change
NSString *mimeType = #"text/html";
to
NSString *mimeType = #"text/plain";
Edit
Also change the fileName here
[mailComposer addAttachmentData:fileData mimeType:mimeType fileName:#"textfile.txt"];
Looks like file name problem, change following statement along with above suggested by Yogesh -
//make a file name to write the data to using the documents directory:
NSString * fileName = [documentsDirectory stringByAppendingPathComponent:#"textfile.txt"];
NSData *fileData = [NSData dataWithContentsOfFile:fileName];
NSString *mimeType = #"text/plain";
Your fileData may not be getting the data from file. Check if [filedata length] = some value.
I know there are tons of threads already on this issue, but I cannot seem to find one that solves my problem. I have a plist with a dictionary as the root, containing three arrays. My code to write to the plist works fine in the simulator but is (null) on the device.
I'm not trying to write to the app bundle,
My file path is correct and I am checking at launch to make sure the file exists in the Documents folder (and it does exist).
- (void) writeToPlist:(NSString *)fileName playerColor:(NSString *)player withData:(NSArray *)data
{
NSArray *sysPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory ,NSUserDomainMask, YES);
NSString *documentsDirectory = [sysPaths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:fileName];
NSLog(#"File Path: %#", filePath);
NSDictionary *plistDict = [[NSDictionary alloc] initWithContentsOfFile:filePath];
NSLog(#"plist: %#", [plistDict description]);
[plistDict setValue:data forKey:player];
BOOL didWriteToFile = [plistDict writeToFile:filePath atomically:YES];
if (didWriteToFile) {
NSLog(#"Write to file a SUCCESS!");
} else {
NSLog(#"Write to file a FAILURE!");
}
}
Debug output:
File Path: /var/mobile/Applications/CA9D8884-2E92-48A5-AA73-5252873D2571/Documents/CurrentScores.plist
plist: (null)
Write to file a FAILURE!
I've used this same method in other projects, so I don't know if I'm forgetting something or what the deal is. I've checked spelling/capitalization and remade the plist, neither made any difference.
So why is plistDict (null) on the device, but not on the simulator? I apologize if I missed the solution in another post on plists.
Your code is written to assume the file already exists in the Documents folder. This won't be the case the first time this method is ever called.
You should add a check for the existence of the file. If it's there, load it. If not, do some other proper initialization of your data in preparation for writing.
Also, your dictionary needs to be mutable for you to change or add keys/values.
- (void) writeToPlist:(NSString *)fileName playerColor:(NSString *)player withData:(NSArray *)data
{
NSArray *sysPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory ,NSUserDomainMask, YES);
NSString *documentsDirectory = [sysPaths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:fileName];
NSLog(#"File Path: %#", filePath);
NSMutableDictionary *plistDict; // needs to be mutable
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
plistDict = [[NSMutableDictionary alloc] initWithContentsOfFile:filePath];
} else {
// Doesn't exist, start with an empty dictionary
plistDict = [[NSMutableDictionary alloc] init];
}
NSLog(#"plist: %#", [plistDict description]);
[plistDict setValue:data forKey:player];
BOOL didWriteToFile = [plistDict writeToFile:filePath atomically:YES];
if (didWriteToFile) {
NSLog(#"Write to file a SUCCESS!");
} else {
NSLog(#"Write to file a FAILURE!");
}
}
I would like to create a file using a NSString (already made) with a .csv extension then email it using the UIMessage framework. So can someone show me the code to create a file (with a .csv extensions and with the contents of a NSString) then how to attach it to a MFMailComposeViewController.
This is how you attach a CSV file to a MFMailComposeViewController:
MFMailComposeViewController *mailer = [[MFMailComposeViewController alloc] init];
mailer.mailComposeDelegate = self;
[mailer setSubject:#"CSV File"];
[mailer addAttachmentData:[NSData dataWithContentsOfFile:#"PathToFile.csv"]
mimeType:#"text/csv"
fileName:#"FileName.csv"];
[self presentModalViewController:mailer animated:YES];
// Note: PathToFile.csv is the actual path of the file on your iOS device's
// file system. FileName.csv is what it should be displayed as in the email.
As far as how to generate the CSV file itself, the CHCSVWriter class at https://github.com/davedelong/CHCSVParser will help you.
Here are the parts where you create a new csv, save it to file and attach it all in one. You know, if you're in to that sort of thing
NSString *emailTitle = #"My Email Title";
NSString *messageBody = #"Email Body";
MFMailComposeViewController *mc = [[MFMailComposeViewController alloc] init];
mc.mailComposeDelegate = self;
[mc setSubject:emailTitle];
[mc setMessageBody:messageBody isHTML:NO];
[mc setToRecipients:#[]];
NSMutableString *csv = [NSMutableString stringWithString:#""];
//add your content to the csv
[csv appendFormat:#"MY DATA YADA YADA"];
NSString* filePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString* fileName = #"MyCSVFileName.csv";
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);
[mc addAttachmentData:[NSData dataWithContentsOfFile:fileAtPath]
mimeType:#"text/csv"
fileName:#"MyCSVFileName.csv"];
[self presentViewController:mc animated:YES completion:nil];
}