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
Accepted Answer

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

For the first part of what you are trying to show me on choosing a ride type. I have it set up with a collection view. The type of rides is stored in an array.

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

Should I still do it this way because I keep getting errors?

let array:[String] = ["Luxury", "Basic"]

This is how my array looks. Also, how does a prepare for segue look?

Sure, you can use an array ; the interest of enum is to force verification at compile time.


What error do you get now ? Where ?


If you use array, you would change :


var rideType : TypeOfRide => Probably becomes an Int, with 0 for luxury and 1 for basic ?

var rideType : Int


If so, I do advise to define constants :

let luxury = 0
let basic = 1

Then, in prepare :


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

     var ridePrice : Float = 0.0

     switch rideType {
        case basic :      // no more dot ahead
          switch rideLength {      // Do you keep the enum here, or change to Int as well ?
               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      // In PriceVC, define it as Int? and not TypeOfRide ?
                    destVC.length = rideLength
               }
          }
     }


Finally, in PriceView

in viewDidLoad

priceLabel.text = price
typeLabel.text = array[category]     // But is is much less readble than category.description
lengthLabel.text = length.description     // Change as well

Dont use basic names as array ; give a name that shows what the array is.

I was able to do it the original way you showed me but at the top, on the view controller class it tells me "class "View Controller" has no initializers". Right now I'm on this step

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

OK, it is because var such as

var rideType : TypeOfRide
var rideLength : LengthOfRide 


are not initialized.


You can either give it de default value:

var rideType : TypeOfRide = .basic
var rideLength : LengthOfRide  = .short


or make it an optional

var rideType : TypeOfRide?
var rideLength : LengthOfRide?


Or you can initialize values in viewDidLoad.


First solution is the simplest.

I get the following errors in the prepare for segue:


Ambiguous use of 'rideLength' I get this error on the line

case luxury :  
          switch rideLength {


Use of undeclared type 'PriceView' I get this error on the line

if let destVC = segue.destination as? PriceView {

1. You forgot the dot : it is .luxury and not luxury (if you use the enum and not the let constant)

case .luxury :   
          switch rideLength {


.luxury is equivalent to TypeOfRide.luxury, because Swift can infer the type ; but with just luxury, it does not understand.


2. Have you defined a class for the destination ViewController ? In my code I name it PriceView:

class PriceView: UIViewController {

}


How did you name it ?


3. To answer the other message that was deleted:

It is not the default value, it is an initialisation value.


Of course, people will be able to select a luxury ride. You should have a button for this, that is ON/Off for luxury with IBAction:


@IBOutlet luxurySwitch : UISwitch!


With an IBAction


@IBAction setRideType(_ sender: UIButton) {
     rideType = luxurySwitch.isOn ? .luxury : .basic
}

for number 1 I am using dot notation. I still get the error.

I tested the code, it compiles fine.


So, please show the complete code of the controller, it is impossible to tell with so limited information.

I have a file named collectionViewCell for the collectionView this is the code:


import UIKit

class CollectionViewCell: UICollectionViewCell {
    
    
    //MARK: Properties
    @IBOutlet weak var rideHorizontalSwipe: UIImageView!
}


Then this is the code in the viewController:

@IBOutlet weak var segueToPrice: UIButton!
    let array:[String] = ["cheap", "basic", "luxury", "platinum"]
    var rideType: rideHorizontalSwipe = .cheap
    var rideLength2: rideLength = .sixtyMinuteride
    
    
    class priceView: UIViewController{
        var price : Float?
        var category : rideHorizontalSwipe?
        var length: rideLength?
        @IBOutlet weak var rideLengthPrice: UILabel!
        @IBOutlet weak var ridePriceTotal: UILabel!
        override func viewDidLoad() {
            ridePriceTotal.text = price
            rideLengthPrice.text = length.description
        }
        
    }

    //Number of Views for Collection View
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return array.count
    }
    
    //Populate Collection View
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell
        cell.rideHorizontalSwipe.image = UIImage(named: array[indexPath.row] + ".JPG")
        return cell
    }
    
    enum rideHorizontalSwipe{
        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"
            }
        }
        
    }
    
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        
        var ridePrice : Float = 0.0
        
        switch rideType {
        case .cheap :
            switch rideLength {
            case .sixtyMinuteride : ridePrice = 5.0
            case .ninetyMinuteride : ridePrice = 10.0
            case .twoHourride : ridePrice = 15.0
            }
        case .basic :
            switch rideLength{
            case .sixtyMinuteride : ridePrice = 12.5
            case .ninetyMinuteride : ridePrice = 25.0
            case .twoHourride : ridePrice = 40.0
            }
        case .luxury:
            switch rideLength{
            case .sixtyMinuteride : ridePrice = 12.0
            case .ninetyMinuteride : ridePrice = 12.0
            case .twoHourride : ridePrice = 12.0
            }
        case .platinum:
            switch rideLength{
            case .sixtyMinuteride : ridePrice = 12.0
            case .ninetyMinuteride : ridePrice = 12.0
            case .twoHourride : ridePrice = 12.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
            }
        }
    }

