My app on the appstore is accessing the iPhone contacts, after the users downloaded it on iOS 6 it can't access the iPhone contacts while its working fine on iOS 5
the problem is the new privacy settings apple has put in iOS 6 .. so after searching i found out that i have to do the following in my code to be able to access the user contacts:
//in order to test addressbook availability we have to attempt to create an addressbook instance using ABAddressBookCreateWithOptions
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 60000
// Request authorization to Address Book
ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions(NULL, NULL);
if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined) {
ABAddressBookRequestAccessWithCompletion(addressBookRef,
^(bool granted, CFErrorRef error) {
if (granted)
[self loadContacts];
});
} else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized) {
// The user has previously given access, add the contact
[self loadContact];
} else {
}
#endif //end iOS6+
//ABAddressBookCreateWithOptions not available or succeeded. return YES;
[self loadContacts];
My problem now is while debugging on the device, the alert is not showing, i don't know why ?
I know that the above code should work fine, but only when the app is submitted to the appstore but i want to test that in debug mode before submission ?
Any advice ?
Appreciate your support.
Thanks.
I have managed to get it resolved
Here is the new code after a slight modification:
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 60000
__block MyClassType *controller = self;
// Request authorization to Address Book
ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions(NULL, NULL);
if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined) {
ABAddressBookRequestAccessWithCompletion(addressBookRef,
^(bool granted, CFErrorRef error) {
if (granted)
[controller loadContacts];
});
} else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized) {
// The user has previously given access, add the contact
[self loadContacts];
} else {
}
#else
[self loadContacts];
#endif
The key to be able to test it is to Reset the Privacy and location settings from Settings>>General>>Reset>>Reset Location & Privacy
It worked fine with me.
Resetting Location & Privacy doesn't work for me.
My authorization status is always kABAuthorizationStatusAuthorized, regardless of whether I clear simulator settings and then reset Location and Privacy.
Related
I am trying to get a clumsy Objective-C proof-of-concept example to run with SFSpeechRecognizer on Catalina transcribing a local audio file.
After some googling I have managed to get the authorization to work by adding an Info.plist with NSSpeechRecognitionUsageDescription and I get the authorization dialog and the correct SFSpeechRecognizerAuthorizationStatus (SFSpeechRecognizerAuthorizationStatusAuthorized).
However, my SFSpeechRecognizer instance still is unavailable. I suspect, I must be making a stupid mistake due to lack of basic Objective-C knowledge.
Any hints greatly appreciated.
Here's my code:
//
// main.m
// SpeechTestCatalina
//
#import <Foundation/Foundation.h>
#import <Speech/Speech.h>
void transcribeTestFile(){
NSLocale *locale =[[NSLocale alloc] initWithLocaleIdentifier:#"en-US"];
SFSpeechRecognizer *speechRecognizer = [[SFSpeechRecognizer alloc] initWithLocale:locale];
NSLog(#"Locale %#, %#", speechRecognizer.locale.languageCode, speechRecognizer.locale.countryCode);
NSLog(#"Available %hhd", speechRecognizer.available);
NSLog(#"Auth status %ld", [SFSpeechRecognizer authorizationStatus]);
NSLog(#"Supports on device %hhd", speechRecognizer.supportsOnDeviceRecognition);
if(speechRecognizer.isAvailable && speechRecognizer.supportsOnDeviceRecognition){
NSString *audioFilePath = #"/Users/doe/speech-detection/speech_sample.wav";
NSURL *url = [[NSURL alloc] initFileURLWithPath:audioFilePath];
NSLog(#"Analyzing %# in language %#", url, locale.languageCode);
SFSpeechURLRecognitionRequest *urlRequest = [[SFSpeechURLRecognitionRequest alloc] initWithURL:url];
urlRequest.requiresOnDeviceRecognition = true;
urlRequest.shouldReportPartialResults = YES; // YES if animate writting
[speechRecognizer recognitionTaskWithRequest: urlRequest resultHandler: ^(SFSpeechRecognitionResult * _Nullable result, NSError * _Nullable error){
NSString *transcriptText = result.bestTranscription.formattedString;
if(!error){
NSLog(#"Transcript: %#", transcriptText);
} else {
NSLog(#"Error: %#", error);
}
}];
} else {
NSLog(#"speechRecognizer is not available on this device");
}
}
int main(int argc, const char * argv[]) {
#autoreleasepool {
[SFSpeechRecognizer requestAuthorization:^(SFSpeechRecognizerAuthorizationStatus authStatus) {
NSLog(#"Status: %ld", (long)authStatus);
switch (authStatus) {
case SFSpeechRecognizerAuthorizationStatusAuthorized:
//User gave access to speech recognition
NSLog(#"Authorized");
transcribeTestFile();
break;
case SFSpeechRecognizerAuthorizationStatusDenied:
//User denied access to speech recognition
NSLog(#"SFSpeechRecognizerAuthorizationStatusDenied");
break;
case SFSpeechRecognizerAuthorizationStatusRestricted:
//Speech recognition restricted on this device
NSLog(#"SFSpeechRecognizerAuthorizationStatusRestricted");
break;
case SFSpeechRecognizerAuthorizationStatusNotDetermined:
//Speech recognition not yet authorized
break;
default:
NSLog(#"Default");
break;
}
}];
NSLog(#"Sleeping");
[NSThread sleepForTimeInterval:20.0f];
}
return 0;
}
The output when I run it is:
2020-01-26 17:48:39.454809+0100 SpeechTestCatalina[3623:82404] Sleeping
2020-01-26 17:48:41.182459+0100 SpeechTestCatalina[3623:82811] Status: 3
2020-01-26 17:48:41.182562+0100 SpeechTestCatalina[3623:82811] Authorized
2020-01-26 17:48:41.186933+0100 SpeechTestCatalina[3623:82811] Locale en, US
2020-01-26 17:48:41.190973+0100 SpeechTestCatalina[3623:82811] Available 0
2020-01-26 17:48:41.191269+0100 SpeechTestCatalina[3623:82811] Auth status 3
2020-01-26 17:48:41.197965+0100 SpeechTestCatalina[3623:82811] Supports on device 0
2020-01-26 17:48:41.198065+0100 SpeechTestCatalina[3623:82811] speechRecognizer is not available on this device
Program ended with exit code: 0
You aren't getting the callback because your binary does not have a runloop. I'll take the response from this different question but with the same answer:
Callbacks in most Apple frameworks are delivered through your application's main run loop. If your command-line tool does not have a run loop, it cannot receive callbacks that are sent this way.
Without a runloop, the only way for the framework to invoke your callback would be to run it on another thread, which could lead to weird behaviour in an application that didn't expect that.
You can manually pump the runloop by inserting this code before the end of main:
NSRunLoop* runloop = [NSRunLoop currentRunLoop];
[runloop runUntilDate:[NSDate distantFuture]];
This will prevent your application from exiting; you'll need to update your logic to know when speech recognition is finished and restructure that with a while loop or something - but I assume the logic inside your "real" application is different than this toy sample.
The message:
AddInstanceForFactory: No factory registered for id F8BB1C28-BAE8-11D6-9C31-00039315CD46 HALC_ShellDriverPlugIn::Open: Can't get a pointer to the Open routine
that appears in your console a meaningless; it's some log statement leaking out of the system frameworks and you can disregard it.
Finally, for clarification on a couple other points:
"Enable Ask Siri" was required to be enabled in System Preferences > Siri for speech recognition to be available
There is a potential issue where the device may report that "on device recognition" is not available the first time you check, despite being supported for the chosen locale
I am working on MAC Application to create VPN Connection from MY Application.
After a lot of Research i found that i need to run application as ROOT to store User password and sharedSecretKey in SYSTEM keychain.
Application user will not gonna open application as ROOT so that i need to add user password and sharedsecretkey in SYSTEM KEYCHAIN without ROOT Access.
I search on web on this and found that Apple Provide this code : https://developer.apple.com/library/mac/samplecode/SMJobBless/Introduction/Intro.html
https://developer.apple.com/library/mac/samplecode/EvenBetterAuthorizationSample/Introduction/Intro.html
but didn't understand how can i use this 2 code in my application to store user's password and SharedSecretKey in SYSTEM KEYCHAIN WITH OUT ROOT ACCESS.
Any help will be appreciated.
Thanks in advance.
Here is my code to add Password in SYSTEM KEYCHAIN Which is work great if i run my code as ROOT.
// Vendor dependencies
#import <Security/SecKeychain.h>
// Local dependencies
#import "VPNKeychain.h"
// These are the applications which are going to get access to new Keychain items.
// How do we know them? Just create a VPN service manualy and run the following command:
// security dump-keychain -a /Library/Keychains/System.keychain
// Among the results, you will find your VPN service and you can see the paths that have access to it
static const char * trustedAppPaths[] = {
"/System/Library/Frameworks/SystemConfiguration.framework/Versions/A/Helpers/SCHelper", "/System/Library/PreferencePanes/Network.prefPane/Contents/XPCServices/com.apple.preference.network.remoteservice.xpc",
"/System/Library/CoreServices/SystemUIServer.app",
"/usr/sbin/pppd",
"/usr/sbin/racoon",
"/usr/libexec/configd",
};
// This class contains all code we need to handle System Keychain Items
// Exit status codes: 60-79
#implementation VPNKeychain
// This will create a PPP Password Keychain Item
+ (int) createPasswordKeyChainItem:(NSString*)label forService:(NSString*)service withAccount:(NSString*)account andPassword:(NSString*)password {
return [self createItem:label withService:service account:account description:#"PPP Password" andPassword:password];
}
// This will create an IPSec Shared Secret Keychain Item
+ (int) createSharedSecretKeyChainItem:(NSString*)label forService:(NSString*)service withPassword:(NSString*)password {
service = [NSString stringWithFormat:#"%#.SS", service];
return [self createItem:label withService:service account:#"" description:#"IPSec Shared Secret" andPassword:password];
}
// A generic method to create Keychain Items holding Network service passwords
+ (int) createItem:(NSString*)label withService:(NSString*)service account:(NSString*)account description:(NSString*)description andPassword:(NSString*)password {
// This variable will hold all sorts of operation status responses
OSStatus status;
// Converting the NSStrings to char* variables which we will need later
const char *labelUTF8 = [label UTF8String];
const char *serviceUTF8 = [service UTF8String];
const char *accountUTF8 = [account UTF8String];
const char *descriptionUTF8 = [description UTF8String];
const char *passwordUTF8 = [password UTF8String];
// This variable is soon to hold the System Keychain
SecKeychainRef keychain = NULL;
status = SecKeychainCopyDomainDefault(kSecPreferencesDomainSystem, &keychain);
if (status == errSecSuccess) {
NSLog(#"Succeeded opening System Keychain");
} else {
NSLog(#"Could not obtain System Keychain: %#", SecCopyErrorMessageString(status, NULL));
return 60;
}
NSLog(#"Unlocking System Keychain");
status = SecKeychainUnlock(keychain, 0, NULL, FALSE);
if (status == errSecSuccess) {
NSLog(#"Succeeded unlocking System Keychain");
} else {
NSLog(#"Could not unlock System Keychain: %#", SecCopyErrorMessageString(status, NULL));
return 61;
}
// This variable is going to hold our new Keychain Item
SecKeychainItemRef item = nil;
SecAccessRef access = nil;
status = SecAccessCreate(CFSTR("Some VPN Test"), (__bridge CFArrayRef)(self.trustedApps), &access);
if(status == noErr) {
NSLog(#"Created empty Keychain access object");
} else {
NSLog(#"Could not unlock System Keychain: %#", SecCopyErrorMessageString(status, NULL));
return 62;
}
// Putting together the configuration options
SecKeychainAttribute attrs[] = {
{kSecLabelItemAttr, (int)strlen(labelUTF8), (char *)labelUTF8},
{kSecAccountItemAttr, (int)strlen(accountUTF8), (char *)accountUTF8},
{kSecServiceItemAttr, (int)strlen(serviceUTF8), (char *)serviceUTF8},
{kSecDescriptionItemAttr, (int)strlen(descriptionUTF8), (char *)descriptionUTF8},
};
SecKeychainAttributeList attributes = {sizeof(attrs) / sizeof(attrs[0]), attrs};
status = SecKeychainItemCreateFromContent(kSecGenericPasswordItemClass, &attributes, (int)strlen(passwordUTF8), passwordUTF8, keychain, access, &item);
if(status == noErr) {
NSLog(#"Successfully created Keychain Item");
} else {
NSLog(#"Creating Keychain item failed: %#", SecCopyErrorMessageString(status, NULL));
return 63;
}
return 0;
}
+(NSArray*) trustedApps {
NSMutableArray *apps = [NSMutableArray array];
SecTrustedApplicationRef app;
OSStatus err;
for (int i = 0; i < (sizeof(trustedAppPaths) / sizeof(*trustedAppPaths)); i++) {
err = SecTrustedApplicationCreateFromPath(trustedAppPaths[i], &app);
if (err == errSecSuccess) {
//NSLog(#"SecTrustedApplicationCreateFromPath succeeded: %#", SecCopyErrorMessageString(err, NULL));
} else {
NSLog(#"SecTrustedApplicationCreateFromPath failed: %#", SecCopyErrorMessageString(err, NULL));
}
[apps addObject:(__bridge id)app];
}
return apps;
}
In OS X, applications do not handle users' credentials directly, but instead request the system to do so, via the function call AuthorizationCopyRights, which is documented in Authorization Services.
A Gui application cannot directly perform administrative (root) actions and since Yosemite (10.10), Gui applications cannot run as root. Instead, your application must use a 'helper' application via XPC services, which is what the SMJobBless and BetterAuthorization samples demonstrate. You can read more about XPC here.
In your case, you would need to create such a helper application, which will have the necessary rights to access the system keychain.
Note that if you plan to distribute your application via the Apple Store, the application must be sandboxed and cannot use any security services, such as calling the function AuthorizationCopyRights.
It is explained in the sample code that you link to, see ReadMe.txt:
Once you run the sample you'll be prompted for an admin user name and
password. Enter your admin user name and password and, if all goes
well, the sample's window will show "The Helper Tool is available!"
indicating that everything is OK. If not, you can look in the console
log for information about the failure.
So generally, your application will have to ask for admin credentials at some point.
Update:
This should be done through a privileged helper tool, as demonstrated in cited SMJobBless example. Your helper tool should perform keychain access for your app. Here are main steps to install such helper tool:
Create authorisation object with AuthorizationCreate function.
Perform preauthorisation on the object with given set of rights using
AuthorizationCopyRights function. This will in fact result in asking your user for admin credentials.
Verify, install and register helper tool with launchd using
SMJobBless function.
Once the helper tool is installed and registered you should use NSXPCConnection to talk to your helper tool. See Sandboxing with NSXPCConnection sample code for details on how to achieve it.
I am trying to integrate Apple Pay in my demo app following this link & I am facing this issue issue. I have updated my iphone 6+ os to 8.1.1 version but still not able to present PKPaymentAuthorizationViewController properly & I am getting this error "Application tried to present a nil modal view controller on target".Please suggest something since I am stuck on this.Here is the code which I have written :-
PKPaymentRequest *request = [[PKPaymentRequest alloc] init];
request.currencyCode = #"USD";
request.countryCode = #"US";
// This is a test merchant id to demo the capability, this would work with Visa cards only.
request.merchantIdentifier = #"merchant.com.procharge"; // replace with YOUR_APPLE_MERCHANT_ID
request.applicationData = [#"" dataUsingEncoding:NSUTF8StringEncoding];
request.merchantCapabilities = PKMerchantCapability3DS;
request.supportedNetworks = #[PKPaymentNetworkMasterCard, PKPaymentNetworkVisa, PKPaymentNetworkAmex];
request.requiredBillingAddressFields = PKAddressFieldPostalAddress|PKAddressFieldPhone|PKAddressFieldEmail;
request.requiredShippingAddressFields = PKAddressFieldPostalAddress|PKAddressFieldPhone|PKAddressFieldEmail;
///Set amount here
NSString *amountText = #"0.01"; // Get the payment amount
NSDecimalNumber *amountValue = [NSDecimalNumber decimalNumberWithString:amountText];
PKPaymentSummaryItem *item = [[PKPaymentSummaryItem alloc] init];
item.amount = amountValue;
//item.amount = [[NSDecimalNumber alloc] initWithInt:20];
item.label = #"Test Payment Total";
request.paymentSummaryItems = #[item];
PKPaymentAuthorizationViewController *vc = nil;
// need to setup correct entitlement to make the view to show
#try
{
vc = [[PKPaymentAuthorizationViewController alloc] initWithPaymentRequest:request];
}
#catch (NSException *e)
{
NSLog(#"Exception %#", e);
}
if (vc != nil)
{
vc.delegate = self;
[self presentViewController:vc animated:YES completion:CompletionBlock];
}
else
{
//The device cannot make payments. Please make sure Passbook has valid Credit Card added.
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"PassKit Payment Error"
message:NSLocalizedString(#"The device cannot make payment at this time. Please check Passbook has Valid Credit Card and Payment Request has Valid Currency & Apple MerchantID.", #"")
delegate:nil
cancelButtonTitle:NSLocalizedString(#"OK", #"")
otherButtonTitles:nil];
[alert show];
}
Thanks & Regards
Did you implement these methods to check before trying to present the viewcontroller?
// Determine whether this device can process payment requests.
// YES if the device is generally capable of making in-app payments.
// NO if the device cannot make in-app payments or if the user is restricted from authorizing payments.
+ (BOOL)canMakePayments;
// Determine whether this device can process payment requests using specific payment network brands.
// Your application should confirm that the user can make payments before attempting to authorize a payment.
// Your application may also want to alter its appearance or behavior when the user is not allowed
// to make payments.
// YES if the user can authorize payments on this device using one of the payment networks supported
// by the merchant.
// NO if the user cannot authorize payments on these networks or if the user is restricted from
// authorizing payments.
+ (BOOL)canMakePaymentsUsingNetworks:(NSArray *)supportedNetworks;
I'm using parse.com's api and trying to link a currently active user to his to facebook account.
I keep bumbing into:
Another user is already linked to this facebook id
As to my question: How do I get the user's connected facebook id? I'd like to switch my current anonymous user with the active and logged in user.
Thank you
I solved this by logging out the current user then logging in. Works well.
[PFFacebookUtils linkUser:[PFUser currentUser] permissions:permissionsArray block:^(BOOL success, NSError *error) {
if (!success) {
if (!error) {
NSLog(#"User Cancelled");
} else {
if(error.code == 208) {
[PFUser logOut];
[PFFacebookUtils logInWithPermissions:permissionsArray block:^(PFUser *user, NSError *error) {
if(error) {
NSLog(#"An error occurred: %#", error);
} else {
// Carry on
}
}];
} else {
NSLog(#"An error occurred: %#", error);
}
}
} else {
// Carry on
}
}];
You can ignore it as it's a warning to notify you that the Facebook account was already linked(In your case it's to the anonymous user), not an error.
The last assigned user will be linked to the Facebook account. So your original intention - " To link the currently active user to his to facebook account" works even though a warning will be raised to notify you that the anonymous user was linked to the same facebook account.
To avoid this warning, always logout the anonymous user before linking Facebook user with the currently active user.
I faced same issue and solution was even better, just ignore this error. It is kind of warning. It is thrown but user gets linked to another account.
Check if you are asking for permissions when you try to link like this:
[PFFacebookUtils linkUser:[PFUser currentUser] permissions:[NSArray arrayWithObjects:#"publish_stream",#"publish_actions", #"user_about_me", #"user_photos", #"read_stream",#"friends_photos",#"email",#"user_birthday",#"user_location", nil] block:^(BOOL succeeded, NSError *error) {
if (succeeded) {
[self login:nil];
NSLog(#"Woohoo, user logged in with Facebook!");
}else{
NSLog(#"%#", [[error userInfo] objectForKey:#"error"]);
NSLog(#"An error occurred: %#", error);
}
}];
In my case i was passing nil param for permissions. May be it will be helpfull for someone else
Has anyone had success using Beeblex to validate in-app purchases? I have an app that is already live in the app-store with in-app purchases enabled and working. In light of the recent hacking news concerning in-app purchases I am attempting to update it to use Beeblex to validate purchases. The problem is that Beeblex always returns a result saying that Apple claims the purchase to be invalid. I am hoping someone on here is successfully using Beeblex and can post a working example. I am following their advice to let the purchase go completely through, then validate it. I basically just cut and pasted their code right off their website, so not sure what I could do differently.
- (void) validatePurchaseOrRestore
{
//The transaction has been reported as complete for a new purchase of the upgrade. I now make use of Beeblex
//to verify the receipt to ensure the purchase is legit.
if (![BBXIAPTransaction canValidateTransactions])
{
transactionResult = 4;
return; // There is no connectivity to reach the server.
// You should try the validation at a later date.
}
BBXIAPTransaction *bbxTransaction = [[BBXIAPTransaction alloc] initWithTransaction:transactionCopy];
bbxTransaction.useSandbox;
[bbxTransaction validateWithCompletionBlock:^(NSError *error)
{
if (bbxTransaction.transactionVerified)
{
if (bbxTransaction.transactionIsDuplicate)
{
// The transaction is valid, but duplicate - it has already been
// sent to Beeblex in the past.
transactionResult = 1;
}
else
{
// The transaction has been successfully validated
// and is unique.
NSLog(#"Transaction data: %#", bbxTransaction.validatedTransactionData);
transactionResult = 0;
}
}
else
{
// Check whether this is a validation error, or if something
// went wrong with Beeblex.
if (bbxTransaction.hasServerError)
{
// The error was not caused by a problem with the data, but is
// most likely due to some transient networking issues.
transactionResult = 4;
}
else
{
// The transaction supplied to the validation service was not valid according to Apple.
transactionResult = 3;
purchaseDidFail = TRUE;
}
}
}];
}