You Won’t Believe How We Enabled In-App Purchases for PWAs on iOS

This post guides us through the journey of how we wrangled Swift to integrate in-app purchases in iOS PWAs.
Last updated on November 3, 2023
| Software Engineer
Buying Apple Subscription from the Store PWA

Progressive Web Applications on iOS typically have restricted access to Apple’s exclusive APIs compared to native apps. These limitations extend to user reachability, purchases, and authentication features. Even after the introduction of the Web Push API to Safari’s PWA, there continues to be a limitation in access for Store PWAs. This restriction also applies to In-App Purchases (which is the opposite for Android and Windows)

Lack of Web APIs in Store PWA (WKWebView)

For those new to PWABuilder, let me give a quick primer. The PWABuilder service can package Progressive Web Apps for various platforms, including the Apple App Store. It achieves this by automatically generating a Swift project-wrapper, allowing PWA developers to target the App Store without having to touch native iOS code. However, this approach has certain limitations. Notably, it does not natively support the integration of In-App Purchases.

This gap in functionality comes with extra coding and configurations, which can be quite a challenge especially for those of you who, like me, are PWA developers with limited Swift experience. But with challenges come opportunities, and this is where it gets interesting. Let’s see how we can enable in-app purchases in our PWA.

Iterating the code for new IAP module via ChatGPT Playground

After active iterations with ChatGPT and reading related to Store Kit articles [Mastering Store Kit, In-App Purchase with StoreKit2, Shotty Birds Repository, Documentation], we eventually made headway.

We defined and set up the correct webkit message handlers and functions in Swift with each one assigned a specific task - be it kicking off a purchase, retrieving info on products, or requesting transactions.

// Fetch products list by id's from WebView
func fetchProducts(productIDs: [String] = StoreKitAPI.IntentsProducts) async {
do {
self.products = try await Product.products(for: productIDs)

// Convert each product representation (Data) to JSON String
let productJSONStrings: [String] = self.products.compactMap { product in
guard let jsonString = String(data: product.jsonRepresentation, encoding: .utf8) else {
return nil
}
return jsonString
}

self.productsJson = "[\(productJSONStrings.joined(separator: ","))]"
returnProductsResult(jsonString: self.productsJson)
} catch {
self.products = []
// handle error
}
}

// push results to webview
func returnProductsResult(jsonString: String){
DispatchQueue.main.async(execute: {
PWAShell.webView.evaluateJavaScript("this.dispatchEvent(new CustomEvent('iap-products-result', { detail: '\(jsonString)' }))")
})
}

Following the Swift implementation, the next step was to configure the front end of the demo PWA to handle these new events. We set up JavaScript event listeners to respond to messages from the Swift class, thus creating a smooth relay between Swift and JavaScript.

productsRequest() {
if (this.iOSIAPCapability)
window.webkit.messageHandlers['iap-products-request'].postMessage([
'demo_product_id',
'demo_product2_id',
'demo_subscription',
'demo_subscription_auto'
]);
}

window.addEventListener('iap-products-result', (event: CustomEvent) => {
if (event && event.detail) {
this.logMessage(event.detail, true);
this.products = JSON.parse(event.detail);
}
});
Products and Transactions passed to PWA from Store Kit 2

The output until now is promising. We’ve managed to make a prototype that starts and manages In-App purchase processes within Store PWAs on Apple devices. However, we’ve only just started.

For you to follow along:

  1. MacOS with a fresh Xcode is required for this project (sadly)
  2. Use this repository with “iap” branch as your base. It’s pre-configured as a demo of IAP.
  3. Follow readme instructions about the CocoaPods installation, but skip the firebase setup section
  4. Also check this instruction about the building and publishing xcode project.
  5. Check the documentation about StoreKit testing, and update StoreKit test file for your needs
  6. Native logic is in IAP.swift file
  7. The source code for web demo component of you can find here
  8. Feel free to create issue here or reach us in Discord for any questions

Your feedback is greatly valued. With this feature’s success, it will bring us substantial leap forward, not only enhancing the Store PWA experience but also advancing the PWA community as a whole.

Share this article on your social media!

Author Profile

profile picture

Gleb Khmyznikov @khmyznikov

Eastern Europe based Software Engineer who loves PC hardware, gaming handhelds, classic cars and web technologies.