You have named


class priceView: UIViewController


with a lowerCase;


Should be


class PriceView: UIViewController


starting with Uppercase


Always take care: Swift is case sensitive.

That why following camelCase conventions is so useful.


In addition: is PriceView defined inside ViewCOntroller ? It looks like, shouldn't be.

I made a separate file for the PriceView. This is what it looks like:


import UIKit

class PriceView: UIViewController{
    var price : Float?
    var category : rideHorizontalSwipe?
    var length: rideLength?
    @IBOutlet weak var driverGenderPrice: UILabel!
    @IBOutlet weak var rideLengthPrice: UILabel!
    @IBOutlet weak var ridePriceTotal: UILabel!
    
    override func viewDidLoad() {
        ridePriceTotal.text = price
        driverGenderPrice.text = category.description
        rideLengthPrice.text = length.description
    }
    
}


I get the following error


Use of undeclared type 'rideHorizontalSwipe'

Use of undeclared type 'rideLength'


If you need more of the code to help just let me know.

The problem is the scope of declarations.


If the enum is declared inside a class, it is not visible outside.


So, you should declare rideHorizontalSwipe and rideLength outside the ViewController, so that they have a global scope.


Some more comments :

- Normally, array is no more needed

let array:[String] = ["cheap", "basic", "luxury", "platinum"]


- Take care of naming: rideHorizontalSwipe and rideLength are types declarations, they should start with an Uppercase. Take care to change it everywhere in code.

Now that I moved the enum out of the viewController I get errors on the prepare for segue and I still get errors in the PriceView.

The errors are:


Pattern cannot match values of type '[UIButton]!'

Pattern cannot match values of type '[UIButton]!'

Pattern cannot match values of type '[UIButton]!'

Cannot assign value of type '[UIButton]!' to type 'massageLength?'


These errors appear in the prepare for segue which I had left inside of the viewController because it caused fewer errors. The errors in the PriceView are as follow:


Cannot assign value of type 'Float?' to type 'String?'

Cannot assign value of type '(() -> String)?' to type 'String?'

Cannot assign value of type '(() -> String)?' to type 'String?'


These errors appear in the viewDidLoad() in the PriceView.

As I still do not see the whole code, it is impossible to understand what you have done.


If you wish, post your email here, I'll send you an address where to send the full project.


There is an error in:

   override func viewDidLoad() {
        ridePriceTotal.text = price
        driverGenderPrice.text = category.description
        rideLengthPrice.text = length.description
    }

description are func ; should be with parenthesis (sorry, I mistyped in my first post):


        driverGenderPrice.text = category.description()
        rideLengthPrice.text = length.description()



You can also replace the func by a property :


enum TypeOfRide {
    case basic
    case luxury
  
    var description: String {
        switch self {
        case .basic : return "basic"
        case .luxury : return "luxury"
         }
     }
}

Then, you can call

driverGenderPrice.text = category.description
In-App Purchase
 
 
Q