Check Auto-renewable InApp Purchase iOS - ios8

I am implementing Auto-renewable InApp Purchase for iOS. It is working fine for first month. But I am unable to know whether user's subscription is auto renewed or not means what is the status of current InApp Purchase.
After studying so many answers on StackOverflow, I am able to understand that I have to provide Restore button to Restore transaction. I have implemented it as well and it is giving my all transactions till date. So, I am unable to get my last transaction. So, please help to understand the status of last auto renewed transaction so that I shall be able to update the status on my server.
Below is the code I am using to restore my transaction:
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue]restoreCompletedTransactions];
and implementing its delegates as follows:
- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue {
NSLog(#"%#",queue );
NSLog(#"Restored Transactions are once again in Queue for purchasing %#",[queue transactions]);
NSMutableArray *purchasedItemIDs = [[NSMutableArray alloc] init];
NSLog(#"received restored transactions: %zd", queue.transactions.count);
for (SKPaymentTransaction *transaction in queue.transactions)
{
NSString *productID = transaction.payment.productIdentifier;
[purchasedItemIDs addObject:productID];
NSLog (#"product id is %#" , productID.description);
NSLog (#"observationInfo is %#" , transaction.payment.productIdentifier.observationInfo);
// here put an if/then statement to write files based on previously purchased items
// example if ([productID isEqualToString: #"youruniqueproductidentifier]){write files} else { nslog sorry}
}
}
- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error
{
NSLog(#"Restore Error... %#",error.description);
}

I guess transaction restore is not work if application is running and Transaction is renew. Please refer StoreKit Guide :-
After a subscription is successfully renewed, Store Kit adds a
transaction for the renewal to the transaction queue. Your app checks
the transaction queue on launch and handles the renewal the same way
as any other transaction. Note that if your app is already running
when the subscription renews, the transaction observer is not called;
your app finds out about the renewal the next time it’s launched.
After relaunch application, Transaction restore functionality work fine with restoring current auto-renewed transaction.
On Alternate, If you want to check transaction is renewed or not without relaunch application, You can do with manually refreshing "App Receipt".
Link (Expiration and Renewal) :- https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Chapters/Subscriptions.html

Related

StoreKit multiple transactions

I'm stucked solving problem with In-App Purchases using StoreKit framework for Mac OS. The problem is this:
1) I call addPayment method when purchase started
2) Then updatedTransactions method called twice with transaction state SKPaymentTransactionStatePurchasing. And after second call [[SKPaymentQueue defaultQueue].transactions count] returns 2.
3) When purchase completed updatedTransactions called third time with transaction state SKPaymentTransactionStatePurchased. And [[SKPaymentQueue defaultQueue].transactions count] returns 3.
4) Then I finishTransaction with state SKPaymentTransactionStatePurchased. But in defaultQueue still remain two transactions with SKPaymentTransactionStatePurchasing. They alive till the app terminates. When I launch app again the problem repeats.
Why so many transactions for only one addPayment call? And how this problem can be solved?
Make sure to call finishTransaction method after any of the below transaction state :
1) SKPaymentTransactionStatePurchased
2) SKPaymentTransactionStateFailed
3) SKPaymentTransactionStateRestored
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
Calling finishTransaction: on a transaction removes it from the queue.

Why do I sometimes get mangled replies with concurrent NSURLSession requests

