In-App Purchase

Hi


I want to add to my app the ability to purchase something. My app is similar to Uber. What I mean is that I have a collection view, and I want to be able to associate a price for the type of ride ex. basic or luxury, and the duration of the ride. My app is set up where you choose in collection view what type of ride you want. Then there are 3 sets of buttons where you choose the length of the ride. What I wanted to know is how would I make it so that my app take the type of ride, and the duration and adds the two of them together on another screen, as well as displays what choices you made on the previous screen, and what would the code look like for that. The only thing that I have for the follow-up screen where I want my app to show the price and ask the user to pay is I made a view controller, and I connected it to the previous screen with a custom segue which has no code in it yet.

Answered by Claude31 in 323066022

So your question is not about IAP but about transferring data from one view to another.


What I wanted to know is how would I make it so that my app take the type of ride, and the duration and adds the two of them together on another screen


Let's call the view wwhere you define the ride the rideView and the view where you tell the price the price View

Proceed like this:

you have probably defined enum

enum TypeOfRide {
    case basic
    case luxury

    func description() -> String {
          switch self {
               case .basic : return "basic"
               case .luxury : return "luxury"
          }
}

enum LengthOfRide {
    case short
    case medium
    case long    

     func description() -> String {
          switch self {
               case .short : return "short"
               case .medium : return "medium"
               case .long : return "long"

          }
}


In RideView:

- you have a var to store the type of ride

var rideType : TypeOfRide

This var is set when you select the type either to .basic or .luxury in the collectionView


- you have a var to store the length of ride

var rideLength : LengthOfRide

This is set when you click a button, in the IBAction


In PriceView,

you define a var to store the price, the lenght and the type of ride

var price : Float?
var category : TypeOfRide?
var length: LengthOfRide?

and an IBOutlet where you will display the price

    @IBOutlet weak var priceLabel: UILabel!
    @IBOutlet weak var typeLabel: UILabel!
    @IBOutlet weak var lengthLabel: UILabel!


In RideView:

You should have a butto like Order

Control-drag from the button to the PriceViewController to define a segue

Give it an identifier as SegueToPrice


In prerare for segue

- you compute the price, according to your computation formula

- you pass the price to the destination VC


override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

     var ridePrice : Float = 0.0

     switch rideType {
        case .basic :
          switch rideLength {
               case .short : ridePrice = 5.0
               case .medium : ridePrice = 10.0
               case .long : ridePrice = 15.0
          }
        case .luxury :
          switch rideLength {
               case .short : ridePrice = 12.5
               case .medium : ridePrice = 25.0
               case .long : ridePrice = 40.0
          }
     }

