I am trying to dismiss my view but for some reason when calling [self dismissModalViewControllerAnimated:NO]; nothing happens.
Options *option = [Options getInstance];
if(option.authToken != nil)
{
}
else
{
loginViewController = [[LoginViewController alloc] init];
[loginViewController setModalTransitionStyle:UIModalTransitionStyleCoverVertical];
[self presentModalViewController:loginViewController animated:NO];
}
Then in my login view controller after logging in
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[connection release];
NSString *theXML = [NSString stringWithFormat:#"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>%#",[[NSString alloc] initWithBytes: [responseData mutableBytes] length:[responseData length] encoding:NSUTF8StringEncoding]];
[self handleXmlResponse:theXML];
TBXML *tbXml = [[TBXML tbxmlWithXMLString:theXML] retain];
if(tbXml)
{
TBXMLElement *isError = [TBXML childElementNamed:#"IsError" parentElement:tbXml.rootXMLElement];
if([[TBXML textForElement:isError] isEqualToString:#"true"])
{
TBXMLElement *error = [TBXML childElementNamed:#"Error" parentElement:tbXml.rootXMLElement];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Login Error"
message:[TBXML textForElement:error]
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
[alert release];
}
else
{
TBXMLElement *value = [TBXML childElementNamed:#"Value" parentElement:tbXml.rootXMLElement];
Options* option = [Options getInstance];
option.authToken = [TBXML textForElement:value];
NSLog(#"YES");
[self dismissModalViewControllerAnimated:NO];
}
}
}
I have searched every where and can't figure out why this is happening
dismissModalViewControllerAnimated not working
Try Use dismissViewControllerAnimated:completion: instead.
Related
In my app, there is code that, if a button is pressed the user can upgrade the app and remove the advertisements, it calls the MKStoreKit and once the user purchases the upgrade, the adverts do remain in the app UNTIL the app is restarted.
I want to fix this, so the advertisements are removed immediately, but how would I go about doing this?
Here is some of the code;
-(IBAction)removeAds:(id)sender
{
if (![MKStoreManager isFeaturePurchased:#"com.davidsapps.puzzler.removeads"]) { //
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Upgrade App" message:#"To upgrade and remove all advertisements from this app Please click OK to purchase the upgrade!" delegate:self cancelButtonTitle:#"No Thanks" otherButtonTitles:#"Yes Please",nil];
[alert show];
[alert release];
}
}
The alert it links to;
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 0){
//cancel button clicked. Do something here or nothing here
}
else{
//other button indexes clicked
[[MKStoreManager sharedManager] buyFeature:#- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 0){
//cancel button clicked. Do something here or nothing here
}
else{
//other button indexes clicked
[[MKStoreManager sharedManager] buyFeature:#"com.idevver.mybatterypal.upgrade"];
}
}
}
Using MKStoreKit
Is there anything I can do to make it remove the advertisements without the user having to restart the app?
Thanks,
Chris
EDIT - the advertisements are Admob Mediation, using this code in the ViewDidLoad
- (void)viewDidLoad {
bannerView_ = [[GADBannerView alloc] initWithAdSize:kGADAdSizeSmartBannerPortrait];
bannerView_.adUnitID = MY_BANNER_UNIT_ID;
CGRect screenRect = [[UIScreen mainScreen] bounds];
CGFloat screenWidth = screenRect.size.width;
CGFloat screenHeight = screenRect.size.height;
CGFloat screenXPos = (screenWidth /2);
CGFloat screenYPos = screenHeight - 90;
[bannerView_ setCenter:CGPointMake(screenXPos, screenYPos)];
bannerView_.rootViewController = self;
bannerView_.adUnitID = MY_BANNER_UNIT_ID;
bannerView_.rootViewController = self;
[self.view addSubview:bannerView_];
This is the code that actually calls the advertisement banner, it is in an if/else statement later in the viewDidLoad
if (![MKStoreManager isFeaturePurchased:#"com.davidsapps.puzzler.removeads"]) { //
[bannerView_ loadRequest:[GADRequest request]];
}
So I guess somehow when the feature is purchased, I need to add to the MKStoreManager.m file something to say dismiss the bannerView_ code above?
Would that work?
EDIT 2 - Full .m file for the in-app code (with thanks to MugunthKumar for a wonderful kit)
//
// MKStoreManager.m
// MKStoreKit
//
// Created by Mugunth Kumar on 17-Nov-2010.
// Copyright 2010 Steinlogic. All rights reserved.
// File created using Singleton XCode Template by Mugunth Kumar (http://mugunthkumar.com
// Permission granted to do anything, commercial/non-commercial with this file apart from removing the line/URL above
// Read my blog post at http://mk.sg/1m on how to use this code
// As a side note on using this code, you might consider giving some credit to me by
// 1) linking my website from your app's website
// 2) or crediting me inside the app's credits page
// 3) or a tweet mentioning #mugunthkumar
// 4) A paypal donation to mugunth.kumar#gmail.com
//
// A note on redistribution
// While I'm ok with modifications to this source code,
// if you are re-publishing after editing, please retain the above copyright notices
#import "MKStoreManager.h"
#import "GADBannerView.h"
#import "AppDelegate.h"
#interface MKStoreManager (PrivateMethods)
- (void) requestProductData;
- (BOOL) canCurrentDeviceUseFeature: (NSString*) featureID;
- (BOOL) verifyReceipt:(NSData*) receiptData;
- (void) enableContentForThisSession: (NSString*) productIdentifier;
#end
#implementation MKStoreManager
#synthesize purchasableObjects = _purchasableObjects;
#synthesize storeObserver = _storeObserver;
static NSString *ownServer = nil;
static __weak id<MKStoreKitDelegate> _delegate;
static MKStoreManager* _sharedStoreManager;
- (void)dealloc {
[_purchasableObjects release];
[_storeObserver release];
[_sharedStoreManager release];
[super dealloc];
}
#pragma mark Delegates
+ (id)delegate {
return _delegate;
}
+ (void)setDelegate:(id)newDelegate {
_delegate = newDelegate;
}
#pragma mark Singleton Methods
+ (MKStoreManager*)sharedManager
{
#synchronized(self) {
if (_sharedStoreManager == nil) {
#if TARGET_IPHONE_SIMULATOR
NSLog(#"You are running in Simulator MKStoreKit runs only on devices");
#else
_sharedStoreManager = [[self alloc] init];
_sharedStoreManager.purchasableObjects = [[NSMutableArray alloc] init];
[_sharedStoreManager requestProductData];
_sharedStoreManager.storeObserver = [[MKStoreObserver alloc] init];
[[SKPaymentQueue defaultQueue] addTransactionObserver:_sharedStoreManager.storeObserver];
#endif
}
}
return _sharedStoreManager;
}
+ (id)allocWithZone:(NSZone *)zone
{
#synchronized(self) {
if (_sharedStoreManager == nil) {
_sharedStoreManager = [super allocWithZone:zone];
return _sharedStoreManager; // assignment and return on first allocation
}
}
return nil; //on subsequent allocation attempts return nil
}
- (id)copyWithZone:(NSZone *)zone
{
return self;
}
- (id)retain
{
return self;
}
- (unsigned)retainCount
{
return UINT_MAX; //denotes an object that cannot be released
}
- (void)release
{
//do nothing
}
- (id)autorelease
{
return self;
}
#pragma mark Internal MKStoreKit functions
- (void) restorePreviousTransactions
{
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
-(void) requestProductData
{
SKProductsRequest *request= [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObjects:
kFeatureAId,
kConsumableFeatureBId,
kConsumableBaseFeatureId,
nil]];
request.delegate = self;
[request start];
}
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
[self.purchasableObjects addObjectsFromArray:response.products];
#ifndef NDEBUG
for(int i=0;i<[self.purchasableObjects count];i++)
{
SKProduct *product = [self.purchasableObjects objectAtIndex:i];
NSLog(#"Feature: %#, Cost: %f, ID: %#",[product localizedTitle],
[[product price] doubleValue], [product productIdentifier]);
}
for(NSString *invalidProduct in response.invalidProductIdentifiers)
NSLog(#"Problem in iTunes connect configuration for product: %#", invalidProduct);
#endif
[request autorelease];
isProductsAvailable = YES;
if([_delegate respondsToSelector:#selector(productFetchComplete)])
[_delegate productFetchComplete];
}
// call this function to check if the user has already purchased your feature
+ (BOOL) isFeaturePurchased:(NSString*) featureId
{
return [[NSUserDefaults standardUserDefaults] boolForKey:featureId];
}
// Call this function to populate your UI
// this function automatically formats the currency based on the user's locale
- (NSMutableArray*) purchasableObjectsDescription
{
NSMutableArray *productDescriptions = [[NSMutableArray alloc] initWithCapacity:[self.purchasableObjects count]];
for(int i=0;i<[self.purchasableObjects count];i++)
{
SKProduct *product = [self.purchasableObjects objectAtIndex:i];
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
[numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
[numberFormatter setLocale:product.priceLocale];
NSString *formattedString = [numberFormatter stringFromNumber:product.price];
[numberFormatter release];
// you might probably need to change this line to suit your UI needs
NSString *description = [NSString stringWithFormat:#"%# (%#)",[product localizedTitle], formattedString];
#ifndef NDEBUG
NSLog(#"Product %d - %#", i, description);
#endif
[productDescriptions addObject: description];
}
[productDescriptions autorelease];
return productDescriptions;
}
- (void) buyFeature:(NSString*) featureId
{
if([self canCurrentDeviceUseFeature: featureId])
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(#"Review request approved", #"")
message:NSLocalizedString(#"You can use this feature for reviewing the app.", #"")
delegate:self
cancelButtonTitle:NSLocalizedString(#"Dismiss", #"")
otherButtonTitles:nil];
[alert show];
[alert release];
[self enableContentForThisSession:featureId];
return;
}
if ([SKPaymentQueue canMakePayments])
{
SKPayment *payment = [SKPayment paymentWithProductIdentifier:featureId];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
else
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(#"In-App Purchasing disabled", #"")
message:NSLocalizedString(#"Check your parental control settings and try again later", #"")
delegate:self
cancelButtonTitle:NSLocalizedString(#"Dismiss", #"")
otherButtonTitles: nil];
[alert show];
[alert release];
}
}
- (BOOL) canConsumeProduct:(NSString*) productIdentifier
{
int count = [[NSUserDefaults standardUserDefaults] integerForKey:productIdentifier];
return (count > 0);
}
- (BOOL) canConsumeProduct:(NSString*) productIdentifier quantity:(int) quantity
{
int count = [[NSUserDefaults standardUserDefaults] integerForKey:productIdentifier];
return (count >= quantity);
}
- (BOOL) consumeProduct:(NSString*) productIdentifier quantity:(int) quantity
{
int count = [[NSUserDefaults standardUserDefaults] integerForKey:productIdentifier];
if(count < quantity)
{
return NO;
}
else
{
count -= quantity;
[[NSUserDefaults standardUserDefaults] setInteger:count forKey:productIdentifier];
return YES;
}
}
-(void) enableContentForThisSession: (NSString*) productIdentifier
{
if([_delegate respondsToSelector:#selector(productPurchased:)])
[_delegate productPurchased:productIdentifier];
}
#pragma mark In-App purchases callbacks
// In most cases you don't have to touch these methods
-(void) provideContent: (NSString*) productIdentifier
forReceipt:(NSData*) receiptData
{
if(ownServer != nil && SERVER_PRODUCT_MODEL)
{
// ping server and get response before serializing the product
// this is a blocking call to post receipt data to your server
// it should normally take a couple of seconds on a good 3G connection
if(![self verifyReceipt:receiptData]) return;
}
NSRange range = [productIdentifier rangeOfString:kConsumableBaseFeatureId];
NSString *countText = [productIdentifier substringFromIndex:range.location+[kConsumableBaseFeatureId length]];
int quantityPurchased = [countText intValue];
if(quantityPurchased != 0)
{
int oldCount = [[NSUserDefaults standardUserDefaults] integerForKey:productIdentifier];
oldCount += quantityPurchased;
[[NSUserDefaults standardUserDefaults] setInteger:oldCount forKey:productIdentifier];
}
else
{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:productIdentifier];
}
[[NSUserDefaults standardUserDefaults] synchronize];
if([_delegate respondsToSelector:#selector(productPurchased:)])
[_delegate productPurchased:productIdentifier];
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:#"In-App Upgrade" message:#"Successfull removal of advertisements upgrade - thankyou" delegate:self cancelButtonTitle:#"ok" otherButtonTitles:nil, nil];
[alert show];
[alert release];
}
- (void) transactionCanceled: (SKPaymentTransaction *)transaction
{
#ifndef NDEBUG
NSLog(#"User cancelled transaction: %#", [transaction description]);
#endif
if([_delegate respondsToSelector:#selector(transactionCanceled)])
[_delegate transactionCanceled];
}
- (void) failedTransaction: (SKPaymentTransaction *)transaction
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:[transaction.error localizedFailureReason]
message:[transaction.error localizedRecoverySuggestion]
delegate:self
cancelButtonTitle:NSLocalizedString(#"Dismiss", #"")
otherButtonTitles: nil];
[alert show];
[alert release];
}
#pragma mark In-App purchases promo codes support
// This function is only used if you want to enable in-app purchases for free for reviewers
// Read my blog post http://mk.sg/31
- (BOOL) canCurrentDeviceUseFeature: (NSString*) featureID
{
NSString *uniqueID = [[UIDevice currentDevice] uniqueIdentifier];
// check udid and featureid with developer's server
if(ownServer == nil) return NO; // sanity check
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"%#/%#", ownServer, #"featureCheck.php"]];
NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:60];
[theRequest setHTTPMethod:#"POST"];
[theRequest setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
NSString *postData = [NSString stringWithFormat:#"productid=%#&udid=%#", featureID, uniqueID];
NSString *length = [NSString stringWithFormat:#"%d", [postData length]];
[theRequest setValue:length forHTTPHeaderField:#"Content-Length"];
[theRequest setHTTPBody:[postData dataUsingEncoding:NSASCIIStringEncoding]];
NSHTTPURLResponse* urlResponse = nil;
NSError *error = [[[NSError alloc] init] autorelease];
NSData *responseData = [NSURLConnection sendSynchronousRequest:theRequest
returningResponse:&urlResponse
error:&error];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSASCIIStringEncoding];
BOOL retVal = NO;
if([responseString isEqualToString:#"YES"])
{
retVal = YES;
}
[responseString release];
return retVal;
}
// This function is only used if you want to enable in-app purchases for free for reviewers
// Read my blog post http://mk.sg/
-(BOOL) verifyReceipt:(NSData*) receiptData
{
if(ownServer == nil) return NO; // sanity check
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"%#/%#", ownServer, #"verifyProduct.php"]];
NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:60];
[theRequest setHTTPMethod:#"POST"];
[theRequest setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
NSString *receiptDataString = [[NSString alloc] initWithData:receiptData encoding:NSASCIIStringEncoding];
NSString *postData = [NSString stringWithFormat:#"receiptdata=%#", receiptDataString];
[receiptDataString release];
NSString *length = [NSString stringWithFormat:#"%d", [postData length]];
[theRequest setValue:length forHTTPHeaderField:#"Content-Length"];
[theRequest setHTTPBody:[postData dataUsingEncoding:NSASCIIStringEncoding]];
NSHTTPURLResponse* urlResponse = nil;
NSError *error = [[[NSError alloc] init] autorelease];
NSData *responseData = [NSURLConnection sendSynchronousRequest:theRequest
returningResponse:&urlResponse
error:&error];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSASCIIStringEncoding];
BOOL retVal = NO;
if([responseString isEqualToString:#"YES"])
{
retVal = YES;
}
[responseString release];
return retVal;
}
#end
When the purchase completes, it is up to you to remove the ads. Do something similar to this.
[bannerView removeFromSuperview];
All you have to do is hide the ads when the purchase completes.
i display route from start to end point in mapView in iphone and i done it.But after one day i open this project and it does not display route because UICGDirectionsDelegate functions are not called. I dont know why its happen kindly some body guide me about this problem.Many thanks and my sample code is here`
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
if (diretions.isInitialized) {
[self update];
}
routeOverlayView = [[UICRouteOverlayMapView alloc] initWithMapView:mapViews];
diretions = [UICGDirections sharedDirections];
diretions.delegate = self;
}
- (IBAction)backButton:(id)sender {
[self.navigationController popViewControllerAnimated:YES];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)update {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
UICGDirectionsOptions *options = [[UICGDirectionsOptions alloc] init];
options.travelMode = travelMode;
if (is_route) {
startPoint = [NSString stringWithFormat:#"%f,%f",APPDELEGATE.user_latitude,APPDELEGATE.user_longitude];
endPoint = [NSString stringWithFormat:#"%#,%#",routeObj.latitude,routeObj.longitude];
destination = poiObj.english_title;
}else {
startPoint = [NSString stringWithFormat:#"%f,%f",APPDELEGATE.user_latitude,APPDELEGATE.user_longitude];
endPoint = [NSString stringWithFormat:#"%# ,%#",poiObj.latitude,poiObj.longitude];
destination = routeObj.starting_poi_name;
}
[diretions loadWithStartPoint:startPoint endPoint:endPoint options:options];
}
- (void)moveToCurrentLocation:(id)sender {
[mapViews setCenterCoordinate:[mapViews.userLocation coordinate] animated:YES];
}
- (void)addPinAnnotation:(id)sender {
UICRouteAnnotation *pinAnnotation = [[UICRouteAnnotation alloc] initWithCoordinate:[mapViews centerCoordinate]
title:nil
annotationType:UICRouteAnnotationTypeWayPoint];
[mapViews addAnnotation:pinAnnotation];
}
#pragma mark <UICGDirectionsDelegate> Methods
- (void)directionsDidFinishInitialize:(UICGDirections *)directions {
[self update];
}
- (void)directions:(UICGDirections *)directions didFailInitializeWithError:(NSError *)error {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Map Directions" message:[error localizedFailureReason] delegate:nil cancelButtonTitle:nil otherButtonTitles:#"OK", nil];
[alertView show];
}
- (void)directionsDidUpdateDirections:(UICGDirections *)directions {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
// Overlay polylines
UICGPolyline *polyline = [directions polyline];
NSArray *routePoints = [polyline routePoints];
[routeOverlayView setRoutes:routePoints];
// Add annotations
currentLocation = #"You are here";
UICRouteAnnotation *startAnnotation = [[UICRouteAnnotation alloc] initWithCoordinate:[[routePoints objectAtIndex:0] coordinate]
title:currentLocation
annotationType:UICRouteAnnotationTypeStart];
UICRouteAnnotation *endAnnotation = [[UICRouteAnnotation alloc] initWithCoordinate:[[routePoints lastObject] coordinate]
title:destination
annotationType:UICRouteAnnotationTypeEnd];
[mapViews addAnnotations:[NSArray arrayWithObjects:startAnnotation, endAnnotation, nil]];
}
- (void)directions:(UICGDirections *)directions didFailWithMessage:(NSString *)message {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Map Directions" message:message delegate:nil cancelButtonTitle:nil otherButtonTitles:#"OK", nil];
[alertView show];
}
`
In UICGRoute.m
Replace this
NSArray *stepDics;
NSDictionary *k;
for (int i = 0; i<allKeys.count; i++) {
k = [dictionaryRepresentation objectForKey:[allKeys objectAtIndex:i]];
if ([k objectForKey:#"Steps"]) {
stepDics = [k objectForKey:#"Steps"];
break;
}
}
with
NSDictionary *k = [dictionaryRepresentation objectForKey:[allKeys objectAtIndex:[allKeys count] - 1]];
NSArray *stepDics = [k objectForKey:#"Steps"];
I cannot seem to figure out what to do to resolve this error that I am receiving. I click on a cell which pops a new UITableViewController onto the stack. Once I hit the back button on the Navigation UI when in this controller I receive this error in the debugger but there doesn't seem to be any issue with the app as it doesn't crash or hang and still works fine.
An instance 0x79a8400 of class UITableView was deallocated while key value observers were still registered with it. Observation info was leaked, and may even become mistakenly attached to some other object. Set a breakpoint on NSKVODeallocateBreak to stop here in the debugger. Here's the current observation info:
(
Context: 0x0, Property: 0x738c010>
)
Code is below and I am using similar code on other UITableViewControllers but not receiving the error.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self)
{
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
pull = [[PullToRefreshView alloc] initWithScrollView:(UIScrollView *) self.tableView];
[pull setDelegate:self];
[self.tableView addSubview:pull];
[tableView.dataSource self];
[tableView.delegate self];
NSString *isAuthenticated = [[NSString alloc] init];
isAuthenticated = [self retrieveUserToken:[[NSUserDefaults standardUserDefaults] valueForKey:#"email"]];
NSNumber *categorySelected = [[NSNumber alloc] init];
categorySelected = [[NSUserDefaults standardUserDefaults] valueForKey:#"category_id"];
if (![isAuthenticated length])
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Alert" message:#"Message" delegate:self cancelButtonTitle:#"Dismiss" otherButtonTitles:nil, nil];
[alert show];
return;
}else if (categorySelected ==nil)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"alert" message:#"message" delegate:self cancelButtonTitle:#"Dismiss" otherButtonTitles:nil, nil];
[alert show];
return;
}
[self getTableViewData];
}
- (void)viewDidUnload
{
[self setTableView:nil];
pull = nil;
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (NSString *)retrieveUserToken:(NSString *)user
{
NSError *error = nil;
NSString *username = user;
return [SFHFKeychainUtils getPasswordForUsername:username andServiceName:#"app" error:&error];
}
- (void)getTableViewData
{
URLSingleton *urls = [[URLSingleton alloc] init];
responseData = [NSMutableData data];
NSNumber *categoryID = [[NSNumber alloc] init];
categoryID = [[NSUserDefaults standardUserDefaults] valueForKey:#"category_id"];
NSString *urlComplete = [[NSString alloc] init];
urlComplete = [urls getEvent:categoryID];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:urlComplete]];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[connection start];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return categories.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"cell"];
cell.textLabel.textColor = [UIColor blackColor];
cell.textLabel.text = [categories objectAtIndex:indexPath.row];
return cell;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.tableView reloadData];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[responseData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[responseData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Alert" message:#"Message." delegate:self cancelButtonTitle:#"Dismiss" otherButtonTitles:nil, nil];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[pull finishedLoading];
[alert show];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
NSDictionary *dictionary = [responseString JSONValue];
NSArray *response = [dictionary valueForKey:#"name"];
NSArray *responseID = [dictionary valueForKey:#"id"];
categories = [[NSMutableArray alloc] initWithArray:response];
eventID = [[NSMutableArray alloc] initWithArray:responseID];
[self.tableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[pull finishedLoading];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *selectedCell = [self.tableView cellForRowAtIndexPath:indexPath];
NSString *cellText = selectedCell.textLabel.text;
int i = 0;
for(NSString *name in categories)
{
if ([name isEqualToString:cellText])
{
[[NSUserDefaults standardUserDefaults] setValue:[eventID objectAtIndex:i] forKey:#"event_id"];
[[NSUserDefaults standardUserDefaults] setValue:cellText forKey:#"event_name"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
i++;
}
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
if(checkedIndexPath) {
UITableViewCell* uncheckCell = [self.tableView
cellForRowAtIndexPath:checkedIndexPath];
uncheckCell.accessoryType = UITableViewCellAccessoryNone;
}
UITableViewCell* cell = [self.tableView cellForRowAtIndexPath:indexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
checkedIndexPath = indexPath;
}
-(void)tableView:(UITableView*)tableView willBeginEditingRowAtIndexPath:(NSIndexPath *)indexPath
{
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
URLSingleton *urls = [URLSingleton sharedInstance];
NSNumber *event = [[NSNumber alloc] init];
if(editingStyle == UITableViewCellEditingStyleDelete)
{
UITableViewCell *selectedCell = [self.tableView cellForRowAtIndexPath:indexPath];
NSString *cellText = selectedCell.textLabel.text;
int i = 0;
for(NSString *name in categories)
{
if ([name isEqualToString:cellText])
{
event = [eventID objectAtIndex:i];
[eventID removeObjectAtIndex:i];
}
i++;
}
NSString *reqURL = [[NSString alloc] initWithString:[urls deleteEvent:[event stringValue]]];
NSURLRequest *delReq = [NSURLRequest requestWithURL:[NSURL URLWithString:reqURL]];
NSURLResponse *resp = nil;
NSError *err = nil;
NSData *response = [NSURLConnection sendSynchronousRequest:delReq returningResponse: &resp error: &err];
NSString *reply = [[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding];
SBJsonParser *parser = [SBJsonParser new];
id content = [reply JSONValue];
if(!content){
NSLog(#"%#", parser.errorTrace);
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
return;
}
NSNumber *status = [content valueForKey:#"success"];
NSNumber *one = [[NSNumber alloc] initWithInt:1];
if ([status isEqualToNumber:one])
{
[categories removeObjectAtIndex:indexPath.row];
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}else
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Alert" message:#"Message" delegate:self cancelButtonTitle:#"Dismiss" otherButtonTitles:nil, nil];
[alert show];
}
}
}
- (void)pullToRefreshViewShouldRefresh:(PullToRefreshView *)view;
{
NSString *isAuthenticated = [[NSString alloc] init];
isAuthenticated = [self retrieveUserToken:[[NSUserDefaults standardUserDefaults] valueForKey:#"email"]];
NSNumber *categorySelected = [[NSNumber alloc] init];
categorySelected = [[NSUserDefaults standardUserDefaults] valueForKey:#"category_id"];
if (![isAuthenticated length])
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Alert" message:#"Message" delegate:self cancelButtonTitle:#"Dismiss" otherButtonTitles:nil, nil];
[alert show];
return;
}else if (categorySelected ==nil)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Alert" message:#"Message" delegate:self cancelButtonTitle:#"Dismiss" otherButtonTitles:nil, nil];
[alert show];
return;
}
[self getTableViewData];
}
- (IBAction)createEvent:(id)sender
{
NSString *isAuthenticated = [[NSString alloc] init];
isAuthenticated = [self retrieveUserToken:[[NSUserDefaults standardUserDefaults] valueForKey:#"email"]];
NSNumber *categorySelected = [[NSNumber alloc] init];
categorySelected = [[NSUserDefaults standardUserDefaults] valueForKey:#"category_id"];
if (![isAuthenticated length])
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Alert" message:#"Message" delegate:self cancelButtonTitle:#"Dismiss" otherButtonTitles:nil, nil];
[alert show];
return;
}else if (categorySelected == nil)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Alert" message:#"Alert" delegate:self cancelButtonTitle:#"Dismiss" otherButtonTitles:nil, nil];
[alert show];
return;
}
AddEventViewController *aevc = [self.storyboard instantiateViewControllerWithIdentifier:#"AddEventViewController"];
[self.navigationController popToViewController:aevc animated:YES];
}
I fixed it by adding the following method
- (void)dealloc
{
[self.tableView removeObserver:pull forKeyPath:#"contentOffset"];
}
I am writing an iPhone app that requires that I send an e-mail attachment programmatically. The attachment is a csv file, that I create through the code. I then attach the file to the email, and the attachment shows up on the phone. When I send the email to myself, however, the attachment doesn't appear in the e-mail. Here is the code I'm using.
[self exportData];
if ([MFMailComposeViewController canSendMail])
{
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"expenses" ofType:#"csv"];
NSData *myData = [NSData dataWithContentsOfFile:filePath];
MFMailComposeViewController *mailer = [[MFMailComposeViewController alloc] init];
mailer.mailComposeDelegate = self;
[mailer setSubject:#"Vehicle Expenses from myConsultant"];
NSString *emailBody = #"";
[mailer setMessageBody:emailBody isHTML:NO];
[mailer addAttachmentData:myData mimeType:#"text/plain" fileName:#"expenses"];
[self presentModalViewController:mailer animated:YES];
[mailer release];
}
else
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Failure"
message:#"Your device doesn't support the composer sheet"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
[alert release];
}
}
- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error
{
switch (result)
{
case MFMailComposeResultCancelled:
NSLog(#"Mail cancelled: you cancelled the operation and no email message was queued.");
break;
case MFMailComposeResultSaved:
NSLog(#"Mail saved: you saved the email message in the drafts folder.");
break;
case MFMailComposeResultSent:
NSLog(#"Mail send: the email message is queued in the outbox. It is ready to send.");
break;
case MFMailComposeResultFailed:
NSLog(#"Mail failed: the email message was not saved or queued, possibly due to an error.");
break;
default:
NSLog(#"Mail not sent.");
break;
}
// Remove the mail view
[self dismissModalViewControllerAnimated:YES];
The is being successfully created- I checked in the simulator files.
- (void) exportData
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
NSString *root = [documentsDir stringByAppendingPathComponent:#"expenses.csv"];
NSString *temp=#"Date,Purpose,Start Odometer,End Odometer, Total Driven, Fees, ";
for(int i = 0; i < expenses.count; i++){
VehicleExpense *tempExpense = [expenses objectAtIndex:i];
temp = [temp stringByAppendingString:tempExpense.date];
temp = [temp stringByAppendingString:#", "];
temp = [temp stringByAppendingString:tempExpense.purpose];
temp = [temp stringByAppendingString:#", "];
temp = [temp stringByAppendingString:[NSString stringWithFormat: #"%.02f",tempExpense.start_mile]];
temp = [temp stringByAppendingString:#", "];
temp = [temp stringByAppendingString:[NSString stringWithFormat: #"%.02f",tempExpense.end_mile]];
temp = [temp stringByAppendingString:#", "];
temp = [temp stringByAppendingString:[NSString stringWithFormat: #"%.02f",tempExpense.distance]];
temp = [temp stringByAppendingString:#", "];
temp = [temp stringByAppendingString:[NSString stringWithFormat: #"%.02f",tempExpense.fees]];
temp = [temp stringByAppendingString:#", "];
}
[temp writeToFile:root atomically:YES encoding:NSUTF8StringEncoding error:NULL];
NSLog(#"got here in export data--- %#", documentsDir);
}
try
[mailer addAttachmentData:myData mimeType:#"text/csv" fileName:#"expenses.csv"];
Edit:
This is the code I'm using in my app:
- (IBAction) ExportData:(id)sender
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:kExportFileName];
self.timeRecords = [[NSMutableArray alloc] init];
for (int i=0; i< [self.selectedTimeEntries count]; i++)
for (int j=0; j<[[self.selectedTimeEntries objectAtIndex:i] count]; j++)
if ([[self.selectedTimeEntries objectAtIndex:i] objectAtIndex:j] == [NSNumber numberWithBool:YES])
[self.timeRecords addObject:[self timeEntriesForDay:[self.uniqueArray objectAtIndex:i] forIndex:j]];
if( !([self.timeRecords count]!=0))
{
UIAlertView *alert=[[UIAlertView alloc] initWithTitle:#"There are no time entries selected!" message:#"Please select at least one time entry before proceeding" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
[alert release];
return;
}
NSMutableString *csvLine;
NSError *err = nil;
NSDateFormatter* dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd"];
NSString *dateString = nil;
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setPositiveFormat:#"###0.##"];
NSString *formattedNumberString = nil;
if(![[NSFileManager defaultManager] fileExistsAtPath:filePath])
{
[[NSFileManager defaultManager] createFileAtPath:filePath contents:nil attributes:nil];
}
for (timeEntries *timeEntry in self.timeRecords) {
csvLine = [NSMutableString stringWithString:timeEntry.client];
[csvLine appendString:#","];
[csvLine appendString:timeEntry.category];
[csvLine appendString:#","];
[csvLine appendString:timeEntry.task];
[csvLine appendString:#","];
dateString = [dateFormatter stringFromDate:[NSDate dateWithTimeIntervalSince1970:timeEntry.date]];
[csvLine appendString:dateString];
[csvLine appendString:#","];
formattedNumberString = [numberFormatter stringFromNumber:timeEntry.duration];
[csvLine appendString:formattedNumberString];
[csvLine appendString:#","];
[csvLine appendString:timeEntry.description];
[csvLine appendString:#"\n"];
if([[NSFileManager defaultManager] fileExistsAtPath:filePath])
{
NSString *oldFile = [[NSString alloc] initWithContentsOfFile:filePath];
[csvLine insertString:oldFile atIndex:0];
BOOL success =[csvLine writeToFile:filePath atomically:NO encoding:NSUTF8StringEncoding error:&err];
if(success){
}
[oldFile release];
}
}
if (!appDelegate.shouldSendCSV) {
self.csvText = csvLine;
}
if([[NSFileManager defaultManager] fileExistsAtPath:filePath])
{
[self emailExport:filePath];
}
self.selectedTimeEntries =nil;
self.navigationController.toolbarHidden = NO;
}
- (void)emailExport:(NSString *)filePath
{
NSLog(#"Should send CSV = %#", [NSNumber numberWithBool:appDelegate.shouldSendCSV]);
MFMailComposeViewController *picker = [[MFMailComposeViewController alloc] init];
picker.mailComposeDelegate = self;
// Set the subject of email
[picker setSubject:#"My Billed Time Export"];
// Add email addresses
// Notice three sections: "to" "cc" and "bcc"
NSString *valueForEmail = [[NSUserDefaults standardUserDefaults] stringForKey:#"emailEntry"];
NSString *valueForCCEmail = [[NSUserDefaults standardUserDefaults] stringForKey:#"ccEmailEntry"];
if( valueForEmail == nil || [valueForEmail isEqualToString:#""])
{
UIAlertView *alert=[[UIAlertView alloc] initWithTitle:#"Please set an email address before sending a time entry!" message:#"You can change this address later from the settings menu of the application!" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
[alert release];
return;
}
else {
[picker setToRecipients:[NSArray arrayWithObjects:valueForEmail, nil]];
}
if(valueForCCEmail != nil || ![valueForCCEmail isEqualToString:#""])
{
[picker setCcRecipients:[NSArray arrayWithObjects:valueForCCEmail, nil]];
}
// Fill out the email body text
NSString *emailBody = #"My Billed Time Export File.";
// This is not an HTML formatted email
[picker setMessageBody:emailBody isHTML:NO];
if (appDelegate.shouldSendCSV) {
// Create NSData object from file
NSData *exportFileData = [NSData dataWithContentsOfFile:filePath];
// Attach image data to the email
[picker addAttachmentData:exportFileData mimeType:#"text/csv" fileName:#"MyFile.csv"];
} else {
[picker setMessageBody:self.csvText isHTML:NO];
}
// Show email view
[self presentModalViewController:picker animated:YES];
// Release picker
[picker release];
}
Dani's answer is great, however the code seems to be too long for someone looking for a simple way to send an attachment through email programatically.
The Gist
I pruned his answer to take out only the important bits, as well as refactored it like so:
MFMailComposeViewController *mailComposer = [[MFMailComposeViewController alloc] init];
mailComposer.mailComposeDelegate = self;
mailComposer.subject = #"Sample subject";
mailComposer.toRecipients = #[#"arthur#example.com", #"jeanne#example.com", ...];
mailComposer.ccRecipients = #[#"nero#example.com", #"mashu#example.com", ...];
[mailComposer setMessageBody:#"Sample body" isHTML:NO];
NSData *fileData = [NSData dataWithContentsOfFile:filePath];
[mailComposer addAttachmentData:fileData
mimeType:mimeType
fileName:fileName];
[self presentViewController:mailComposer animated:YES completion:nil];
That's basically the gist of it, this is enough as it is. If, for example, you put this code on a button's action, it will present an email composing screen with the respective fields pre-filled up, as well as having the file you want attached to the email.
Further Reading
Framework
The MFMailComposeViewController is under the MessageUI Framework, so to use it, import (if you have not done yet) the Framework like so:
#import <MessageUI/MessageUI.h>
Mail Capability Checking
Now when you run the source code, and you have not yet setup a mail account on your device, (not sure what the behavior is on simulator), this code will crash your app. It seems that if the mail account is not yet setup, doing [[MFMailComposeViewController alloc] init] will still result in the mailComposer being nil, causing the crash. As the answer in the linked question states:
You should check is MFMailComposeViewController are able to send your mail just before sending
You can do this by using the canSendMail method like so:
if (![MFMailComposeViewController canSendMail]) {
[self openCannotSendMailDialog];
return;
}
You can put this right before doing [[MFMailComposeViewController alloc] init] so that you can notify the user immediately.
Handling cannotSendMail
If canSendMail returns false, according to Apple Dev Docs, that means that the device is not configured for sending mail. This could mean that maybe the user has not yet setup their Mail account. To help the user with that, you can offer to open the Mail app and setup their account. You can do this like so:
NSURL *mailUrl = [NSURL URLWithString:#"message://"];
if ([[UIApplication sharedApplication] canOpenURL:mailUrl]) {
[[UIApplication sharedApplication] openURL:mailUrl];
}
You can then implement openCannotSendMailDialog like so:
- (void)openCannotSendMailDialog
{
UIAlertController *alert =
[UIAlertController alertControllerWithTitle:#"Error"
message:#"Cannot send mail."
preferredStyle:UIAlertControllerStyleAlert];
NSURL *mailUrl = [NSURL URLWithString:#"message://"];
if ([[UIApplication sharedApplication] canOpenURL:mailUrl]) {
[alert addAction:
[UIAlertAction actionWithTitle:#"Open Mail"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * _Nonnull action) {
[[UIApplication sharedApplication] openURL:mailUrl];
}]];
[alert addAction:
[UIAlertAction actionWithTitle:#"Cancel"
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * _Nonnull action) {
}]];
} else {
[alert addAction:
[UIAlertAction actionWithTitle:#"OK"
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * _Nonnull action) {
}]];
}
[self presentViewController:alert animated:YES completion:nil];
}
Mime Types
If like me, you forgot/are unsure which mimeType to use, here is a resource you can use. Most probably, text/plain is enough, if the file you are attaching is just a plain text, or image/jpeg / image/png for images.
Delegate
As you probably noticed, Xcode throws us a warning on the following line:
mailComposer.mailComposeDelegate = self;
This is because we have not yet set ourself to conform to the appropriate protocol and implement its delegate method. If you want to receive events whether the mail was cancelled, saved, sent or even failed sending, you need to set your class to conform to the protocol MFMailComposeViewControllerDelegate, and handle the following events:
MFMailComposeResultSent
MFMailComposeResultSaved
MFMailComposeResultCancelled
MFMailComposeResultFailed
According to Apple Dev Docs (emphasis mine):
The mail compose view controller is not dismissed automatically. When the user taps the buttons to send the email or cancel the interface, the mail compose view controller calls the mailComposeController:didFinishWithResult:error: method of its delegate. Your implementation of that method must dismiss the view controller explicitly.
With this in mind, we can then implement the delegate method like so:
- (void)mailComposeController:(MFMailComposeViewController *)controller
didFinishWithResult:(MFMailComposeResult)result
error:(NSError *)error
{
switch (result) {
case MFMailComposeResultSent:
// Mail was sent
break;
case MFMailComposeResultSaved:
// Mail was saved as draft
break;
case MFMailComposeResultCancelled:
// Mail composition was cancelled
break;
case MFMailComposeResultFailed:
//
break;
default:
//
break;
}
// Dismiss the mail compose view controller.
[controller dismissViewControllerAnimated:YES completion:nil];
}
Conclusion
The final code may look like so:
- (void)openMailComposerWithSubject:(NSString *)subject
toRecipientArray:(NSArray *)toRecipientArray
ccRecipientArray:(NSArray *)ccRecipientArray
messageBody:(NSString *)messageBody
isMessageBodyHTML:(BOOL)isHTML
attachingFileOnPath:(NSString)filePath
mimeType:(NSString *)mimeType
{
if (![MFMailComposeViewController canSendMail]) {
[self openCannotSendMailDialog];
return;
}
MFMailComposeViewController *mailComposer =
[[MFMailComposeViewController alloc] init];
mailComposer.mailComposeDelegate = self;
mailComposer.subject = subject;
mailComposer.toRecipients = toRecipientArray;
mailComposer.ccRecipients = ccRecipientArray;
[mailComposer setMessageBody:messageBody isHTML:isHTML];
NSData *fileData = [NSData dataWithContentsOfFile:filePath];
NSString *fileName = filePath.lastPathComponent;
[mailComposer addAttachmentData:fileData
mimeType:mimeType
fileName:fileName];
[self presentViewController:mailComposer animated:YES completion:nil];
}
- (void)openCannotSendMailDialog
{
UIAlertController *alert =
[UIAlertController alertControllerWithTitle:#"Error"
message:#"Cannot send mail."
preferredStyle:UIAlertControllerStyleAlert];
NSURL *mailUrl = [NSURL URLWithString:#"message://"];
if ([[UIApplication sharedApplication] canOpenURL:mailUrl]) {
[alert addAction:
[UIAlertAction actionWithTitle:#"Open Mail"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * _Nonnull action) {
[[UIApplication sharedApplication] openURL:mailUrl];
}]];
[alert addAction:
[UIAlertAction actionWithTitle:#"Cancel"
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * _Nonnull action) {
}]];
} else {
[alert addAction:
[UIAlertAction actionWithTitle:#"OK"
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * _Nonnull action) {
}]];
}
[self presentViewController:alert animated:YES completion:nil];
}
- (void)mailComposeController:(MFMailComposeViewController *)controller
didFinishWithResult:(MFMailComposeResult)result
error:(NSError *)error
{
NSString *message;
switch (result) {
case MFMailComposeResultSent:
message = #"Mail was sent.";
break;
case MFMailComposeResultSaved:
message = #"Mail was saved as draft.";
break;
case MFMailComposeResultCancelled:
message = #"Mail composition was cancelled.";
break;
case MFMailComposeResultFailed:
message = #"Mail sending failed.";
break;
default:
//
break;
}
// Dismiss the mail compose view controller.
[controller dismissViewControllerAnimated:YES completion:^{
if (message) {
UIAlertController *alert =
[UIAlertController alertControllerWithTitle:#"Confirmation"
message:message
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:
[UIAlertAction actionWithTitle:#"OK"
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * _Nonnull action) {
}]];
[self presentViewController:alert animated:YES completion:nil];
}
}];
}
With the button action looking like:
- (IBAction)mailButtonTapped:(id)sender
{
NSString *reportFilePath = ...
[self openMailComposerWithSubject:#"Report Files"
toRecipientArray:mainReportRecipientArray
ccRecipientArray:subReportRecipientArray
messageBody:#"I have attached report files in this email"
isMessageBodyHTML:NO
attachingFileOnPath:reportFilePath
mimeType:#"text/plain"];
}
I kind of went overboard here, but you can, with a grain of salt, take and use the code I posted here. Of course there is a need to adapt it to your requirements, but that is up to you. (I also modified this answer from my working source code, so there might be errors somewhere, please do comment if you find one :))
The issue was that I wasn't looking in the proper place for the file. Thanks #EmilioPalesz .
Here's the code I needed:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
NSString *root = [documentsDir stringByAppendingPathComponent:#"expenses.csv"]
NSData *myData = [NSData dataWithContentsOfFile:root];
I am developing on app in that i want to send email at the back ground.
for that i used the "SKPSMTP" library but when i got the mail it is without body so can any one tell me where i m wrong in my code.
following is the code on button click..
- (void)sendMessageInBack:(id)anObject
{
if(![self validateEmail:txt_email.text])
{
if([txt_email.text isEqualToString:#""] )
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Mandatory field" message:#"Please fill complete email" delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil];
[alert show];
[alert release];
}
else
{
if(self.interfaceOrientation==UIInterfaceOrientationLandscapeLeft || self.interfaceOrientation==UIInterfaceOrientationLandscapeRight )
{
if(rotate)
[btn_Send setImage:[UIImage imageNamed:#"send_button.png"] forState:UIControlStateNormal];
}
else if(self.interfaceOrientation==UIInterfaceOrientationPortrait || self.interfaceOrientation==UIInterfaceOrientationPortraitUpsideDown )
{
if(rotate)
[btn_Send setImage:[UIImage imageNamed:#"send button1.png"] forState:UIControlStateNormal];
}
NSLog(#"Start Sending");
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *writableDBPath = [documentsDirectory stringByAppendingPathComponent:#"RequestReview.txt"];
NSData *dataObj = [NSData dataWithContentsOfFile:writableDBPath];
//NSString *mailid;
SKPSMTPMessage *testMsg = [[SKPSMTPMessage alloc] init];
testMsg.fromEmail =#"spymekdemo#gmail.com";
testMsg.toEmail = #"priyanka.chinchmalatpure#gmail.com";// txtEmail.text;
testMsg.relayHost = #"smtp.gmail.com";
testMsg.requiresAuth = YES;
testMsg.login = #"spymekdemo#gmail.com";
testMsg.pass =#"spymek123";
testMsg.subject =#"Sbject"; //[NSString stringWithFormat:#"Reply to Review From %#", txtName.text];
testMsg.wantsSecure = YES; // smtp.gmail.com doesn't work without TLS!
// Only do this for self-signed certs!
testMsg.validateSSLChain = NO;
testMsg.delegate = self;
NSString *deviceIdentifier=[NSString stringWithFormat:#"%#",[[UIDevice currentDevice]uniqueIdentifier]];//[NSString stringWithFormat:#"%#",deviceToken.uniqueIdentifier];
NSLog(#"%#",deviceIdentifier);
//NSDictionary *plainPart = [NSDictionary dictionaryWithObjectsAndKeys:#"text/plain",kSKPSMTPPartContentTypeKey,
// [NSString stringWithFormat:#"Request Review\n\nDevice Identifier=%#\nProduct Name=%#\nProduct Download URL=%#\nUser Name=%#\nEmail=%#\nPromo Code=%#\nDescription Of Product=%#\n\n\n", deviceIdentifier, txtProduct.text,txtDownloadURL.text, txtName.text,txtEmail.text,txtPromocode.text, txtDescription.text]
// ,kSKPSMTPPartMessageKey,#"8bit",kSKPSMTPPartContentTransferEncodingKey,nil];
NSDictionary *plainPart = [NSDictionary dictionaryWithObjectsAndKeys:#"text/html",kSKPSMTPPartContentTypeKey,
[NSString stringWithFormat:#"<h2>Request Review</h2><br/><br/><b>Thank you for giving us your review</b> <br/>Device Identifier=%#<br/>Product Name=<br/>Product Download URL=<br/>User Name=<br/>Email=<br/>Promo Code=<br/>Description Of Product=<br/><br/><br/>", deviceIdentifier]
,kSKPSMTPPartMessageKey,#"8bit",kSKPSMTPPartContentTransferEncodingKey,nil];
// [NSString stringWithFormat:#"<h3>Review App</h3> <br/> <br/><b>Name=%#<b><br/> <br/>,\n<u>Email=%#</u><br/> <br/>,\nPassword=%#,<br/><br/> <b>\nComments=%#\n\n</b><br/>",txtName.text,txtMailId.text, txtPassword.text, txtComment.text],kSKPSMTPPartMessageKey,#"8bit",kSKPSMTPPartContentTransferEncodingKey,nil];
// #"<html><body><h1>Review App</h1> <br/> <b>Some text to include in body</b></body></html>"
NSDictionary *vcfPart = [NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:#"text/directory;\r\n\tx-unix-mode=0644;\r\n\tname=\"%#.txt\"",txt_name.text],kSKPSMTPPartContentTypeKey,
[NSString stringWithFormat:#"attachment;\r\n\tfilename=\"%#.txt\"",txt_name.text],kSKPSMTPPartContentDispositionKey,[dataObj encodeBase64ForData],kSKPSMTPPartMessageKey,#"base64",kSKPSMTPPartContentTransferEncodingKey,nil];
testMsg.parts = [NSArray arrayWithObjects:plainPart,vcfPart, nil]; //vcfPart
[testMsg send];
}
}
rotate=YES;
}
- (void)messageFailed:(SKPSMTPMessage *)message error:(NSError *)error
{
[message release];
//open an alert with just an OK button
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error" message:#"Unable to send email"
delegate:self cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alert show];
[alert release];
NSLog(#"delegate - error(%d): %#", [error code], [error localizedDescription]);
//[self ClearFile];
//UIAlertView *alert=[[UIAlertView alloc]initWithTitle:#"Request Review" message:#"Review Posting failed.. Try Again Later.." delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"Done",nil];
}
- (void)messageSent:(SKPSMTPMessage *)message
{
[message release];
NSLog(#"delegate - message sent");
}