I'm trying to implement In App Purchase in my app. The type is Non-Consumable.
The produtct request reuturns fine..with correct title, description and price. But when I click to buy, open the alertView saying "confirm....Environment:Sandbox" when I click in confirm, should appears the apple box requesting the password from user test..but It doesn't appears and the tansaction failed without message error... My app isn't in AppStore yet and the In App Purchase Product in Itunes Connect has the status Ready to Submit It's correct?
Here is My code: (Sorry for longer code, but I mean that its relevant and important for my question)
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
NSArray *products = response.products;
proUpgradeProduct = [products count] == 1 ? [[products firstObject] retain] : nil;
if (proUpgradeProduct)
{
NSLog(#"Product title: %#" , proUpgradeProduct.localizedTitle);
NSLog(#"Product description: %#" , proUpgradeProduct.localizedDescription);
NSLog(#"Product price: %#" , proUpgradeProduct.price);
NSLog(#"Product id: %#" , proUpgradeProduct.productIdentifier);
// lblTitulo.text = proUpgradeProduct.productIdentifier;
// lblDescricao.text = proUpgradeProduct.localizedDescription;
}
for (NSString *invalidProductId in response.invalidProductIdentifiers)
{
NSLog(#"Invalid product id: %#" , invalidProductId);
}
// finally release the reqest we alloc/init’ed in requestProUpgradeProductData
[productsRequest release];
[[NSNotificationCenter defaultCenter] postNotificationName:kInAppPurchaseManagerProductsFetchedNotification object:self userInfo:nil];
}
- (void)requestProUpgradeProductData
{
NSSet *productIdentifiers = [NSSet setWithObject:#"versaopro" ];
productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];
productsRequest.delegate = self;
[productsRequest start];
// we will release the request object in the delegate callback
}
- (void)loadStore
{
// restarts any purchases if they were interrupted last time the app was open
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
// get the product description (defined in early sections)
[self requestProUpgradeProductData];
}
- (void)purchaseProUpgrade
{
SKPayment *payment = [SKPayment paymentWithProduct:proUpgradeProduct];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
-(IBAction) buyClick: (id) sender {
[self purchaseProUpgrade];
}
Related
I try to figure out how to handle the data update from the cloud.
In Swift sample application all work "from the box", but in Obj-C I can't update the interface when the application gets updated data from iCloud.
Below my code for instantiating container and context:
#synthesize persistentContainer = _persistentContainer;
- (NSPersistentCloudKitContainer *)persistentContainer {
// The persistent container for the application. This implementation creates and returns a container, having loaded the store for the application to it.
#synchronized (self) {
if (_persistentContainer == nil) {
_persistentContainer = [[NSPersistentCloudKitContainer alloc] initWithName:#"SampleCloudObjC"];
NSPersistentStoreDescription *description = [_persistentContainer.persistentStoreDescriptions firstObject];
[description setOption:#(true) forKey:NSPersistentStoreRemoteChangeNotificationPostOptionKey];
[_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) {
if (error != nil) {
NSLog(#"Unresolved error %#, %#", error, error.userInfo);
abort();
}
}];
}
}
return _persistentContainer;
}
- (void)saveContext {
NSManagedObjectContext *context = self.persistentContainer.viewContext;
context.automaticallyMergesChangesFromParent = YES;
NSError *error = nil;
if ([context hasChanges] && ![context save:&error]) {
NSLog(#"Unresolved error %#, %#", error, error.userInfo);
abort();
}
}
And here I try to handle notification:
- (void)viewDidLoad {
[super viewDidLoad];
taskList = [Task fetchAll];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(updateTable)
name:NSPersistentStoreRemoteChangeNotificationPostOptionKey
object:nil];
}
- (IBAction)updateTable {
taskList = [Task fetchAll];
[self.tableView reloadData];
}
As I understood key thing was to make followed changes in code:
AppDelegate:
...
NSPersistentStoreDescription *description = [_persistentContainer.persistentStoreDescriptions firstObject];
[description setOption:#(true) forKey:NSPersistentStoreRemoteChangeNotificationPostOptionKey];
[_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) {
...
Controller:
...
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(remoteHandler:)
name:NSPersistentStoreRemoteChangeNotification
object:nil];
...
Now UI is updating but looks like only after the application goes to the background and back to active.
i using this code in iOS 8 for security and uses touch ID
- (IBAction)authenticateButtonTapped{
LAContext *myContext = [[LAContext alloc] init];
NSError *authError = nil;
NSString *myLocalizedReasonString = #"Authenticate using your finger\r Scan Your Finger Now";
if ([myContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&authError]) {
[myContext evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
localizedReason:myLocalizedReasonString
reply:^(BOOL succes, NSError *error) {
if (succes) {
[self showMessage:#"Authentication is successful" withTitle:#"Success"];
NSLog(#"User authenticated");
} else {
switch (error.code) {
case LAErrorAuthenticationFailed:
[self showMessage:#"Authentication is failed" withTitle:#"Error"];
NSLog(#"Authentication Failed");
break;
case LAErrorUserCancel:
[self showMessage:#"You clicked on Cancel" withTitle:#"Error"];
NSLog(#"User pressed Cancel button");
break;
case LAErrorUserFallback:
[self showMessage:#"You clicked on \"Enter Password\"" withTitle:#"Error"];
NSLog(#"User pressed \"Enter Password\"");
[self copyMatchingAsync];
break;
default:
[self showMessage:#"Touch ID is not configured" withTitle:#"Error"];
NSLog(#"Touch ID is not configured");
break;
}
NSLog(#"Authentication Fails");
}
}];
} else {
NSLog(#"Can not evaluate Touch ID");
[self showMessage:#"Can not evaluate TouchID" withTitle:#"Error"];
}
}
after for use the passcode system i copy this code from apple example
- (void)copyMatchingAsync
{
NSDictionary *query = #{
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrService: #"SampleService",
(__bridge id)kSecReturnData: #YES,
(__bridge id)kSecUseOperationPrompt: NSLocalizedString(#"AUTHENTICATE_TO_ACCESS_SERVICE_PASSWORD", nil)
};
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
CFTypeRef dataTypeRef = NULL;
NSString *msg;
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)(query), &dataTypeRef);
if (status == errSecSuccess)
{
NSData *resultData = ( __bridge_transfer NSData *)dataTypeRef;
NSString * result = [[NSString alloc] initWithData:resultData encoding:NSUTF8StringEncoding];
msg = [NSString stringWithFormat:NSLocalizedString(#"RESULT", nil), result];
} else {
}
});
}
-(void) showMessage:(NSString*)message withTitle:(NSString *)title
{
UIAlertController * alert= [UIAlertController
alertControllerWithTitle:title
message:message
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* cancel = [UIAlertAction
actionWithTitle:#"OK"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action)
{
[alert dismissViewControllerAnimated:YES completion:nil];
}];
[alert addAction:cancel];
[self presentViewController:alert animated:YES completion:nil];
}
its work great and rapid for fingerprint but the passcode system can't show and dosen't work. and i received "ERROR_ITEM_NOT_FOUND" = "error item not found";
this apple link https://developer.apple.com/library/ios/samplecode/KeychainTouchID/Introduction/Intro.html
but i can't good understand
Sorry to tell you but you can't do that.
You are mixing access control with keychain access, You don't have access to the user passcode.
using SecItemCopyMatching is possible if you added a resource with SecItemAdd using the same attributes (the contents of "query")
"ERROR_ITEM_NOT_FOUND" = "error item not found" show no item in keychain.
I also saw the Apple sample code "KeychainTouchID" as you said above.
iOS8's new feature make the developer can use the iPhone user's passcode & Touch ID for authentication.
You have to "Add Item" firstly, and then call "Query for Item".
If you want more convenient solution, maybe you can use SmileTouchID.
It is a library for integrate Touch ID & Passcode to iOS App conveniently that even support for iOS7 and the device that cannot support Touch ID.
You just need a few line of code and get what you want.
if ([SmileAuthenticator hasPassword]) {
[SmileAuthenticator sharedInstance].securityType = INPUT_TOUCHID;
[[SmileAuthenticator sharedInstance] presentAuthViewController];
}
AppStore always returns a response 'invalid identifier'.
I wrote following code, and give product identifier as argument, productId.
- (void)purchase:(NSString *)productId callbackURL:(NSString *)callbackURL {
if (![SKPaymentQueue canMakePayments]) {
DDLogWarn(#"couldn't make purchase");
return;
}
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
NSSet *productIds = [NSSet setWithObject:productId];
SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:productIds];
request.delegate = self;
[request start];
}
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
if (!response) {
DDLogError(#"Product Response is nil");
return;
}
[response.invalidProductIdentifiers bk_each:^(id obj) {
DDLogWarn(#"%# is a invalid identifier", obj);
}];
[response.products bk_each:^(id obj) {
SKProduct *product = obj;
DDLogDebug(#"%# is a valid product", product.localizedTitle);
SKPayment *payment = [SKPayment paymentWithProduct:product];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}];
}
However, always receive a response, invalid identifier.
I saw http://troybrant.net/blog/2010/01/invalid-product-ids/ , and check my code, device and iTC and provisioning profiles.
My application's state is waiting for upload, inapp items are yellow signals.
Registered application bundle identifier and Xcode project's are same string.
Why..!? Thanks.
All my code works perfectly in the simulator. The contact picker is displayed and when a contact is pressed, it is dismissed having obtained the contact data. However, when I try this on my device, the picker is not dismissed and displays the details of the selected contact instead. From there you can press the individual properties such as numbers and addresses, but those just transfer you to the related app.
The button the user pressed to import a contact:
- (IBAction)AddContactPressed:(id)sender {
ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions(NULL, NULL);
if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined) {
ABAddressBookRequestAccessWithCompletion(addressBookRef, ^(bool granted, CFErrorRef error) {
// First time access has been granted, add the contact
contactsAccessible = TRUE;
});
}
else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized) {
// The user has previously given access, add the contact
contactsAccessible = TRUE;
}
else {
// The user has previously denied access
// Send an alert telling user to change privacy setting in settings app
}
//Test if contacts have been enabled or not
if (contactsAccessible) {
//Display contact selection screen
addressBookController = [[ABPeoplePickerNavigationController alloc] init];
addressBookController.peoplePickerDelegate = self;
[self presentViewController:addressBookController animated:YES completion:nil];
}
else{
//Display text saying the contacts could not be accessed and provide a button to ask again
}
}
Here is all my code for the contact picker:
-(BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person{
//Creating dictionary to store contact info
NSMutableDictionary *contactInfoDict = [[NSMutableDictionary alloc]
initWithObjects:#[#"", #"", #"", #""]
forKeys:#[#"firstName", #"lastName", #"mobileNumber", #"homeNumber"]];
//First name
CFTypeRef generalCFObject;
generalCFObject = ABRecordCopyValue(person, kABPersonFirstNameProperty);
if (generalCFObject) {
[contactInfoDict setObject:(__bridge_transfer NSString *)generalCFObject forKey:#"firstName"];
CFRelease(generalCFObject);
}
//Last name
generalCFObject = ABRecordCopyValue(person, kABPersonLastNameProperty);
if (generalCFObject) {
[contactInfoDict setObject:(__bridge_transfer NSString *)generalCFObject forKey:#"lastName"];
CFRelease(generalCFObject);
}
//Phone numbers: (home and mobile)
ABMultiValueRef phonesRef = ABRecordCopyValue(person, kABPersonPhoneProperty);
for (int i=0; i < ABMultiValueGetCount(phonesRef); i++) {
CFStringRef currentPhoneLabel = ABMultiValueCopyLabelAtIndex(phonesRef, i);
CFStringRef currentPhoneValue = ABMultiValueCopyValueAtIndex(phonesRef, i);
if (CFStringCompare(currentPhoneLabel, kABPersonPhoneMobileLabel, 0) == kCFCompareEqualTo) {
[contactInfoDict setObject:(__bridge_transfer NSString *)currentPhoneValue forKey:#"mobileNumber"];
}
if (CFStringCompare(currentPhoneLabel, kABHomeLabel, 0) == kCFCompareEqualTo) {
[contactInfoDict setObject:(__bridge_transfer NSString *)currentPhoneValue forKey:#"homeNumber"];
}
CFRelease(currentPhoneLabel);
CFRelease(currentPhoneValue);
}
CFRelease(phonesRef);
//Getting image if contact has image
if (ABPersonHasImageData(person)) {
NSData *contactImageData = (__bridge_transfer NSData *)ABPersonCopyImageDataWithFormat(person, kABPersonImageFormatThumbnail);
[contactInfoDict setObject:contactImageData forKey:#"image"];
}
//Add contact to array
if (contacts == nil) {
contacts = [[NSMutableArray alloc] init];
}
[contacts addObject:contactInfoDict];
//Save contact
[userDefaults setObject:contacts forKey:#"Contacts"];
[self dismissViewControllerAnimated:YES completion:nil];
return NO;
}
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier{
return NO;
}
-(void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker{
[self dismissViewControllerAnimated:YES completion:nil];
}
The reason could be that the delegate couldn't call the delegate method. This could cause that you use different iOS on simulator and on iPhone.
My best guess that you use iOS 8 on simulator and under iOS 8 there is a new delegate method for the picker
- (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person {
....// do whatever you need here
meantime the old one is what you are using.
From the documentation:
peoplePickerNavigationController:shouldContinueAfterSelectingPerson:property:identifier:
Return Value
YES to perform the action for the property selected and dismiss the picker.
NO to show the person in the picker.
So this is probably the issue. On simulator first method gets called while on the device the other. So you should handle picking in both methods and return YES from the one described above.
When I use the webdialog for a friendrequest, everything is going fine, except no request or anything is made.
The code:
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithObjectsAndKeys:
facebookFriend.id, #"to",
nil];
[FBWebDialogs presentRequestsDialogModallyWithSession:FBSession.activeSession
message:NSLocalizedString(#"FB_FRIEND_INVITE_MESSAGE", #"Facebook friend invite message")
title:NSLocalizedString(#"FB_FRIEND_INVITE_TITLE", #"Facebook friend invite title")
parameters:params
handler:^(FBWebDialogResult result, NSURL *resultURL, NSError *error) {
}];
This is the result I get:
fbconnect://success?request=xxxxxxxxxxxx&to%5B0%5D=xxxxxxxx
How can I debug what is going wrong?
Thanks in advance.
Ruud
For SDK 3.2 or above we have a facility to use FBWebDialogs class that will help us to show a popup along with the friend list and pick one or more from list to send invitation.
Lets do it step by step:
1) Download and setup SDK 3.2 or above.
2) First setup your application on facebook by following this url.
3) Then use the attached code.
Sample Code: (It generates invite friend request)
-(void)inviteFriends
{
if ([[FBSession activeSession] isOpen])
{
NSMutableDictionary* params = [NSMutableDictionary dictionaryWithObjectsAndKeys:nil];
[FBWebDialogs presentRequestsDialogModallyWithSession:nil
message:[self getInviteFriendMessage]
title:nil
parameters:params
handler:^(FBWebDialogResult result, NSURL *resultURL, NSError *error)
{
if (error)
{
[self requestFailedWithError:error];
}
else
{
if (result == FBWebDialogResultDialogNotCompleted)
{
[self requestFailedWithError:nil];
}
else if([[resultURL description] hasPrefix:#"fbconnect://success?request="])
{
// Facebook returns FBWebDialogResultDialogCompleted even user
// presses "Cancel" button, so we differentiate it on the basis of
// url value, since it returns "Request" when we ACTUALLY
// completes Dialog
[self requestSucceeded];
}
else
{
// User Cancelled the dialog
[self requestFailedWithError:nil];
}
}
}
];
}
else
{
/*
* open a new session with publish permission
*/
[FBSession openActiveSessionWithPublishPermissions:[NSArray arrayWithObject:#"publish_stream"]
defaultAudience:FBSessionDefaultAudienceFriends
allowLoginUI:YES
completionHandler:^(FBSession *session, FBSessionState status, NSError *error)
{
if (!error && status == FBSessionStateOpen)
{
NSMutableDictionary* params = [NSMutableDictionary dictionaryWithObjectsAndKeys:nil];
[FBWebDialogs presentRequestsDialogModallyWithSession:nil
message:[self getInviteFriendMessage]
title:nil
parameters:params
handler:^(FBWebDialogResult result, NSURL *resultURL, NSError *error)
{
if (error)
{
[self requestFailedWithError:error];
}
else
{
if (result == FBWebDialogResultDialogNotCompleted)
{
[self requestFailedWithError:nil];
}
else if([[resultURL description] hasPrefix:#"fbconnect://success?request="])
{
// Facebook returns FBWebDialogResultDialogCompleted even user
// presses "Cancel" button, so we differentiate it on the basis of
// url value, since it returns "Request" when we ACTUALLY
// completes Dialog
[self requestSucceeded];
}
else
{
// User Cancelled the dialog
[self requestFailedWithError:nil];
}
}
}];
}
else
{
[self requestFailedWithError:error];
}
}];
}
}
and here are the helper functions that calls delegates function OnFBSuccess and OnFBFailed.
- (void)requestSucceeded
{
NSLog(#"requestSucceeded");
id owner = [fbDelegate class];
SEL selector = NSSelectorFromString(#"OnFBSuccess");
NSMethodSignature *sig = [owner instanceMethodSignatureForSelector:selector];
_callback = [NSInvocation invocationWithMethodSignature:sig];
[_callback setTarget:owner];
[_callback setSelector:selector];
[_callback retain];
[_callback invokeWithTarget:fbDelegate];
}
- (void)requestFailedWithError:(NSError *)error
{
NSLog(#"requestFailed");
id owner = [fbDelegate class];
SEL selector = NSSelectorFromString(#"OnFBFailed:");
NSMethodSignature *sig = [owner instanceMethodSignatureForSelector:selector];
_callback = [NSInvocation invocationWithMethodSignature:sig];
[_callback setTarget:owner];
[_callback setSelector:selector];
[_callback setArgument:&error atIndex:2];
[_callback retain];
[_callback invokeWithTarget:fbDelegate];
}
So the class taht calls method InviteFriend MUST have these functions:
-(void)OnFBSuccess
{
CCLOG(#"successful");
// do stuff here
[login release];
}
-(void)OnFBFailed:(NSError *)error
{
if(error == nil)
CCLOG(#"user cancelled");
else
CCLOG(#"failed");
// do stuff here
[login release];
}
Recommended Reads:
Send Invitation via Facebook
API Permissions
An Example
NOTE:
1) Don't forget to setup Facebook application ID in plist.
2) Don't forget to adjust AppDelegate to handle urls.
Partial snippet taken from above link in point 2:
/*
* If we have a valid session at the time of openURL call, we handle
* Facebook transitions by passing the url argument to handleOpenURL
*/
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation {
// attempt to extract a token from the url
return [FBSession.activeSession handleOpenURL:url];
}
Hope it helps!
EDIT
Here:
declaration for fbDelegate is:
#property (nonatomic, assign) id <FBLoginDelegate> fbDelegate;
#protocol FBLoginDelegate <NSObject>
#required
-(void) OnFBSuccess;
-(void) OnFBFailed : (NSError *)error;
#end
and this is how you can consume this code:
FBLoginHandler *login = [[FBLoginHandler alloc] initWithDelegate:self]; // here 'self' is the fbDelegate you have asked about
[login inviteFriends];
I think your application is not enable for Android and for web . And you are trying to get notification on web or on Android device.
Points : For getting notification on Android or on web you have to enable your app for Android and web too .
To Enable Android and Web on your App : GoTo your App > Setting > Click on + Add platform add enter necessary information and Save .
Lets Enjoy Notification . :-)