Apple Pay detect Wallet has no credit cards - ios

I am trying to implement Apple Pay for my app. I have the PKPaymentAuthorizationViewController trying to load up the Apple Pay View. This view controller was being returned as Nil by the constructor if I didn't have any cards already in my wallet. So, I decided to guide the user though the process where they enter their card information. I was able to achieve this using
PKPassLibrary* lib = [[PKPassLibrary alloc] init];
[lib openPaymentSetup];
Here is the part where I have the initialization of the PKPaymentAuthorizationViewController. This returns a valid object on Simulator showing the view. But on a real device without a configured credit card returns nil and runs into an runtime exception. Here is the initialization code:
if ([PKPaymentAuthorizationViewController canMakePayments]) {
// init arr
[arr addObject:total];
request.paymentSummaryItems = arr;
PKPaymentAuthorizationViewController *paymentPane = [[PKPaymentAuthorizationViewController alloc] initWithPaymentRequest:request];
paymentPane.delegate = self;
[self presentViewController:paymentPane animated:TRUE completion:nil];
}
Here the array is a valid NSArray of PKPaymentSummaryItem which is why is successfully works on simulator.
I need to call the above method of openPaymentSetup, everytime I see a user without the credit card in their wallet. Is there a way to detect that?
Currently I am using
if ( [PKPassLibrary isPassLibraryAvailable] ) {
PKPassLibrary* lib = [[PKPassLibrary alloc] init];
if ([lib passesOfType:PKPassTypePayment].count == 0 ) {
[lib openPaymentSetup];
}
}
But this will not work since I am looking at the count of passes in wallet. Which may be like boarding pass for airline, or eventbrite pass, etc.
Looked at :
PKPaymentAuthorizationViewController present as nil view controller
Apple pay PKPaymentauthorizationViewController always returning nil when loaded with Payment request
https://developer.apple.com/library/ios/documentation/PassKit/Reference/PKPaymentAuthorizationViewController_Ref/

I did as suggested by #maddy, and it actually worked. Its unfortunate that apple has very limited documentation about it. Thanks Maddy.
Here is my code
-(BOOL) openAddCardForPaymentUIIfNeeded
{
if ( [PKPassLibrary isPassLibraryAvailable] )
{
if ( ![PKPaymentAuthorizationViewController canMakePaymentsUsingNetworks:[NSArray arrayWithObjects: PKPaymentNetworkAmex, PKPaymentNetworkMasterCard, PKPaymentNetworkVisa, nil]] )
{
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:#"Add a Credit Card to Wallet" message:#"Would you like to add a credit card to your wallet now?" delegate:self cancelButtonTitle:#"No" otherButtonTitles:#"Yes", nil];
[alert show];
return true;
}
}
return false;
}
Now I am directing the user to go to add a card wizard in the wallet app. Is there any way I can get the user back to the App after he/she has finished adding the card in the Wallet?
Thanks!

Related

Invite feature in iOS, how to send a person's contact a message from within your app? [duplicate]