          if segue.identifier == "SegueToPrice" {          // The identifier you have given to the segue
               if let destVC = segue.destination as? PriceView {
                    destVC.price = ridePrice
                    destVC.category = rideType
                    destVC.length = rideLength
               }
          }
     }



Finally, in PriceView

in viewDidLoad

priceLabel.text = price
typeLabel.text = category.description
lengthLabel.text = length.description

So, you connect the Continue button directly to the next controller with a segue ?

And you named the segue SegueToPrice ? ('take care of uppercase)

If so, that's correct


To check the parameters you pass through the segue, please add print statementrs, and report what you get :


        if segue.identifier == "SegueToPrice" {          // The identifier you have given to the segue
            if let destVC = segue.destination as? PriceView {
                destVC.price = ridePrice
               print(destVC.price)
                destVC.category = rideType
               print(destVC.category.description())
                destVC.length = rideLength
               print(destVC.length.description())
           }
        } else {
          print("wrong segue"; segue.identifier)
     }



Can you show also how you set the IBOutlets with their value ?


@IBOutlet weak var driverGenderPrice: UILabel!

@IBOutlet weak var rideLengthPrice: UILabel!

@IBOutlet weak var ridePriceTotal: UILabel!

Can you please show an example of how to connect the Continue button directly to the next controller with a segue? Also, what do you mean show IBOutlets with their values?

1.how to connect the Continue button directly to the next controller with a segue


What do you expect when you click Continue ? To go to destVC I guess.


The segue to do this can be:

- created from the ViewController : control-drag from the leftmost button at the very top of viewController window to the destVC

Then, you would have an IBAction in the Continue button to call perform segue


- create directly the link from Continue button:

Control-drag from the button to destVC and select show for segue


In this case, you don't need to create the IBAction (in fact, it is created by the system for you).


In both cases, you have to name the segue if you need identifier in prepare for segue.


If not one of those 2 methods, how did you program the Continue Button to show the destVC ?


2. what do you mean show how you set IBOutlets with their values?


In PriceView, you have outlets.

So You have to write their content with the values you passed with prepare for segue

This is usually done in ViewDidLoad, with statements as


driverGenderPrice.text = String(self.price) // That sets the content of the IBOutlet

1. I control-dragged from the continue button to the destVC and chose the show segue. So for the segue in the identifier would I name it SegueToPrice?


2. This is the values I have for the outlets.


override func viewDidLoad() {

ridePriceTotal.text = String(format: "%.2f", price!)

driverGenderPrice.text = category?.description()

rideLengthPrice.text = length?.description()

}

1. Yes, name it SegueToPrice ; but this identifier must not be given to another segue.


2. for setting up the labels contents, test if nil

    override func viewDidLoad() {
       if price != nil { ridePriceTotal.text = String(format: "%.2f", price!) }
        if category != nil {driverGenderPrice.text = category!.description() }
       if length != nil { rideLengthPrice.text = length!.description() }
    }


3. What is the result of the prints :

        if segue.identifier == "SegueToPrice" {          // The identifier you have given to the segue 
            if let destVC = segue.destination as? PriceView { 
                destVC.price = ridePrice 
               print(destVC.price) 
                destVC.category = rideType 
               print(destVC.category.description()) 
                destVC.length = rideLength 
               print(destVC.length.description()) 
           } 
        } else { 
          print("wrong segue"; segue.identifier) 
     }

I changed the identifier, changed the viewDidLoad. The app is still crashing. As well as I am not getting any result in the debug area for the print.

1. So, you don't get any message from prepare from segue ? Not even "wrong segue" ?

Did you copy the complete code I proposed ?

=> Please confirm


If so, that mean that prepare is not called


2. We need to see if you go to the destVC.


So in viewDidLoad of PriceView, add som print as well


    override func viewDidLoad() {
       print("viewDidLoad starts")
       if price != nil { ridePriceTotal.text = String(format: "%.2f", price!) }
       print("price is", ridePriceTotal.text, price!)
       if category != nil {driverGenderPrice.text = category!.description() }
       print("category is", driverGenderPrice.text, category!.description())
       if length != nil { rideLengthPrice.text = length!.description() }
       print("length is", rideLengthPrice.text, length!.description())
    }

=> Please test and tell exactly what you get


3. If you do not get some print here, that means that you do not connect to the destVC.

If so, what did you do exactly for the Continue button ?

Did you connect via a segue to destVC ?

Did you do something else ?

=> Please explain

This is what I get in the debug area:


2018-08-02 07:51:06.828750-0700 Spa Day[861:28144] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<Drive_Day.ViewController 0x7fd3a801ba00> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key rideLengthPrice.'

What is surprising is that the error speaks of ViewController, not PriceView.

I suppose you have a class named ViewController

(the one where you define

@IBOutlet weak var driverGenderFemale: UIButton!

@IBOutlet weak var eitherGender: UIButton!

@IBOutlet weak var driverGenderMale: UIButton!

)


Are you sure that you have defined the class of the PriceView controller, in the Identity inspector of IB as PriceView and not ViewController ?


In addition, it is very hard to help if you don't answer the questions I ask you.

Could you answer all questions of my previous post ?


Is this post now too long ? It does not show on the list of posts, except with view all posts !

From the previous post


1. Yes I copied the code


2. I added the code and got this:


2018-08-02 07:51:06.828750-0700 Drive Day[861:28144] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<Drive_Day.ViewController 0x7fd3a801ba00> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key rideLengthPrice.'


For the current post, I did not name the identity inspector PriceView I created a file named PriceView and I put the storyboard id as PriceView but I still get the error that posted. How do you set an identifier for a view controller in the storyboard? Also, was I supposed to put the enums and outlets as well as prepare for segue for the drive type and length in the PriceView because I put it in the viewController.


This is the full error:


2018-08-03 20:44:43.447341-0700 Drive Day[1636:86270] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<Drive_Day.ViewController 0x7faa3d0d3800> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key rideLengthPrice.'

*** First throw call stack:

(

0 CoreFoundation 0x000000010706b1e6 __exceptionPreprocess + 294

1 libobjc.A.dylib 0x0000000106700031 objc_exception_throw + 48

2 CoreFoundation 0x000000010706b0b9 -[NSException raise] + 9

3 Foundation 0x0000000105906b47 -[NSObject(NSKeyValueCoding) setValue:forKey:] + 292

4 UIKit 0x0000000107c0cec0 -[UIViewController setValue:forKey:] + 87

5 UIKit 0x0000000107efae8a -[UIRuntimeOutletConnection connect] + 109

6 CoreFoundation 0x000000010700de8d -[NSArray makeObjectsPerformSelector:] + 317

7 UIKit 0x0000000107ef9834 -[UINib instantiateWithOwner:options:] + 1856

8 UIKit 0x0000000107c140d7 -[UIViewController _loadViewFromNibNamed:bundle:] + 383

9 UIKit 0x0000000107c14a04 -[UIViewController loadView] + 177

10 UIKit 0x0000000107c14d21 -[UIViewController loadViewIfRequired] + 175

11 UIKit 0x0000000107c15574 -[UIViewController view] + 27

12 UIKit 0x00000001086a0a43 -[_UIFullscreenPresentationController _setPresentedViewController:] + 89

13 UIKit 0x0000000107be1f17 -[UIPresentationController initWithPresentedViewController:presentingViewController:] + 133

14 UIKit 0x0000000107c284d0 -[UIViewController _presentViewController:withAnimationController:completion:] + 3782

15 UIKit 0x0000000107c2b2c6 __63-[UIViewController _presentViewController:animated:completion:]_block_invoke + 99

16 UIKit 0x0000000107c2b958 -[UIViewController _performCoordinatedPresentOrDismiss:animated:] + 511

17 UIKit 0x0000000107c2b22c -[UIViewController _presentViewController:animated:completion:] + 169

18 UIKit 0x0000000107c2b570 -[UIViewController presentViewController:animated:completion:] + 154

19 UIKit 0x0000000107c307cb -[UIViewController _showViewController:withAction:sender:] + 274

20 UIKit 0x00000001081d7849 __66-[UIStoryboardShowSegueTemplate newDefaultPerformHandlerForSegue:]_block_invoke + 134

21 UIKit 0x0000000108395f47 -[UIStoryboardSegueTemplate _performWithDestinationViewController:sender:] + 276

22 UIKit 0x0000000108395e05 -[UIStoryboardSegueTemplate _perform:] + 82

23 UIKit 0x00000001083960c7 -[UIStoryboardSegueTemplate perform:] + 157

24 UIKit 0x0000000107a723e8 -[UIApplication sendAction:to:from:forEvent:] + 83

25 UIKit 0x0000000107bed7a4 -[UIControl sendAction:to:forEvent:] + 67

26 UIKit 0x0000000107bedac1 -[UIControl _sendActionsForEvents:withEvent:] + 450

27 UIKit 0x0000000107beca09 -[UIControl touchesEnded:withEvent:] + 580

28 UIKit 0x0000000107ae70bf -[UIWindow _sendTouchesForEvent:] + 2729

29 UIKit 0x0000000107ae87c1 -[UIWindow sendEvent:] + 4086

30 UIKit 0x0000000107a8c310 -[UIApplication sendEvent:] + 352

31 UIKit 0x00000001083cd6af __dispatchPreprocessedEventFromEventQueue + 2796

32 UIKit 0x00000001083d02c4 __handleEventQueueInternal + 5949

33 CoreFoundation 0x000000010700dbb1 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17

34 CoreFoundation 0x0000000106ff24af __CFRunLoopDoSources0 + 271

35 CoreFoundation 0x0000000106ff1a6f __CFRunLoopRun + 1263

36 CoreFoundation 0x0000000106ff130b CFRunLoopRunSpecific + 635

37 GraphicsServices 0x000000010c7e1a73 GSEventRunModal + 62

38 UIKit 0x0000000107a71057 UIApplicationMain + 159

39 Drive Day 0x00000001041ba8f7 main + 55

40 libdyld.dylib 0x000000010af36955 start + 1

)

libc++abi.dylib: terminating with uncaught exception of type NSException

(lldb)


This is the only code in my PriceView:


import UIKit

class PriceView: UIViewController{
    var price : Float?
    var category : RideType?
    var length: rideLength?
    @IBOutlet weak var driverGenderPrice: UILabel!
    @IBOutlet weak var rideLengthPrice: UILabel!
    @IBOutlet weak var ridePriceTotal: UILabel!
    
    override func viewDidLoad() {
        print("viewDidLoad starts")
        if price != nil { ridePriceTotal.text = String(format: "%.2f", price!) }
        print("price is", ridePriceTotal.text, price!)
        if category != nil {driverGenderPrice.text = category!.description() }
        print("category is", driverGenderPrice.text, category!.description())
        if length != nil { rideLengthPrice.text = length!.description() }
        print("length is", rideLengthPrice.text, length!.description())
    }
    
}



This is the code that I implemented into the viewController:


   override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
      
        var ridePrice : Float = 0.0
      
        switch rideType {
        case .cheap :
            switch rideLength{
            case .sixtyMinuteRide : ridePrice = 90.00
            case .ninetyMinuteRide : ridePrice = 120.00
            case .twoHourRide : ridePrice = 140.00
            }
        case .basic :
            switch rideLength{
            case .sixtyMinuteRide : ridePrice = 90.00
            case .ninetyMinuteRide : ridePrice = 120.00
            case .twoHourRide : ridePrice = 140.00
            }
        case .luxury:
            switch rideLength{
            case .sixtyMinuteRide : ridePrice = 90.00
            case .ninetyMinuteRide : ridePrice = 120.00
            case .twoHourRide : ridePrice = 140.00
            }
        case .platinum:
            switch rideLength{
            case .sixtyMinuteRide : ridePrice = 90.00
            case .ninetyMinuteRide : ridePrice = 120.00
            case .twoHourRide : ridePrice = 140.00
            }
        }
      
        if segue.identifier == "SegueToPrice" {          // The identifier you have given to the segue
            if let destVC = segue.destination as? PriceView {
                destVC.price = ridePrice
                destVC.category = rideType
                destVC.length = rideLength
            }
        }
    }

This is the code for the PriceView:

import UIKit

class PriceView: UIViewController{
    var price : Float?
    var category : rideType?
    var length: rideLength?
    @IBOutlet weak var driverGenderPrice: UILabel!
    @IBOutlet weak var rideLengthPrice: UILabel!
    @IBOutlet weak var ridePriceTotal: UILabel!
   
    override func viewDidLoad() {
       ridePriceTotal.text = String(format: "%.2f", price!)
        driverGenderPrice.text = category?.description()
        rideLengthPrice.text = length?.description()
    }
   
}



Above the viewController I put this code:


enum RideType{  
    case cheap  
    case basic  
    case luxury  
    case platinum  
    func description() -> String{  
        switch self{  
        case .cheap: return "cheap"  
        case .basic: return "basic"  
        case .luxury: return "luxury"  
        case .platinum: return "platinum"  
        }  
    }  

}  

enum rideLength{  
    case sixtyMinuteRide  
    case ninetyMinuteRide  
    case twoHourRide  
    func description() -> String{  
        switch self{  
        case .sixtyMinuteRide : return "sixtyMinuteRide"  
        case .ninetyMinuteRide : return "ninetyMinuteRide"  
        case .twoHourRide: return "twoHourRide"  
        }  
    }  

}

If you need more of the code let me know I'm trying my best to give you everything.

Yes it is too long my post are now being moderated as soon as I reply.

In-App Purchase
 
 
Q