Using -Werror to treat all warning as errors, I don't want to suppress a deprecated declaration warning :
#pragma clang diagnostic push
#pragma clang diagnostic ignore "-Wdeprecated-declarations"
SKPayment *myPayment = [SKPayment paymentWithProductIdentifier:completeName];
[[SKPaymentQueue defaultQueue] addPayment:myPayment];
#pragma clang diagnostic pop
How to do it ?
Ok, found it, just use warning instead of ignore :
#pragma clang diagnostic push
#pragma clang diagnostic warning "-Wdeprecated-declarations"
SKPayment *myPayment = [SKPayment paymentWithProductIdentifier:completeName];
[[SKPaymentQueue defaultQueue] addPayment:myPayment];
#pragma clang diagnostic pop
Now, I still use this deprecated API, compilation passes with -Werror and warning is still present (remove this API use is kept in mind).
Related
When using Xcode 9, there are some compiler warnings saying This function declaration is not a prototype. It suggests to add void to the method body, which will resolve it. The issue I am having is that those warnings are also thrown for system-API's like UIApplication delegate-methods:
- (void)application:(UIApplication *)application
handleActionWithIdentifier:(NSString *)identifier
forRemoteNotification:(NSDictionary *)userInfo
withResponseInfo:(NSDictionary *)responseInfo
completionHandler:(void (^)())completionHandler
This could be resolved by the following:
- (void)application:(UIApplication *)application
handleActionWithIdentifier:(NSString *)identifier
forRemoteNotification:(NSDictionary *)userInfo
withResponseInfo:(NSDictionary *)responseInfo
completionHandler:(void (^)(void))completionHandler
Now I am wondering if the delegate methods will still work on the long-term or Apple will insert the void in later iOS 11 Beta versions. I am curious because if I include the void body, Xcode will complain about mismatching method-selectors (which makes sense). Did someone experience the same issue so far?
The block declaration with empty parenthesis:
void (^)()
has the same semantics as a function pointer with empty parenthesis:
void (*)()
It does not mean that there are no arguments. It means the arguments are not specified, therefore it opens the way to bugs since you can call it in the following ways:
void (^block)() = ...
block();
block(10);
block(#"myString");
When declaring blocks with no parameters, always use:
void (^)(void)
Apple was not doing that correctly everywhere and they are not probably fixing that for old APIs for compatibility reasons. You will have to keep that warning there until you move to the newer API.
You can also turn off that warning (-Wstrict-prototypes):
or using #pragma (thanks #davidisdk):
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wstrict-prototypes"
- (void)application:(UIApplication *)application
handleActionWithIdentifier:(NSString *)identifier
forRemoteNotification:(NSDictionary *)userInfo
withResponseInfo:(NSDictionary *)responseInfo
completionHandler:(void (^)())completionHandler {
}
#pragma clang diagnostic pop
See the LLVM discussion here or the bug on openradar.
Note that's there was no change in the internal working of the APIs, all code will still work. We will only know that the API is not as good as it should be.
I have joined in a old project and I found this line
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
signal(SIGPIPE, SIG_IGN);
....
}
I have found in docs this:
/*
* For historical reasons; programs expect signal's return value to be
* defined by <sys/signal.h>.
*/
But I'm still confused as to what the purpose of that line is.
From Apple's documentation:
When a connection closes, by default, your process receives a SIGPIPE signal. If your program does not handle or ignore this signal, your program will quit immediately.
Ignore the signal globally with the following line of code:
signal(SIGPIPE, SIG_IGN);
I'm struggling with if I need to add a Transaction observer and also if I need to remove the transaction observer and where and what all this is ...
My inherited code includes the app delegate w/ an observer...
AppDelegate ->
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
SKPaymentQueue *paymentQueue = [self.injector getInstance:[SKPaymentQueue class]];
[paymentQueue addTransactionObserver:self.purchaseHelper];
}
and I'm working on the restore part of the purchase helper...
PurchaseHelper ->
-(void) beginRestorePurchases:(BOOL)serverRestore {
self.serverRestore = serverRestore;
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
-(void) paymentQueueRestoreCompletedTransactionsFinished : (SKPaymentQueue *) queue {
for (SKPaymentTransaction *transaction in queue.transactions) {
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
}
My concern at the moment is with Restoring purchases. ( I realize that i'm using a dependency injection pattern for the other portion(the purchasing part) of the code so it different. I'm not sure if this is having an effect on my problem as well.)
The problem is that I have no idea if I should be adding another observer for restoring or not. If I do I get some weird behavior with iTunes prompting multiple times upon restoring. In SO posts I see some mention of removing the observer. When would you do this if I'm creating it on the app delegate level?
Perhaps the my SKPaymentQueue * paymentQueue object is not a defaultQueue object???? No idea....
You always need to add your transaction observer, and early in the app lifecycle. Actions outside your control can cause transactions to fail to complete and the OS level queue will re-feed these to your app. I can't think of any common, legitimate reason to remove the observer.
You only need one observer, and that observer should be the gatekeeper to handling all the IAP events.
You also need to make certain that you're calling finish on each legitimately completed transaction. If you don't, you can wind up stacking lots of duplicated (from the SKU perspective) transactions on top of each other.
iam using this code on click on the item after init the buy process
SKPayment *payment = [SKPayment paymentWithProduct:self.skDollar_15] ;
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue] addPayment:payment] ;
it works fine in the first time but if i out of the screen and comeback again and try to perform this code again the application crash in this line
[[SKPaymentQueue defaultQueue] addPayment:payment] ;
The document say:
The payment request must have a product identifier registered with the
Apple App Store and a quantity greater than 0. If either property is
invalid, addPayment: throws an exception.
quantity is default to one, so check if your product identifier is valid not nil.
I got so far: After a reinstall, a user needs to click "buy feature", then he gets scared with the $0.99 question, then has to login and then gets told the feature is already bought and he gets it for free.
I know apple is a religion and users are strong believers, but isn't there a better way? :-) What I want is to check for the feature without actually buying it. Letting the user enter his account info seems to be neccessary, maybe buy a $0.00 feature? or is there a method somewhere that does this?
I'm using MKStoreKit for the whole In-App-Purchase, but any solution would be great.
UPDATE
thanx to darvids0n, your method solved my problem! here's some working code for others trying the same:
- (void)removePreviousPurchases { //just for sandbox testing
[[MKStoreManager sharedManager] removeAllKeychainData];
}
- (void)restorePreviousPurchases { //needs account info to be entered
if([SKPaymentQueue canMakePayments]) {
[[MKStoreManager sharedManager] restorePreviousTransactionsOnComplete:^(void) {
NSLog(#"Restored.");
/* update views, etc. */
}
onError:^(NSError *error) {
NSLog(#"Restore failed: %#", [error localizedDescription]);
/* update views, etc. */
}];
}
else
{
NSLog(#"Parental control enabled");
/* show parental control warning */
}
}
If the $0.99 item is non-consumable, then you should provide a "Restore Purchases" button (or similar) which calls
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
Assuming you've added a transaction observer already, and implemented the protocol including a case to handle a restored transaction (with state SKPaymentTransactionStateRestored) this will work.
Add these two methods :
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue]restoreCompletedTransactions];