Does anybody know if it's possible, and how, to programmatically send a SMS from the iPhone, with the official SDK / Cocoa Touch?
Restrictions
If you could send an SMS within a program on the iPhone, you'll be able to write games that spam people in the background. I'm sure you really want to have spams from your friends, "Try out this new game! It roxxers my boxxers, and yours will be too! roxxersboxxers.com!!!! If you sign up now you'll get 3,200 RB points!!"
Apple has restrictions for automated (or even partially automated) SMS and dialing operations. (Imagine if the game instead dialed 911 at a particular time of day)
Your best bet is to set up an intermediate server on the internet that uses an online SMS sending service and send the SMS via that route if you need complete automation. (ie, your program on the iPhone sends a UDP packet to your server, which sends the real SMS)
iOS 4 Update
iOS 4, however, now provides a viewController you can import into your application. You prepopulate the SMS fields, then the user can initiate the SMS send within the controller. Unlike using the "SMS:..." url format, this allows your application to stay open, and allows you to populate both the to and the body fields. You can even specify multiple recipients.
This prevents applications from sending automated SMS without the user explicitly aware of it. You still cannot send fully automated SMS from the iPhone itself, it requires some user interaction. But this at least allows you to populate everything, and avoids closing the application.
The MFMessageComposeViewController class is well documented, and tutorials show how easy it is to implement.
iOS 5 Update
iOS 5 includes messaging for iPod touch and iPad devices, so while I've not yet tested this myself, it may be that all iOS devices will be able to send SMS via MFMessageComposeViewController. If this is the case, then Apple is running an SMS server that sends messages on behalf of devices that don't have a cellular modem.
iOS 6 Update
No changes to this class.
iOS 7 Update
You can now check to see if the message medium you are using will accept a subject or attachments, and what kind of attachments it will accept. You can edit the subject and add attachments to the message, where the medium allows it.
iOS 8 Update
No changes to this class.
iOS 9 Update
No changes to this class.
iOS 10 Update
No changes to this class.
iOS 11 Update
No significant changes to this class
Limitations to this class
Keep in mind that this won't work on phones without iOS 4, and it won't work on the iPod touch or the iPad, except, perhaps, under iOS 5. You must either detect the device and iOS limitations prior to using this controller, or risk restricting your app to recently upgraded 3G, 3GS, and 4 iPhones.
However, an intermediate server that sends SMS will allow any and all of these iOS devices to send SMS as long as they have internet access, so it may still be a better solution for many applications. Alternately, use both, and only fall back to an online SMS service when the device doesn't support it.
Here is a tutorial which does exactly what you are looking for: the MFMessageComposeViewController.
http://blog.mugunthkumar.com/coding/iphone-tutorial-how-to-send-in-app-sms/
Essentially:
MFMessageComposeViewController *controller = [[[MFMessageComposeViewController alloc] init] autorelease];
if([MFMessageComposeViewController canSendText])
{
controller.body = #"SMS message here";
controller.recipients = [NSArray arrayWithObjects:#"1(234)567-8910", nil];
controller.messageComposeDelegate = self;
[self presentModalViewController:controller animated:YES];
}
And a link to the docs.
https://developer.apple.com/documentation/messageui/mfmessagecomposeviewcontroller
You must add the MessageUI.framework to your Xcode project
Include an #import <MessageUI/MessageUI.h> in your header file
Add these delegates to your header file MFMessageComposeViewControllerDelegate & UINavigationControllerDelegate
In your IBAction method declare instance of MFMessageComposeViewController say messageInstance
To check whether your device can send text use [MFMessageComposeViewController canSendText] in an if condition, it'll return Yes/No
In the if condition do these:
First set body for your messageInstance as:
messageInstance.body = #"Hello from Shah";
Then decide the recipients for the message as:
messageInstance.recipients = [NSArray arrayWithObjects:#"12345678", #"87654321", nil];
Set a delegate to your messageInstance as:
messageInstance.messageComposeDelegate = self;
In the last line do this:
[self presentModalViewController:messageInstance animated:YES];
You can use a sms:[target phone number] URL to open the SMS application, but there are no indications on how to prefill a SMS body with text.
One of the systems of inter-process communication in MacOS is XPC. This system layer has been developed for inter-process communication based on the transfer of plist structures using libSystem and launchd. In fact, it is an interface that allows managing processes via the exchange of such structures as dictionaries. Due to heredity, iOS 5 possesses this mechanism as well.
You might already understand what I mean by this introduction. Yep, there are system services in iOS that include tools for XPC communication. And I want to exemplify the work with a daemon for SMS sending. However, it should be mentioned that this ability is fixed in iOS 6, but is relevant for iOS 5.0—5.1.1. Jailbreak, Private Framework, and other illegal tools are not required for its exploitation. Only the set of header files from the directory /usr/include/xpc/* are needed.
One of the elements for SMS sending in iOS is the system service com.apple.chatkit, the tasks of which include generation, management, and sending of short text messages. For the ease of control, it has the publicly available communication port com.apple.chatkit.clientcomposeserver.xpc. Using the XPC subsystem, you can generate and send messages without user's approval. 
Well, let's try to create a connection.
xpc_connection_t myConnection;
dispatch_queue_t queue = dispatch_queue_create("com.apple.chatkit.clientcomposeserver.xpc", DISPATCH_QUEUE_CONCURRENT);
myConnection = xpc_connection_create_mach_service("com.apple.chatkit.clientcomposeserver.xpc", queue, XPC_CONNECTION_MACH_SERVICE_PRIVILEGED);
Now we have the XPC connection myConnection set to the service of SMS sending. However, XPC configuration provides for creation of suspended connections —we need to take one more step for the activation.
xpc_connection_set_event_handler(myConnection, ^(xpc_object_t event){
xpc_type_t xtype = xpc_get_type(event);
if(XPC_TYPE_ERROR == xtype)
{
NSLog(#"XPC sandbox connection error: %s\n", xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION));
}
// Always set an event handler. More on this later.
NSLog(#"Received a message event!");
});
xpc_connection_resume(myConnection);
The connection is activated. Right at this moment iOS 6 will display a message in the telephone log that this type of communication is forbidden. Now we need to generate a dictionary similar to xpc_dictionary with the data required for the message sending.
NSArray *recipient = [NSArray arrayWithObjects:#"+7 (90*) 000-00-00", nil];
NSData *ser_rec = [NSPropertyListSerialization dataWithPropertyList:recipient format:200 options:0 error:NULL];
xpc_object_t mydict = xpc_dictionary_create(0, 0, 0);
xpc_dictionary_set_int64(mydict, "message-type", 0);
xpc_dictionary_set_data(mydict, "recipients", [ser_rec bytes], [ser_rec length]);
xpc_dictionary_set_string(mydict, "text", "hello from your application!");
Little is left: send the message to the XPC port and make sure it is delivered.
xpc_connection_send_message(myConnection, mydict);
xpc_connection_send_barrier(myConnection, ^{
NSLog(#"The message has been successfully delivered");
});
That's all. SMS sent.
Add the MessageUI.Framework and use the following code
#import <MessageUI/MessageUI.h>
And then:
if ([MFMessageComposeViewController canSendText]) {
MFMessageComposeViewController *messageComposer =
[[MFMessageComposeViewController alloc] init];
NSString *message = #"Your Message here";
[messageComposer setBody:message];
messageComposer.messageComposeDelegate = self;
[self presentViewController:messageComposer animated:YES completion:nil];
}
and the delegate method -
- (void)messageComposeViewController:(MFMessageComposeViewController *)controller
didFinishWithResult:(MessageComposeResult)result {
[self dismissViewControllerAnimated:YES completion:nil];
}
You can use this approach:
[[UIApplication sharedApplication]openURL:[NSURL URLWithString:#"sms:MobileNumber"]]
iOS will automatically navigate from your app to the messages app's message composing page. Since the URL's scheme starts with sms:, this is identified as a type that is recognized by the messages app and launches it.
Follow this procedures
1 .Add MessageUI.Framework to project
2 . Import #import <MessageUI/MessageUI.h> in .h file.
3 . Copy this code for sending message
if ([MFMessageComposeViewController canSendText]) {
MFMessageComposeViewController *messageComposer =
[[MFMessageComposeViewController alloc] init];
NSString *message = #"Message!!!";
[messageComposer setBody:message];
messageComposer.messageComposeDelegate = self;
[self presentViewController:messageComposer animated:YES completion:nil];
}
4 . Implement delegate method if you want to.
- (void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result{
///your stuff here
[self dismissViewControllerAnimated:YES completion:nil];
}
Run And GO!
//Add the Framework in .h file
#import <MessageUI/MessageUI.h>
#import <MessageUI/MFMailComposeViewController.h>
//Set the delegate methods
UIViewController<UINavigationControllerDelegate,MFMessageComposeViewControllerDelegate>
//add the below code in .m file
- (void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
MFMessageComposeViewController *controller =
[[[MFMessageComposeViewController alloc] init] autorelease];
if([MFMessageComposeViewController canSendText])
{
NSString *str= #"Hello";
controller.body = str;
controller.recipients = [NSArray arrayWithObjects:
#"", nil];
controller.delegate = self;
[self presentModalViewController:controller animated:YES];
}
}
- (void)messageComposeViewController:
(MFMessageComposeViewController *)controller
didFinishWithResult:(MessageComposeResult)result
{
switch (result)
{
case MessageComposeResultCancelled:
NSLog(#"Cancelled");
break;
case MessageComposeResultFailed:
NSLog(#"Failed");
break;
case MessageComposeResultSent:
break;
default:
break;
}
[self dismissModalViewControllerAnimated:YES];
}
Here is the Swift version of code to send SMS in iOS. Please noted that it only works in real devices. Code tested in iOS 7+. You can read more here.
1) Create a new Class which inherits MFMessageComposeViewControllerDelegate and NSObject:
import Foundation
import MessageUI
class MessageComposer: NSObject, MFMessageComposeViewControllerDelegate {
// A wrapper function to indicate whether or not a text message can be sent from the user's device
func canSendText() -> Bool {
return MFMessageComposeViewController.canSendText()
}
// Configures and returns a MFMessageComposeViewController instance
func configuredMessageComposeViewController(textMessageRecipients:[String] ,textBody body:String) -> MFMessageComposeViewController {
let messageComposeVC = MFMessageComposeViewController()
messageComposeVC.messageComposeDelegate = self // Make sure to set this property to self, so that the controller can be dismissed!
messageComposeVC.recipients = textMessageRecipients
messageComposeVC.body = body
return messageComposeVC
}
// MFMessageComposeViewControllerDelegate callback - dismisses the view controller when the user is finished with it
func messageComposeViewController(controller: MFMessageComposeViewController!, didFinishWithResult result: MessageComposeResult) {
controller.dismissViewControllerAnimated(true, completion: nil)
}
}
2) How to use this class:
func openMessageComposerHelper(sender:AnyObject ,withIndexPath indexPath: NSIndexPath) {
var recipients = [String]()
//modify your recipients here
if (messageComposer.canSendText()) {
println("can send text")
// Obtain a configured MFMessageComposeViewController
let body = Utility.createInvitationMessageText()
let messageComposeVC = messageComposer.configuredMessageComposeViewController(recipients, textBody: body)
// Present the configured MFMessageComposeViewController instance
// Note that the dismissal of the VC will be handled by the messageComposer instance,
// since it implements the appropriate delegate call-back
presentViewController(messageComposeVC, animated: true, completion: nil)
} else {
// Let the user know if his/her device isn't able to send text messages
self.displayAlerViewWithTitle("Cannot Send Text Message", andMessage: "Your device is not able to send text messages.")
}
}
There is a class in iOS 4 which supports sending messages with body and recipents from your application. It works the same as sending mail. You can find the documentation here: link text
- (void)sendSMS:(NSString *)bodyOfMessage recipientList:(NSArray *)recipients
{
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
UIImage *ui =resultimg.image;
pasteboard.image = ui;
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:#"sms:"]];
}
//call method with name and number.
-(void)openMessageViewWithName:(NSString*)contactName withPhone:(NSString *)phone{
CTTelephonyNetworkInfo *networkInfo=[[CTTelephonyNetworkInfo alloc]init];
CTCarrier *carrier=networkInfo.subscriberCellularProvider;
NSString *Countrycode = carrier.isoCountryCode;
if ([Countrycode length]>0) //Check If Sim Inserted
{
[self sendSMS:msg recipientList:[NSMutableArray arrayWithObject:phone]];
}
else
{
[AlertHelper showAlert:#"Message" withMessage:#"No sim card inserted"];
}
}
//Method for sending message
- (void)sendSMS:(NSString *)bodyOfMessage recipientList:(NSMutableArray *)recipients{
MFMessageComposeViewController *controller1 = [[MFMessageComposeViewController alloc] init] ;
controller1 = [[MFMessageComposeViewController alloc] init] ;
if([MFMessageComposeViewController canSendText])
{
controller1.body = bodyOfMessage;
controller1.recipients = recipients;
controller1.messageComposeDelegate = self;
[self presentViewController:controller1 animated:YES completion:Nil];
}
}
If you want, you can use the private framework CoreTelephony which called CTMessageCenter class. There are a few methods to send sms.
Use this:
- (void)showSMSPicker
{
Class messageClass = (NSClassFromString(#"MFMessageComposeViewController"));
if (messageClass != nil) {
// Check whether the current device is configured for sending SMS messages
if ([messageClass canSendText]) {
[self displaySMSComposerSheet];
}
}
}
- (void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result
{
//feedbackMsg.hidden = NO;
// Notifies users about errors associated with the interface
switch (result)
{
case MessageComposeResultCancelled:
{
UIAlertView *alert1 = [[UIAlertView alloc] initWithTitle:#"Message" message:#"SMS sending canceled!!!" delegate:self cancelButtonTitle:nil otherButtonTitles:#"OK", nil];
[alert1 show];
[alert1 release];
}
// feedbackMsg.text = #"Result: SMS sending canceled";
break;
case MessageComposeResultSent:
{
UIAlertView *alert2 = [[UIAlertView alloc] initWithTitle:#"Message" message:#"SMS sent!!!" delegate:self cancelButtonTitle:nil otherButtonTitles:#"OK", nil];
[alert2 show];
[alert2 release];
}
// feedbackMsg.text = #"Result: SMS sent";
break;
case MessageComposeResultFailed:
{
UIAlertView *alert3 = [[UIAlertView alloc] initWithTitle:#"Message" message:#"SMS sending failed!!!" delegate:self cancelButtonTitle:nil otherButtonTitles:#"OK", nil];
[alert3 show];
[alert3 release];
}
// feedbackMsg.text = #"Result: SMS sending failed";
break;
default:
{
UIAlertView *alert4 = [[UIAlertView alloc] initWithTitle:#"Message" message:#"SMS not sent!!!" delegate:self cancelButtonTitle:nil otherButtonTitles:#"OK", nil];
[alert4 show];
[alert4 release];
}
// feedbackMsg.text = #"Result: SMS not sent";
break;
}
[self dismissModalViewControllerAnimated: YES];
}
[[UIApplication sharedApplication]openURL:[NSURL URLWithString:#"sms:number"]]
This would be the best and short way to do it.
You can present MFMessageComposeViewController, which can send SMS, but with user prompt(he taps send button). No way to do that without user permission. On iOS 11, you can make extension, that can be like filter for incoming messages , telling iOS either its spam or not. Nothing more with SMS cannot be done
You need to use the MFMessageComposeViewController if you want to show creating and sending the message in your own app.
Otherwise, you can use the sharedApplication method.

Apple pay PKPaymentauthorizationViewController always returning nil when loaded with Payment request

I am getting the PK Payment auth view controller instance returned as nil. What is wrong with this code?
if([PKPaymentAuthorizationViewController canMakePayments])
{
if ([PKPaymentAuthorizationViewController canMakePaymentsUsingNetworks:#[PKPaymentNetworkAmex, PKPaymentNetworkMasterCard, PKPaymentNetworkVisa]])
{
PKPaymentRequest *request = [[PKPaymentRequest alloc] init];
request.currencyCode = #"USD";
request.countryCode = #"US";
request.merchantCapabilities = 0;
request.requiredBillingAddressFields=PKAddressFieldAll;
request.merchantIdentifier = #"merchant.com.domain.mine";
PKPaymentSummaryItem *item = [[PKPaymentSummaryItem alloc] init];
item.label=#"Merchant";
item.amount=[NSDecimalNumber decimalNumberWithString:#"10"];
request.paymentSummaryItems=#[item];
PKPaymentAuthorizationViewController *viewController = [[PKPaymentAuthorizationViewController alloc] initWithPaymentRequest:request];
viewController.delegate = self;
[self presentViewController:viewController animated:YES completion:nil];
}
}
Before accessing the PKPaymentAuthorizationViewController, you should configure Apple Pay properly on your iPhone device. If you have not configured Apple Pay on your device you'll get nil value for PKPaymentAuthorizationViewController. You can even find an exception on the console stating "This device cannot make payment."
To configure Apple Pay on your device follow the below steps:
Go to Settings.
Select Passbook and Apple Pay option (if this option is not visible in settings, go to General -> Language & Region, change your region to US or UK, after this you'll be able to see the Passbook & Apple Pay option in Settings)
Open Passbook application from your home screen and configure a valid credit/debit card (US/UK based card only).
After verifying the added card, run your application you'll get a valid PKPaymentAuthorizationViewController instance.
Hope this will help.
I had a similar issue. It looks like you included it, but for anyone else struggling with this, my problem was not initially supplying merchantCapabilities to the request.
Swift:
request.merchantCapabilities = PKMerchantCapability.capability3DS
https://developer.apple.com/documentation/passkit/pkmerchantcapability?language=objc
If you are instantiating the supporting networks with raw value, make sure they are done so with the proper capitalization.
// Summarized for posting purposes
let networks = ["AmEx", "Visa", "MasterCard", "Discover"].reduce(into: [PKPaymentNetwork]()) { $0.append(PKPaymentNetwork($1)) }
if PKPaymentAuthorizationViewController.canMakePayments(usingNetworks: networks, capabilities: .capability3DS) {
// Hooray
}

PayPal iOS SDK nil View Controller

I am using the most recent PayPal iOS sdk in my app. For some unknown reason, when I try to init the PayPalPaymentViewContoller then present it, it is crashing. I have determined that the viewController is nil, but I have no idea why.
Here are the two lines of code to do with this.
The first line is the init, and the second is present
PayPalPaymentViewController *paymentViewController = [[PayPalPaymentViewController alloc] initWithPayment:payment configuration:self.payPalConfig delegate:self];
[self presentViewController:paymentViewController animated:YES completion:nil];
Any ideas? Let me what more information you need. I am not really sure what else to provide.
Dave from PayPal here.
#linuxer have you followed our sample code? In particular, from step 5:
// Check whether payment is processable.
if (!payment.processable) {
// If, for example, the amount was negative or the shortDescription was empty, then
// this payment would not be processable. You would want to handle that here.
}
If the payment is not "processable", but you go ahead anyway to create the PayPalPaymentViewController, then you would indeed get back nil.
You should also see a message to that effect in your console log. Have you taken a look there?
When you create object for PayPalItem at here in withPrice param, amount always be in two value after decimal. See below example:
PayPalItem *item1 = [PayPalItem itemWithName:#"T Shirt" withQuantity:1 withPrice:[NSDecimalNumber decimalNumberWithString:**
[NSString stringWithFormat:#"%0.2f",overAllTotalAmt]
**] withCurrency:#"USD" withSku:#"Hip-00037"];

ABRecordCopyValue() EXC_BAD_ACCESS Error while getting kABPersonFirstNameProperty

I am upgrading my Application written a year ago for iOS 6 to iOS 7/8 and I am getting this EXC_BAD_ACCESS error which never occurred in my old version.
In my application I am trying to fetch certain contact information like first name, last name, phone numbers, photo. Application flow is as follow:
1) Click on a button, presents address book.
2) Select any contact.
3.1) If contact has only one phone number, update the label.
3.2) If contact has multiple phone number, represent them in action sheet and whatever number user selects update that number to UILabel.
Now, if a contact has a single phone number application works fine without crash. i.e. 1-->2-->3.1 path. But if a contact has multiple phone and as soon as one contact number is selected from action sheet it crashes at this line.
CFTypeRef firstNameCF = (__bridge CFTypeRef)(CFBridgingRelease(ABRecordCopyValue(sharedSingleton.personGlobal, kABPersonFirstNameProperty)));
Detail Code
1) Select a contact
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker
shouldContinueAfterSelectingPerson:(ABRecordRef)person {
sharedSingleton.personGlobal = nil;
sharedSingleton.personGlobal=person; // ====> Save a ABRecordRef object globally.
//^^^ Could this be a culprit? I tried to make it private variable also at first.
[self displayAndVerifyPerson]; // No 2 below.
[self dismissViewControllerAnimated:YES completion:^{
}];
return NO;
}
2) Will check how many phone nos person has got. 0/1/>1.
If 0 show no phone no error.
If 1 phone update label by calling updateLabel.
If >1 represent action sheet for user to select number. And on clickedButtonIndex call updateLabel.
-(void)displayAndVerifyPerson
{
ABMultiValueRef phoneNumbers = ABRecordCopyValue(sharedSingleton.personGlobal,kABPersonPhoneProperty); //ABRecordRef which globally saved.
globalContact=nil; //NSString to store selected number. Works fine.
//self.personGlobal=person;
NSArray *phoneNumberArray = (__bridge_transfer NSArray *)ABMultiValueCopyArrayOfAllValues(phoneNumbers);
CFRelease(phoneNumbers);
if (ABMultiValueGetCount(phoneNumbers) > 0){ //Check if a contact has any number
NSLog(#" Number--> %#",phoneNumberArray); //Prints numbers correct whether no of contacts are 0/1/>1.
if ([phoneNumberArray count]==1){ //If exactly one contact number no problem.
globalContact = [phoneNumberArray objectAtIndex:0];
NSLog(#"--> %#",globalContact);
[self updateLabel]; // No 3 Below.
}
// We have multiple numbers so select any one.
else{
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:#"Select Number"
delegate:self
cancelButtonTitle:nil
destructiveButtonTitle:nil
otherButtonTitles:nil];
actionSheet.delegate=self;
actionSheet.tag=0;
for(int i=0;i<[phoneNumberArray count];i++){
[actionSheet addButtonWithTitle:[phoneNumberArray objectAtIndex:i]];
}
[actionSheet addButtonWithTitle:#"Cancel"];
actionSheet.destructiveButtonIndex = actionSheet.numberOfButtons - 1;
actionSheet.actionSheetStyle = UIActionSheetStyleBlackTranslucent;
UIWindow* window = [[[UIApplication sharedApplication] delegate] window];
if ([window.subviews containsObject:self.view])
[actionSheet showInView:self.view];
else
[actionSheet showInView:window];
}
}
else{ //No contact found. Display alert.
UIAlertView *av = [[UIAlertView alloc] initWithTitle:#"Error"
message:#"No contact numebr found."
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[av show];
return;
}
}
3) Fetch first name, Last name, Image from ABRecordRef Object.
-(void)updateLabel{
// ----------------- Get First Name From Global ABRecordRef personGlobal---------------------
CFTypeRef firstNameCF = (__bridge CFTypeRef)(CFBridgingRelease(ABRecordCopyValue(sharedSingleton.personGlobal, kABPersonFirstNameProperty)));
^^^^^^^^^^^^^^^^^^^^^^
Crashes only when `updateLabel` called from Actionsheet delegate `clickedButtonAtIndex`
NSString *fName = (NSString *)CFBridgingRelease(firstNameCF);
if ([fName length]==0){
UIAlertView *av = [[UIAlertView alloc] initWithTitle:#"Error"
message:#"Contact name not found."
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[av show];
return;
}
self.lblFirstName.text = fName; //Set label with first Name.
self.lblHomePhone.text = self.globalContact;//Set number label.
}
4) Actionsheet Delegate
-(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
NSString *buttonTitle=[actionSheet buttonTitleAtIndex:buttonIndex];
if(actionSheet.tag==0){
//Printing multiple phone numbers which works and prints perfect.
NSLog(#"Lets see what you got: ===> %#",buttonTitle);
if([buttonTitle isEqualToString:#"Cancel"])
return;
globalContact=buttonTitle; // Save contact to NSString for later use.
[self updateLabel]; // No. 3.
}
}
Extra Notes:
1) Questions I looked for solution(Just 3 of many).
i) ABRecordCopyValue() EXC_BAD_ACCESS Error
ii) EXC_BAD_ACCESS when adding contacts from Addressbook?
iii) kABPersonFirstNameProperty… trowing EXC_BAD_ACCESS
2) Sample project on dropbox if someone is generous/curious enough and wants to run and check.
3) My doubts regarding this error:
The same code works for a current App (Written for iOS 6) which is on App Store but crashes for iOS 7.
Could be due to Memory management of Core Foundation. I tried to release Core Foundation object wherever I used as ARC does not take care of them. But if that is a case then it should also crash while contact has only one phone number.
THREAD ISSUE? Since application only crashed shen contact has more than one phone number, I believe action sheet delegate method clickedButtonAtIndex running on background thread and something is going wrong? (Just a random guess!)
I have tried to make my question easy and informative at my best. Any suggestion, comment or solution will be appreciated as I have been trying to get rid of this issue for last 3 days. Thanks!
you deal with CoreFoundation:
sharedSingleton.personGlobal=person;
=>
since it isn't an arc object, you have to retain it
CFRetain(person);
sharedSingleton.personGlobal=person;
AND release it once done
- dealloc {
CFRelease(sharedSingleton.personGlobal);
}
Ignoring the weirdness of a lot of this code, the fundamental issue is that you are not retaining a value that you intend to use beyond the scope it is presented in. Specifically, I am referring to the person variable in section number 1. You don't retain this variable, and so it is free to be released at any time after the scope ends (which it likely does). Therefore, once you get around to calling updateLabel it is simply a dangling pointer. To fix this, you should make it a strong variable.
But wait a minute...that is only for Objective-C objects, so you need to do a little more decorating of the property. You can add __attribute__((NSObject)) to make this type behave as if it were an NSObject and subject to ARC. I can't find documentation about this anymore, but here is a reference from an old Apple Mailing List Thread

Does the Twitter iOS API provide any way of determining if the tweet was successful?

I'm using the following snippet of code to make a tweet in my iOS 5 application :
- (IBAction)postToTwitterClicked:(id)sender
{
if ([TWTweetComposeViewController canSendTweet])
{
TWTweetComposeViewController *tweetSheet = [[TWTweetComposeViewController alloc]init];
[tweetSheet setInitialText:#"Some sample message here"];
[tweetSheet addURL:[NSURL URLWithString:#"http://myURL"]];
[self presentModalViewController:tweetSheet animated:YES];
}
else
{
UIAlertView *av = [[UIAlertView alloc] initWithTitle:#"Unable to tweet"
message:#"Please ensure that you have at least one twitter account setup and have internet connectivity. You can setup a twitter account in the iOS Settings > Twitter > login."
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[av show];
}
}
This works fine, but how do I know that the user did actually post a tweet, or if there was a problem?
Since this doesn't implement a delegate, there are no "onError" methods that I can override.
I want to know if the user did successfully post a tweet, so I can action some behaviour such as
Disable a button so they can't do it again
Notify them the post was successful and will show up in their feed shortly
There is no way in the iOS Twitter API that you can see that a Tweet actually was posted on the server. But you can analyze the TWTweetComposeViewControllerResult to see if the tweet was finished composing successfully or if the tweet was cancelled.
twitter.completionHandler = ^(TWTweetComposeViewControllerResult res) {
if (res == TWTweetComposeViewControllerResultDone) {
// Composed
} else if (res == TWTweetComposeViewControllerResultCancelled) {
// Cancelled
}
[self dismissModalViewControllerAnimated:YES];
};
Well, actually, you only can set a handler to call when the user is done composing the tweet: TWTweetComposeViewControllerCompletionHandler. This handler has a single parameter that indicates whether the user finished or cancelled composing the tweet.
You can try to send a tweet and make it fail to check the result code (luckily it's cancelled?).
Another alternative to achieve the desired behaviour is use another API.

Resources