Search code examples
swiftuistorekit

Purchase a product using it's id or displayName


I've been following a few tutorials online regarding setting up Storekit in my app. I've gotten as far as successfully requesting all the products and holding them in a products array. The next step of these tutorials is almost always listing out the products using a ForEach like so:

ForEach(products) { product in
  Button {
    Task.init {
      try await purchaseProduct(product)
    }
  } label: {
    HStack {
      Text(product.displayName)
      Spacer()
      Text(product.displayPrice)
    }
  }
}

This doesn't work for my use case, unfortunately. The design I'm working off has 3 buttons in different parts of the screen, each of which initiate a purchase request for a different product.

I've managed to get some of the way there by doing this:

Button {
    Task.init {
        try await purchaseProduct(products.first!)
    }
} label: {
    HStack {
        Text("\(products.first?.displayName ?? "No name")")
        Spacer()
        Text("\(products.first?.displayPrice ?? "No price")")
    }
}

But this feels really hacky to me, for the following reasons:

  • Force unwrapping doesn't feel correct
  • I can make this work for the .first and .last item in the products array but I don't know how to get the second item, and this also means if the order of items inside products changes, my UI ties the wrong product to their respective button.

Here's my purchaseProduct function:

func purchaseProduct(_ product: Product) async throws -> StoreKit.Transaction {
    let result = try await product.purchase()

    switch result {
    case .pending:
        throw PurchaseError.pending

    case .success(let verification):
        switch verification {

        case .verified(let transaction):
            await transaction.finish()
            return transaction

        case .unverified:
            throw PurchaseError.failed
        }
    case .userCancelled:
        throw PurchaseError.cancelled

    @unknown default:
        assertionFailure("Unexpected result")
        throw PurchaseError.failed
    }
}

Ideally, I'm looking to do something like this:

if let productOne = products.PRODUCTID {
  Button {
      Task.init {
          try await purchaseProduct(productOne)
      }
  } label: {
      HStack {
          Text("\(productOne.displayName)")
          Spacer()
          Text("\(productOne.displayPrice)")
      }
  }
}

But I'm struggling to wrap my head around how to get there.


Solution

  • In order to achieve your desired if let productOne = products.PRODUCTID, you can use first(where:): https://developer.apple.com/documentation/swift/array/first(where:)

    if let productOne = products.first(where: {$0.id == "iap.myapp.ProductOne"}) {
     // ...
    }