I am working on an OS X (Yosemite) app which downloads two types of csv data (call them type A and type B) from the internet asynchronously using the NSURLSession API .There are multiple requests for each type of csv. Each request is it's own dedicated session wrapped in a custom class. There is one base request class with a subclass for each type. (In hindsight maybe not an ideal design but irrelevant for my issue I think).
The app is constructed such that each type of csv data is downloaded in a sequential queue. Only one request of each type can be active at a time but both types can occur simultaneously and both use the main thread for delegate callbacks. All of this works fine usually.
The issue I am seeing is that sometimes with heavy traffic I get "cross hearing", i.e. I sometimes get a response back to a type B request that is reported as completed successfully but it contains a number of type B cvs lines and then some type A lines tagged on after - so I sometimes (rarely) get type A data in my type B requests. (or the other way around).
Basically it look like the "switching" logic in Apples API gets confused about which incoming packet belongs to what request/session. The two different request types goes to different URLs but they are related and it may be that they both in the end resolve to the same IP, I am not sure about that. I wonder if there may be something related to the packet headers if they come from the same server that makes it difficult to determine what request they belong to (I'm not good enough at the internet protocols to know if this is a sensible guess). If that is the case then the solution must be to ensure all requests are in one queue so that they cannot be active simultaneously, but I do not want to do that large architecture change before I am confident there is no other workaround.
I looked for similar questions and found this old question (Why is my data getting corrupted when I send requests asynchronously in objective c for iOS?) which appears to describe the exact same issue but unfortunately it has no answer. Other than that I found nothing similar so I guess I am doing something stupid here but it would be good to know why this issue occurs before I start changing the architecture to fix it.
Has anyone seen this before and know what the cause and workaround is?
I did not include any code as I felt there was no point given it appears to be an architecture issue and if I added code it would need to be a lot. However I will be happy to add whatever you suggest if that helps understand the question.
Edit:
The relevant (I hope) code added below. Note objects are one shot only. The parameters for the request are injected by the init method and the NSURLSession is used for a single task only. Hence the session is invalidated after launch and the NSMutableData array released after parsing of the data.
-(BOOL)executeRequest {
NSURLSessionConfiguration *theConfig = [NSURLSessionConfiguration ephemeralSessionConfiguration];
NSURLSession *theSession = [NSURLSession sessionWithConfiguration:theConfig delegate:self delegateQueue:[NSOperationQueue mainQueue]];
NSURLRequest *theRequest = [NSURLRequest requestWithURL:self.queryURL cachePolicy: NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:BSTTIMEOUT];
NSURLSessionDataTask *theTask = [theSession dataTaskWithRequest:theRequest];
if(!theTask) {
return NO;
}
[theTask resume];
[theSession finishTasksAndInvalidate];
self.internetData = [NSMutableData dataWithCapacity:0];
return YES;
}
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
[self.internetData appendData:data];
return;
}
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
if((error)||(![self parseData]))
{
self.internetData = nil;
if(!error) {
NSDictionary *errorDictionary = #{ NSLocalizedDescriptionKey : #"Parsing of internet data failed", NSLocalizedFailureReasonErrorKey : #"Bad data was found in received buffer"};
error = [NSError errorWithDomain:NSCocoaErrorDomain code:EIO userInfo:errorDictionary];
}
NSDictionary* ui = [NSDictionary dictionaryWithObject:error forKey:#"Error"];
[[NSNotificationCenter defaultCenter] postNotificationName:[self failNotification] object:self userInfo:ui];
return;
}
[[NSNotificationCenter defaultCenter] postNotificationName:[self successNotification] object:self];
return;
}
First of all: You should not create a new session for each request. This are no sessions anymore. From the docs:
With the NSURLSession API, your app creates one or more sessions, each of which coordinates a group of related data transfer tasks. For example, if you are writing a web browser, your app might create one session per tab or window, or one session for interactive use and another session for background downloads. Within each session, your app adds a series of tasks, each of which represents a request for a specific URL (following HTTP redirects if necessary).
Second: Where do you store the session et al., so it is not deallocated?
Your main problem: Obviously you start new requests while requests are potentially running. But you have only one NSMutableData instance that receives the data in -URLSession:task:didReceiveData:: Many requests, one storage … Of course that mixes up.
I finally managed to track down my (stupid) error. For future reference the issue was caused by a failure to realise that the data coming back was not zero terminated.
Most of the data requested in my case is XML and the NSXMLParserclass wants a NSDatawithout extra trailing zeros so that works well.
But the requests which occasionally failed uses a CSV format where the data passes over a NSStringwhich is created by [NSString stringWithUTF8String] which expects a zero terminated c style string as input. This was the main culprit. Often it worked as it should. Sometimes it failed outright and sometimes it just did a buffer overrun and got some of the previous request data that was in the same memory area. These were the cases I noticed when posting the question.
Thus the solution is to switch to the use of [[NSString alloc] initWithData: encoding:NSUTF8StringEncoding] which works with non null-terminated NSDatabuffers.

Can I send remote push notification to my app from multiple servers?

