Hey everybody what's going on this is Caleb with Dev slopes dot com and this is part two of the video
you were just watching.
I just wanted to split it into two halves because it was getting a bit long and I didn't want you to
have to sit there and listen to me ramble for such a long time.
So anyway let's go ahead and let's head back to right where we were in the last video.
Very very cool so tap.
OK.
And it says we were successful.
I don't see that it has actually changed anything though so if I go in you'll see that the ads are still
there.
Subscription is still expired.
So that's really interesting.
I wonder what the deal is.
Let's see if we build and run we're going to restart it here if we go back in.
Let's see that let's see if it actually updates and shows that we are confirmed.
OK.
So did you see that I built it it says new expiration date 2017 and it ends in about four minutes so
that's about right.
Subscription valid and watch what happens.
It just says updating.
So we're not getting a notification here for subscription status was changed.
I think we need to go figure out why that is so go back into your IP service and let's think about what's
going on here.
So our request finishes that's great.
It uploads the receipt it checks to make sure everything is active and fantastic.
But what I'm noticing is when we bought it it says purchased successful which comes from our function
down here.
It said that we were successful.
We don't need to necessarily do anything in our complete function for a subscription because request
did finish gets called but let's go check out our subscription active function.
Ok so this is really interesting so I wonder we are not getting the label to update because when we
come through we get a new subscription date.
That means it's valid we get a subscription valid but we don't get subscription active.
So it appears that we're not able to get through here.
Now it looks like the completion handler is not actually returning what we're expecting.
So that means that we're not getting all the way through.
Now we're going to go ahead and see what the deal is here.
We have the now date which is right now we have the expiration date which comes from our user defaults
and we're checking if right now is less than Then we return true or we return false.
So let's actually put some breakpoints here and let's reload the app to see which one is being called
if any at all.
There is a chance that neither one of them are being called.
Okay.
And we do get a break which is perfect that's what we want and that's great.
So we get completion händler is true.
It sends back through our function so we should be getting a boolean here saying that we're good to
go.
That's awesome.
Okay.
And if I keep running here we're going to get an error or what is this error.
Interesting.
So check this out.
Main thread checker UI API called on a background thread UI label set text.
Oh interesting.
So I do notice we get subscription active here but what I'm noticing is that we are actually not doing
what we should be.
That's really weird.
So check it out guys.
If I go in to storefront VC you'll see there's this purple air here and it says you labeled text must
be used from main thread only.
Now this function subscription status was changed is being called by our notification.
And believe it or not our notifications here when we call them these are being sent on a background
thread and any code that is called from a notification is being called on a background thread.
Now you are not allowed to update you I say our interface we're not allowed to update it from a background
thread so we need to put it on the main thread.
Now what we're going to actually do to do that is we're going to just go ahead and call dispatch queue
dispatch queue dot main OK the main dispatch queue we want on the main queue not the background and
we're going to call async.
And this is going to asynchronously go through this and update our user interface depending on whether
our subscription is active or not.
That's just kind of a cool way that we can modify and update our UI by pulling this up and putting it
on the main thread instead of the background thread.
So if we build and run that error should go away.
But I do notice we're getting subscription active but we didn't get it when we bought it.
We also didn't get it when we reloaded it for the first time and I want to tell you why that is.
OK so check it out.
It is now a 20 meaning our subscription has expired.
And you'll notice it successfully says subscription expired but it also just said purchase was successful
and it gave me a new expiration date.
So why did it buy it without us going on.
That's the weird thing.
So let me tell you why we are not properly finishing our transaction.
So basically what happens is a transaction gets loaded into the S-K payment queue and every time we
quit out of the app it's supposed to unload all of those and finish them.
But we're not doing that.
So they just stay in the payment queue.
So when we open the app they get paid again.
They get bought over and over and over again.
So that's really really bad.
We don't want to be doing that.
And I found a clever way to fix this so up to the top.
And if you remember what we do in our IP service sorry to the top of your IP service what we do is when
this is initialized we are adding our IP service as the observer for the payment queue.
But we are not ever removing that observer.
So when we quit our app we are still observing it.
When we open it we're observing it again.
Everything stays there and we never remove that observer.
So what we're actually going to need to do is to move this code somewhere else that makes a little more
sense and I'm going to show you where.
So go ahead and just select all of this and delete it and then we're going to move into the app.
DELEGATE.
OK.
And this is actually what Apple considers best practice.
So we're going to do this the right way.
So select app delegate and there's a function here called did finish launching with options.
This is called right when the app launches and this is actually where we're going to go ahead and set
up the observer on our IAP service.
So we need to first import store kit so tight that import store kit then above return true.
We need to go ahead and type S-K payment.
Q Dot default dot ad and we're going to add an observer.
Now the cool thing is since we're using a singleton we can just call IAP service that instance
instance and that gives us an access to our IP service.
We don't have to instantiate it.
We can reference the exactly right one from our from our app delegate which is cool.
So that's great and all but we didn't think about how are we going to deal with this.
How are we going to remove it when the app is closed and there's a handy dandy function right here.
Application will terminate called when the application is about to terminate.
Understandably save data if appropriate.
And this is actually where we're going to remove the data.
OK.
So in application will terminate call S-K payment queue default remove and the observer is IAP service
for instance.
OK.
And that's going to remove that observer.
So those transactions finish and they're not added again when we open the application.
So go back to your IP service.
Go ahead and build and run.
And we're going to go ahead and take a look to see if this helped our case when we actually buy our
product and I'm going to scroll down to just requested finish OK.
Very cool.
So when we open the app it checks everything OK and make sure that we're still valid and we are so to
take a look subscription active notice that it updated.
It didn't say hey you're doing this on a background thread.
And in about a minute and a half we'll be able to actually verify that our subscription expires which
is very cool.
So I'm just going to go ahead and wait or speed up the video and as soon as we are at an expiration
point we will resume.
So
all right so are our subscription is active Let's go ahead and let's build and run this again because
we currently haven't set up any way to update the view if it's expired.
But we open the app and now look at that subscription expired.
That was our expiration date 8:24 33 seconds.
That's awesome.
So it successfully expires and it did not you know reload.
Now it is still saying subscription valid and that's because we legitimately purchased that with a real
receipt.
It will always be valid unless we bought it with an app or with jailbreak tweak.
So let's go ahead let's buy this subscription again.
Let's tap continue and confirm and let's see if this helps make our label appear OK.
Purchase was successful.
Very cool.
That's great.
Now if we tap.
OK.
It appears that we did not successfully finish so.
Interesting.
Let's see what the deal is.
Let's go down to complete let's make sure that we are successfully AKCA.
We finish the transaction we call complete and OK interesting so we print purchase was successful.
Oh and then we break.
So I wonder if since we're breaking this is not actually being called our function here.
Requested finish since we break early.
I wonder if that is part of the problem.
You know what.
So let's go ahead and let's actually remove the break there and let's build and run and let's see if
that helps fix our problem that we are going to have to wait until our subscription expires which is
unfortunate but basically we can tell it's valid and it's going to expire in you know about three minutes
from now.
But oh so interesting.
We're getting a subscription expired even though we're getting an expiration date back.
You know what I ran into problems with this earlier and right here we set our expiration and we said
that we basically set the value for the expiration date.
But basically what we are looking for in storefront Visi the value that we pull down for expiration.
The issue is that the value we're looking for for the date we set it here.
But when the purchase is complete it's using the old value k because we remember we save it here.
Expiration date this is instantiated when IAP service is instantiated.
So basically the problem is that we have instantiated this and then we go in that we make a purchase
we set non-consumer will purchase to be true.
Right.
But what happens is the notification is sent and that determines whether or not the the non consumable
purchase shows up is true.
And so the issue is that we actually need to refresh and call that when we check to see if our subscription
is active because otherwise we get the old expiration date when this is called.
So we need to create a little function that will refresh those values and it'll set it to the new one
that gets set so that we actually have the updated value when we call is subscription active.
So go ahead and write a little function maybe down here below set expiration called phunk reload expiry
date date.
There we go.
And what we're going to do is we're basically going to just say expiration date meaning the one that
we have instantiated in this instance is going to be equal to user defaults dot standard value for key
and the key is expiration date WIPs expiration date date.
OK I can't type.
Expiration date.
And we're going to go ahead and cast that as date.
OK.
So this function will reload the date and it will update the instance of it in our view controller which
is great.
And so now what we can do is when we call is subscription active before we actually go through this
we can call reload expiry date and it'll update it to the new one set.
When we made a subscription this basically ensures that even if we buy the subscription right on the
page that it will update in the right amount of time.
So go ahead and build and run this.
Let's pop open the app and let's check out what's going on.
Here we go.
All right.
Very cool.
It says expired as it should.
OK.
Because it is after 8:30 so go ahead and tap on the subscribe button tap confirm purchase and in a moment
it's going to ask us to confirm it.
Assuming my internet is going to be nice it's being a little slow today so here we go.
OK.
Awesome.
This description will continue in less canceled tap.
OK.
Your purchase was successful.
Very cool.
So that looks great although it is still saying that it's expired.
So it appears that one of our functions or one of our notifications are not being called properly.
So let's see if I actually if I reloaded this will be a real test because remember it was still saying
it was expired.
OK cool.
So it says it's valid.
It says it's active.
That means that the expiration date is getting properly reloaded.
But we are still not sending notifications through properly and that's going to be an issue.
So why don't we go ahead and let's head over to the next video so that we can fix that problem.
All right let's head over to the next video.
Let's do it.