0 1 00:00:00,510 --> 00:00:00,840 All right. 1 2 00:00:00,840 --> 00:00:07,200 So, now that we've created the delegate method for our table view so that we can detect when the user 2 3 00:00:07,230 --> 00:00:11,290 presses that last row that gets more quotes, 3 4 00:00:11,340 --> 00:00:17,880 we can call a function called buyPremiumQuotes and it's in this function that we're going to trigger 4 5 00:00:18,000 --> 00:00:19,880 the in-app purchase. 5 6 00:00:19,890 --> 00:00:27,810 So first of all, we're going to create a new constant that is going to record our product ID and that 6 7 00:00:27,900 --> 00:00:34,490 ID is the same as the ID that we put into App Store Connect under Features, In-App Purchases. 7 8 00:00:34,500 --> 00:00:40,050 We've got that product called Premium Quotes and this is the product ID that we want. 8 9 00:00:40,050 --> 00:00:47,920 So I'm just going to copy it over to our code here and I'm going to put it in just as a simple string. 9 10 00:00:47,970 --> 00:00:54,450 Now, this is the ID that we're going to use to hit up the Apple App Store to tell it what we want to 10 11 00:00:54,450 --> 00:00:54,990 buy, 11 12 00:00:55,020 --> 00:01:01,200 and so that the user gets charged the right amount, namely, whatever it is that we've put in in here, and 12 13 00:01:01,290 --> 00:01:07,670 that they can purchase and that the funds go towards ourselves as our developer account. 13 14 00:01:07,680 --> 00:01:12,010 So, now that we've got what we want to purchase, how are we going to purchase that, 14 15 00:01:12,090 --> 00:01:18,030 well, in order to implement in-app purchases, the framework that we're going to be using is something 15 16 00:01:18,030 --> 00:01:25,170 called a StoreKit and StoreKit has all of the functionality that we'll need to implement in-app purchases 16 17 00:01:25,290 --> 00:01:26,910 for virtual products. 17 18 00:01:26,910 --> 00:01:32,370 So once we've added StoreKit, then we'll be able to tap into all of its functions and attributes. 18 19 00:01:32,430 --> 00:01:40,350 So inside our buyPremiumQuotes function, the first thing we need to check is whether if the user can 19 20 00:01:40,500 --> 00:01:46,200 actually make a purchase because if they're on a phone that has parental controls enabled, then we won't 20 21 00:01:46,200 --> 00:01:49,050 be able to get them to make any sort of in-app purchases, 21 22 00:01:49,050 --> 00:01:55,350 and this is the way that Apple tries to keep your kids from running up the credit card bill and buying 22 23 00:01:55,380 --> 00:01:59,810 thousands of dollars worth of in-app purchases in Clash of Clans, for example. 23 24 00:01:59,820 --> 00:02:04,090 So the way to check that is something called 24 25 00:02:04,100 --> 00:02:09,220 SKPaymentQueue, and this is the queue of payment transactions to be processed by the App Store. 25 26 00:02:09,300 --> 00:02:15,420 And it has a method called canMakePayments, and this method returns a Boolean, 26 27 00:02:15,420 --> 00:02:21,280 so true or false, as to whether if the user is allowed to make payments, and that's exactly what we need 27 28 00:02:21,280 --> 00:02:21,670 to know. 28 29 00:02:21,750 --> 00:02:30,310 So if this returns true, then, in that case, the user can make payments. And if it returns false or rather 29 30 00:02:30,330 --> 00:02:32,750 else, then can't make payments. 30 31 00:02:32,940 --> 00:02:39,060 So it's a good idea while we're testing our app to just put in a print statement that says, 31 32 00:02:39,660 --> 00:02:42,070 "User can't make payments." That we go. 32 33 00:02:42,540 --> 00:02:49,020 But if they can make payments, then we can go ahead and trigger the next step of our in-app purchase request 33 34 00:02:49,320 --> 00:02:51,570 which is to create a new request. 34 35 00:02:51,570 --> 00:02:58,980 So we're going to use a let to create a constant called paymentRequest and this is going to be a new 35 36 00:02:59,010 --> 00:03:05,850 object created from the class called SKMutablePayment which is a mutable request to the App Store 36 37 00:03:06,090 --> 00:03:10,670 to process payment for additional functionality namely in in-app purchase. 37 38 00:03:10,680 --> 00:03:15,330 So this is where we're creating a new in-app purchase request basically. 38 39 00:03:15,590 --> 00:03:24,690 And we're going to specify that this paymentRequest is for the product with productIdentifier of whatever 39 40 00:03:24,690 --> 00:03:30,320 it is that we had up here which is saved under this constant called productID. 40 41 00:03:30,330 --> 00:03:38,040 So we're saying that we're making a request to purchase a new in-app purchase and the product that we 41 42 00:03:38,040 --> 00:03:42,870 want is the one that has the productID that we've specified. 42 43 00:03:42,870 --> 00:03:50,400 And finally, we tap into that SKPaymentQueue again and we grab the default queue and we add this 43 44 00:03:50,400 --> 00:03:52,840 payment request to the queue. 44 45 00:03:52,930 --> 00:03:56,330 So paymentRequest goes in there, and that's it. 45 46 00:03:56,460 --> 00:04:01,520 That's all the code that we need in order to implement our in-app purchase. 46 47 00:04:01,530 --> 00:04:08,520 Now, this is not the end of the tutorial, however, because we still need to figure out how we can detect 47 48 00:04:08,760 --> 00:04:14,880 when a payment has been successful so that we know that we can give them access to the extra information, 48 49 00:04:15,270 --> 00:04:20,070 or that when a payment is failed so that we don't accidentally give them access to all of our premium 49 50 00:04:20,070 --> 00:04:22,910 features, even though they haven't paid for it. 50 51 00:04:22,920 --> 00:04:27,700 So in order to do that, we need to read a little bit about this payment business. 51 52 00:04:27,720 --> 00:04:34,760 So if you hold down alt and click on SKPaymentQueue, there's a quick talk of how this all works. 52 53 00:04:34,770 --> 00:04:39,210 So the summary is that this is a queue of payment transactions to be processed by the App Store. 53 54 00:04:39,210 --> 00:04:46,400 So, of course, you can add multiple paymentRequests to this queue, so you can purchase, you know, five in-app 54 55 00:04:46,410 --> 00:04:49,280 purchases in the same go if you wanted to. 55 56 00:04:49,530 --> 00:04:56,200 And the discussion part says that the payment queue communicate with the App Store and presents a user 56 57 00:04:56,200 --> 00:05:02,530 or a user interface, so a pop up, so that they can enter the password and authorize the payment. 57 58 00:05:02,530 --> 00:05:09,910 Now, to process a payment, first, and at least one observer object which is called SKOaymentTransactionObserver 58 59 00:05:10,720 --> 00:05:17,680 to the queue, and then add a payment object which is what we've done here for the item that 59 60 00:05:17,680 --> 00:05:19,300 the user wants to purchase. 60 61 00:05:19,540 --> 00:05:25,970 And after the payment is fulfilled, the queue updates the transaction object, and then calls the observer 61 62 00:05:25,990 --> 00:05:33,030 so that it tells us that the transaction has been updated and whether if it failed or succeeded. 62 63 00:05:33,700 --> 00:05:39,770 So the next thing that we need to implement is this SKPaymentTransactionObserver 63 64 00:05:40,090 --> 00:05:42,540 and this has a delegate method, 64 65 00:05:42,550 --> 00:05:51,070 so we have to add the protocol of SKPaymentTransactionObserver which contains a set of methods that 65 66 00:05:51,070 --> 00:05:57,690 processes transactions, unlocks purchase functionality, and continue promoted in-app purchases. 66 67 00:05:57,760 --> 00:06:05,170 So that basically means that when we add this observer, then once the payment goes through and the user 67 68 00:06:05,170 --> 00:06:11,530 types in their user name and password for the App Store and it was successful, then we'll get a message 68 69 00:06:11,530 --> 00:06:17,650 back telling us that it was successful, or if it failed, then we'll get a message saying it failed or a 69 70 00:06:17,650 --> 00:06:19,050 whole bunch of other things. 70 71 00:06:19,060 --> 00:06:22,850 So as soon as I add this protocol, you can see we get an error. 71 72 00:06:22,990 --> 00:06:29,000 And it says that in order to conform to this protocol, you have to add the delegate method. 72 73 00:06:29,080 --> 00:06:31,530 So let's get it to add it automatically. 73 74 00:06:31,780 --> 00:06:38,240 And the only delegate method that's required is this one which is paymentQueue updated transactions. 74 75 00:06:38,260 --> 00:06:44,820 So this delegate method will inform us when the transactions have been updated in the paymentQueue. 75 76 00:06:45,130 --> 00:06:52,090 So let's move this down a little bit to the parts where we've got our in-app purchase methods, and we're going 76 77 00:06:52,090 --> 00:06:57,090 to put it over here, and I'm going to just delete this placeholder that says code. 77 78 00:06:57,100 --> 00:07:03,520 Now, remember that whenever you add a new protocol and you want to use a delegate method, you have to 78 79 00:07:03,520 --> 00:07:06,980 declare a class as the delegate. 79 80 00:07:07,150 --> 00:07:14,950 So we want to declare this current class, the QuoteTabVviewController as the delegate who's going 80 81 00:07:14,950 --> 00:07:21,640 to receive the messages from the SKPaymentTransactionObserver when the transaction status changes. 81 82 00:07:21,640 --> 00:07:23,390 So inside viewDidLoad, 82 83 00:07:23,500 --> 00:07:31,050 we're going to add ourselves as the delegate. And the code for this is simply 83 84 00:07:31,050 --> 00:07:38,790 SKPaymentQueue.default which is the default queue that we're using to process our payment request 84 85 00:07:38,960 --> 00:07:42,260 .add. And this adds an observer. 85 86 00:07:42,460 --> 00:07:48,980 So notice that when I write .add, there's actually two methods that this object has. 86 87 00:07:49,030 --> 00:07:55,060 One is add observer and the other one is add the payment to be processed. 87 88 00:07:55,060 --> 00:07:59,700 Now, the only difference they have is the names of the parameters. And in Swift, 88 89 00:07:59,710 --> 00:08:01,030 this is perfectly legal. 89 90 00:08:01,030 --> 00:08:06,790 You can have the same method name, but as long as the parameter names for the methods are different, then 90 91 00:08:06,790 --> 00:08:10,080 they are treated as completely separate methods. 91 92 00:08:10,360 --> 00:08:16,870 So, if you're coming from other programming languages, for example, C or C++, this should be familiar to 92 93 00:08:16,870 --> 00:08:17,760 you as well. 93 94 00:08:17,800 --> 00:08:21,030 So the one that we want is to add an observer. 94 95 00:08:21,340 --> 00:08:28,000 And this takes an object that is a SKPaymentTransactionObserver. And by declaring ourselves 95 96 00:08:28,000 --> 00:08:34,570 as conforming to this protocol and implementing the required delegate method, then we are qualified to 96 97 00:08:34,570 --> 00:08:39,150 set ourselves or this current class as the observer. 97 98 00:08:39,160 --> 00:08:45,550 So now whenever any changes happens to the payment transaction, it's going to contact our 98 99 00:08:45,550 --> 00:08:51,760 QuoteTableViewController and it's going to try and find this delegate method to trigger an update. 99 100 00:08:51,760 --> 00:08:59,350 Now, if you Allt click on this paymentQueue function or search for it in Google or StackOverflow or 100 101 00:08:59,380 --> 00:09:05,650 Apple Docs, you can see that we have a summary which tells an observer, which is this current class, that 101 102 00:09:05,740 --> 00:09:07,950 one or more transactions have been updated. 102 103 00:09:07,960 --> 00:09:12,390 So it gets triggered every single time the transaction status changes. 103 104 00:09:12,610 --> 00:09:19,750 But not only that, the application should process each transaction by examining the transaction's 104 105 00:09:19,750 --> 00:09:21,250 transactionState property. 105 106 00:09:21,250 --> 00:09:27,530 So this is the transactions that we get back and it has a property, apparently, called transactionState. 106 107 00:09:27,850 --> 00:09:32,890 And if we click on transactionState, it tells us the whole bunch of transaction States we can 107 108 00:09:32,890 --> 00:09:33,740 get back. 108 109 00:09:33,760 --> 00:09:42,020 Namely, one is purchasing, being purchased by the aApp Store. Purchased, so successfully process payment. 109 110 00:09:42,070 --> 00:09:48,510 The money is going to hit your developer account and you are ready to release the premium product. 110 111 00:09:48,730 --> 00:09:52,860 Failed, so the transaction failed and you can check the property to see what happened. 111 112 00:09:53,020 --> 00:09:59,830 Or restored which is where, say, if a user buys a new phone and they download the apps all over again, 112 113 00:09:59,960 --> 00:10:05,000 they want to be able to restore the previous in-app purchases that they've made. 113 114 00:10:05,000 --> 00:10:11,690 So we're going to check for each of these cases in the transaction state inside this delegate method. 114 115 00:10:11,690 --> 00:10:17,320 Now, remember, previously, I said the paymentQueue can contain any number of transactions. 115 116 00:10:17,360 --> 00:10:23,420 So if you were to purchase three in-app purchases all in one go, then all the transactions will get piled 116 117 00:10:23,480 --> 00:10:28,120 into this array of SKPaymentTransactions. 117 118 00:10:28,130 --> 00:10:33,380 So in order to check each of these transactions, we're going to have to loop through it. 118 119 00:10:33,380 --> 00:10:41,590 So we're going to say "for transaction in transactions," and that transactions comes from here. 119 120 00:10:41,720 --> 00:10:46,280 This one is just the variable we're creating to loop through these transactions. 120 121 00:10:46,280 --> 00:10:54,530 And for every transaction in all of the array of transactions, we're going to check if the current transaction 121 122 00:10:54,530 --> 00:11:03,080 that we're looping through .transactionState is equal to .purchased, and that means that it was 122 123 00:11:03,140 --> 00:11:05,030 a successful purchase. 123 124 00:11:05,180 --> 00:11:06,320 So User 124 125 00:11:09,990 --> 00:11:11,730 payment successful 125 126 00:11:14,180 --> 00:11:26,220 Now, else if the transaction.transactionState was actually something like .failed, then in that 126 127 00:11:26,220 --> 00:11:30,270 case, something bad happened and payment failed. 127 128 00:11:30,270 --> 00:11:34,620 Now, this can also include if they click the cancel button and they didn't want to go ahead with the 128 129 00:11:34,620 --> 00:11:35,740 payment anymore. 129 130 00:11:35,940 --> 00:11:42,590 So now we have two if statements, one when it's successful and one when the transaction failed. 130 131 00:11:42,600 --> 00:11:50,130 So let's go ahead and add a print statement to show in the console when we test our app which case it 131 132 00:11:50,130 --> 00:11:50,540 is. 132 133 00:11:50,580 --> 00:11:59,010 So print "Transaction successful!" Print "Transaction failed!" 133 134 00:11:59,040 --> 00:12:05,460 So, now we're ready to test our in-app purchases, and remember what I said before that you have to test 134 135 00:12:05,460 --> 00:12:09,030 it on a physical iPhone or iPad device. 135 136 00:12:09,030 --> 00:12:10,490 It can't be in the simulator. 136 137 00:12:10,500 --> 00:12:15,960 It doesn't work and it won't tell you that it's not working or give you any logical errors. 137 138 00:12:16,080 --> 00:12:20,540 You just have to know that you have to use a physical device. 138 139 00:12:20,550 --> 00:12:27,680 Now, the other problem is that I've set this is in-app purchase for my premium quotes to be .99 cents 139 140 00:12:27,790 --> 00:12:32,040 and I don't really want to pay 99 cents every single time I want to test my app, 140 141 00:12:32,040 --> 00:12:32,490 right? 141 142 00:12:32,520 --> 00:12:34,970 So I don't want to use my normal Apple ID. 142 143 00:12:35,040 --> 00:12:36,400 So what's the workaround? 143 144 00:12:36,450 --> 00:12:42,460 Well, we can create a sandbox user that's just for testing these payments. 144 145 00:12:42,540 --> 00:12:44,010 So in the next lesson, 145 146 00:12:44,040 --> 00:12:49,310 that's exactly what we're going to be doing and we're going to be testing our app and seeing whether 146 147 00:12:49,320 --> 00:12:50,110 if it work.