I am building an iOS & Android application for a contractor.
Basically it's a client that should show a list of notifications from a server.
The contractor plan to give this app to multiple client companies of his. for each company he will ship a server (dedicated to that client), and the mobile app will be installed on ANY device, and for each client fleet the drivers will only have to enter the IP of the client server.
My question is - can I use Parse (or any other remote push aggregator) with a single app but have multiple servers send push notifications to devices (these will NOT be 'broadcast' pushes to all devices - they will be aimed to specific devices) ?
I'm not sure what the "server" in this case it, but Parse should be able to handle what you want.
I would create standard User and Installation classes in Parse, plus a custom class called "company" (or "client" or whatever). You can add whatever fields you like for this class, but it seems like the important one for you would be "IP Address".
You can associate an Installation with a User and a User with a company. The data structure would be like this:
Installation -> (Pointer)User
User -> (Pointer)company
company -> (String)ipAddress
Then, when you want to send a PUSH for a particular IP address, you could do the following:
Query for the company that matches the "IP address".
Query for all User objects that have the result from #1 as their company property.
Query for all Installation objects that have their User object in the array returned by #2.
Send the PUSH using the query in #3.
Here's a way to do it in Objective-C:
- (void)sendPushMessage:(NSString *)message
toClientWithIpAddress:(NSString *)ipAddress {
PFQuery *companyQuery = [PFQuery queryWithClassName:#"company"];
[companyQuery whereKey:#"ipAddress" equalTo:ipAddress];
[companyQuery getFirstObjectInBackgroundWithBlock:^(PFObject *company, NSError *error) {
// Do real error handling
PFQuery *userQuery = [PFUser query];
[userQuery whereKey:#"company" equalTo:company];
[userQuery findObjectsInBackgroundWithBlock:^(NSArray *users, NSError *error) {
// Do real error handling
PFQuery *installationQuery = [PFInstallation query];
[installationQuery whereKey:#"user" containedIn:users];
PFPush *push = [PFPush push];
[push setQuery:installationQuery];
[push setMessage:message];
// You could also have a completion block for this, if desired
[push sendPushInBackground];
}
];
}
];
}
Caveat: Although I wrote the code in Objective-C, and although Parse allows Client PUSH, it's recommended that you do this from a Cloud Code function.

Sinch, message shouldSendPushNotification not being called

I'm trying to implement the push functionality in Sinch.
I've set up my since client as such:
[_client setSupportMessaging:YES];
[_client setSupportPushNotifications:YES];
_client.delegate = self;
[_client start];
[_client startListeningOnActiveConnection];
Then on
- (void)clientDidStart:(id<SINClient>)client {
client.messageClient.delegate = self;
}
And in AppDelegate.h
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
// get previously initiated Sinch client
id<SINClient> client = [SinchClient sharedClient].client;
[client registerPushNotificationData:deviceToken];
}
The other delegate methods work fine (messageSent, messageDelivered, etc), however, I can't seem to invoke
(void)message:(id<SINMessage>)message shouldSendPushNotifications:(NSArray *)pushPairs
for testing, I've setup two phones each running a SinchClient, and force quit the app on the one of the phones (assuming that should trigger the offline state) - still no dice though. I'm watching a breakpoint, but the delegate doesn't seem to be firing.
Any thoughts?
Thanks,
Charlie
Make sure that both phones have registered push data (won't work on a simulator for example). Otherwise, putting one of these devices in flight mode and try to send a message to that device should suffice to get the shouldSendPushNotification callback (make sure you are signed in as different user on the two devices).
If you don't receive the messageSent callback you won't receive the shouldSendPushNotification callback. Likewise, if you receive the messageDelivered event, you won't get the shouldSendPushNotification.

Suggestions needed for architecting my code

Background
I'm writing an part of my app that has no UI. It sits in the background watching what you do and timing your work.
There should be no overlapping times, and there should be no breaks in the time data. If there are either of these things, the app has a bug somewhere and I need to be notified.
What I Want
A class called JGDataIntegrityController that does the following:
Check the data store for duplicate times. Scan since the last Duplicate Report Date stored in NSUserDefaults.
If duplicate times are found, build a report.
Send the report.
If the sending isn't successful, then exit. Otherwise continue.
Remove the duplicates
Update the last Duplicate Report Date in NSUserDefaults
Repeat the above for data breaks.
What I've Got
I've made a base class that does all the hard work of sending the report.
Class Diagram http://synapticmishap.co.uk/ReportClasses.jpg
JGReportSender has the following code:
-(void)postReport:(NSString *)report {
NSMutableDictionary *form = // Dictionary Holding Report;
NSURLRequest *request = [NSURLRequest requestWithURL:#"http://postURL" postForm:form];
[NSURLConnection connectionWithRequest:request delegate:self];
}
Where I'm Getting Stuck
What should I do when the report has been sent?
The delegate methods:
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError*)error
are called when the report has been sent. But how should I communicate with JGDataIntegrityController?
My Crap Idea
My idea is to have a reportStatus NSNumber property in JGReportSender. Then when the delegate methods get called, this is updated.
reportStatus = 1 means "report sent OK".
reportStatus = 2 means "problem sending report".
Then I could add an observer for reportStatus for JGDataDuplicateReportSender and JGDataBreakReportSender. This would then handle the report sending error or continue on.
Any Good Ideas?
I get the feeling this is a really messy way of doing this. I also feel like I'm overlooking something really obvious.
Any ideas how to do this in a neat way?
Update
I totally forgot to mention - this will be a 100% opt in feature. It'll be disabled by default. It'll also have 3 levels of privacy - from "a data break occurred" through to "a data break occurred after this application was active with this document path". And the reports will also be anonymous.
I'm conscious of all the privacy concerns - this is so I can make the software better, not so I can spy on people!
Give the report sender a delegate property and protocol, with at least two methods: reportSenderDidSucceed: and reportSender:failedWithError:. The report sender will send the latter message from its connection:didFailWithError: method, passing along the error object it got.
I do hope you'll make this feature optional. Expect lots of angry/curious email from users (not to mention public warnings of “don't use this app because it phones home” on web pages) if you don't.
Just a quick note to say if anyone wants a good tutorial on implementing your own delegates as Peter is suggesting I do, I found this one:
http://cocoadevcentral.com/articles/000075.php
Check it out. It's excellent!

Resources