diff --git a/bun.lock b/bun.lock
index 80700eed..5d46cd05 100644
--- a/bun.lock
+++ b/bun.lock
@@ -1,5 +1,6 @@
{
"lockfileVersion": 1,
+ "configVersion": 0,
"workspaces": {
"": {
"name": "superwall-docs",
diff --git a/content/docs/android/changelog.mdx b/content/docs/android/changelog.mdx
index b1139db2..8f8d294f 100644
--- a/content/docs/android/changelog.mdx
+++ b/content/docs/android/changelog.mdx
@@ -3,6 +3,23 @@ title: "Changelog"
description: "Release notes for the Superwall Android SDK"
---
+## 2.7.8
+
+### Fixes
+
+- Fix serialization issue with R8 and dates
+- Fix scope cancellation for test mode
+
+## 2.7.7
+
+### Enhancements
+
+- Adds support for local resource loading in paywalls via `Superwall.instance.localResources`
+
+### Fixes
+
+- Fix issues with stripe period types failing to deserialize
+
## 2.7.6
### Fixes
diff --git a/content/docs/android/guides/local-resources.mdx b/content/docs/android/guides/local-resources.mdx
new file mode 100644
index 00000000..35e76ed9
--- /dev/null
+++ b/content/docs/android/guides/local-resources.mdx
@@ -0,0 +1,6 @@
+---
+title: "Local Resources"
+description: "Bundle images, videos, and other assets in your app so paywalls can load them instantly from the device."
+---
+
+../../../shared/local-resources.mdx
diff --git a/content/docs/android/index.mdx b/content/docs/android/index.mdx
index d40980e4..f5b9aa7d 100644
--- a/content/docs/android/index.mdx
+++ b/content/docs/android/index.mdx
@@ -42,4 +42,4 @@ If you have feedback on any of our docs, please leave a rating and message at th
If you have any issues with the SDK, please [open an issue on GitHub](https://github.com/superwall/superwall-android/issues).
-
+
diff --git a/content/docs/android/meta.json b/content/docs/android/meta.json
index dabf6fc1..bf7fb351 100644
--- a/content/docs/android/meta.json
+++ b/content/docs/android/meta.json
@@ -31,6 +31,7 @@
"sdk-reference/identify",
"sdk-reference/setUserAttributes",
"sdk-reference/userId",
+ "sdk-reference/localResources",
"sdk-reference/subscriptionStatus",
"sdk-reference/handleDeepLink",
"sdk-reference/getPresentationResult",
@@ -46,6 +47,7 @@
"guides/vibe-coding",
"guides/using-revenuecat",
"guides/experimental-flags",
+ "guides/local-resources",
"guides/advanced",
"guides/migrations",
"[Troubleshooting](https://support.superwall.com/articles/3578026824-troubleshooting-android-sdk)",
diff --git a/content/docs/android/quickstart/install.mdx b/content/docs/android/quickstart/install.mdx
index a125183a..77d05490 100644
--- a/content/docs/android/quickstart/install.mdx
+++ b/content/docs/android/quickstart/install.mdx
@@ -20,16 +20,16 @@ can find the [latest release here](https://github.com/superwall/Superwall-Androi
```gradle build.gradle
-implementation "com.superwall.sdk:superwall-android:2.7.6"
+implementation "com.superwall.sdk:superwall-android:2.7.8"
```
```kotlin build.gradle.kts
-implementation("com.superwall.sdk:superwall-android:2.7.6")
+implementation("com.superwall.sdk:superwall-android:2.7.8")
```
```toml libs.version.toml
[libraries]
-superwall-android = { group = "com.superwall.sdk", name = "superwall-android", version = "2.7.6" }
+superwall-android = { group = "com.superwall.sdk", name = "superwall-android", version = "2.7.8" }
// And in your build.gradle.kts
dependencies {
diff --git a/content/docs/android/sdk-reference/Superwall.mdx b/content/docs/android/sdk-reference/Superwall.mdx
index 79aec6a8..330488bf 100644
--- a/content/docs/android/sdk-reference/Superwall.mdx
+++ b/content/docs/android/sdk-reference/Superwall.mdx
@@ -177,4 +177,4 @@ Superwall.getInstance().register("feature_access", () -> {
// Set user identity
Superwall.getInstance().identify("user123");
-```
\ No newline at end of file
+```
diff --git a/content/docs/android/sdk-reference/index.mdx b/content/docs/android/sdk-reference/index.mdx
index cac92b4a..27964550 100644
--- a/content/docs/android/sdk-reference/index.mdx
+++ b/content/docs/android/sdk-reference/index.mdx
@@ -15,4 +15,4 @@ If you have feedback on any of our docs, please leave a rating and message at th
If you have any issues with the SDK, please [open an issue on GitHub](https://github.com/superwall/superwall-android/issues).
-
+
diff --git a/content/docs/android/sdk-reference/localResources.mdx b/content/docs/android/sdk-reference/localResources.mdx
new file mode 100644
index 00000000..485b3208
--- /dev/null
+++ b/content/docs/android/sdk-reference/localResources.mdx
@@ -0,0 +1,65 @@
+---
+title: "localResources"
+description: "Register Android resources or file URIs so paywalls can load local media by resource ID."
+---
+
+
+Available in Android SDK `2.7.7+`.
+
+
+## Purpose
+
+`localResources` lets you map paywall asset IDs to local Android resources or file URIs. Paywalls can then request those assets with `swlocal://resource-id` instead of downloading them from a remote URL.
+
+## Signature
+
+```kotlin
+var localResources: Map
+```
+
+```java
+public Map getLocalResources()
+public void setLocalResources(Map localResources)
+```
+
+## Schema
+
+
+
+## PaywallResource variants
+
+
+
+## Returns / State
+
+This is a mutable property on [`Superwall.instance`](/android/sdk-reference/Superwall). Set it before presenting paywalls that depend on local assets.
+
+## Related
+
+- [Local Resources guide](/android/guides/local-resources)
diff --git a/content/docs/dashboard/dashboard-creating-paywalls/paywall-editor-local-resources.mdx b/content/docs/dashboard/dashboard-creating-paywalls/paywall-editor-local-resources.mdx
index ccc3d035..b11938b2 100644
--- a/content/docs/dashboard/dashboard-creating-paywalls/paywall-editor-local-resources.mdx
+++ b/content/docs/dashboard/dashboard-creating-paywalls/paywall-editor-local-resources.mdx
@@ -6,7 +6,7 @@ description: "Use images, videos, and other media bundled in your app for faster
Local resources let you reference media files (such as images and videos) that are bundled directly in your app rather than hosted on a remote server. This means faster load times, no network dependency for those assets, and a smoother experience for your users.
- Local resources require **iOS SDK v4.13.0+** or **Android SDK v2.5.0+**. They are not available on
+ Local resources require **iOS SDK v4.13.0+** or **Android SDK v2.7.7+**. They are not available on
other platforms at this time.
@@ -14,6 +14,8 @@ Local resources let you reference media files (such as images and videos) that a
Instead of pointing an image or video to a URL, you can point it to a **local resource ID**. This ID maps to a file that the developer has registered in the native SDK. When the paywall loads, the SDK intercepts the request and serves the file directly from the device. No network call is required.
+Set up those resource IDs in your app first by following the [SDK Local Resources guide](/sdk/guides/local-resources).
+
The editor discovers which resource IDs are available by looking at device attribute events your app has reported in the last 7 days. This means at least one device running your app with the SDK configured must have reported its local resources before they appear in the editor.
### Setting a local resource on an image
@@ -46,7 +48,7 @@ This means you can safely set a local resource without breaking the paywall for
The resource ID dropdown is populated from device attribute events sent by your app. If you don't see any resource IDs:
-- Make sure at least one test device is running your app with the local resources configured in the SDK (see the [iOS guide](/ios/guides/local-resources) for setup instructions).
+- Make sure at least one test device is running your app with the local resources configured in the SDK. For setup instructions, see the [SDK Local Resources guide](/sdk/guides/local-resources).
- The device must have opened a paywall or otherwise triggered a device attributes event within the **last 7 days**.
- Only **iOS** and **Android** platforms support local resources. The dropdown will not appear for other platforms.
@@ -68,6 +70,6 @@ For smaller or frequently changing images, remote URLs are still the simpler cho
### Related
-- [iOS SDK: Local Resources Guide](/ios/guides/local-resources): How to register local resources in the iOS SDK.
+- [SDK Local Resources Guide](/sdk/guides/local-resources): How to register local resources in supported SDKs.
- [Styling Elements](/paywall-editor-styling-elements): General component styling and image editing.
- [Liquid inside Image URLs](/paywall-editor-liquid#liquid-inside-image-urls): Using dynamic URLs for images.
diff --git a/content/docs/ios/guides/local-resources.mdx b/content/docs/ios/guides/local-resources.mdx
index 7c3a1eec..8afad3b3 100644
--- a/content/docs/ios/guides/local-resources.mdx
+++ b/content/docs/ios/guides/local-resources.mdx
@@ -3,83 +3,4 @@ title: "Local Resources"
description: "Bundle images, videos, and other media in your app for use in paywalls, enabling faster load times and offline support."
---
-Local resources allow you to register media files bundled in your app (images, videos, audio) so that paywalls can reference them by ID instead of loading them from a remote URL. The SDK serves these files directly from disk, which means instant loading and no network dependency.
-
-
- Local resources require **iOS SDK v4.13.0+**. Make sure you're on a compatible version before
- using this feature.
-
-
-## Registering local resources
-
-Set the `localResources` property on `SuperwallOptions` **before** calling `configure()`. Each entry maps a **resource ID** (a string you choose) to a local file URL:
-
-```swift Swift
-let options = SuperwallOptions()
-options.localResources = [
- "hero-image": Bundle.main.url(forResource: "hero", withExtension: "png")!,
- "onboarding-video": Bundle.main.url(forResource: "onboarding", withExtension: "mp4")!
-]
-
-Superwall.configure(apiKey: "pk_your_api_key", options: options)
-```
-
-The resource IDs you choose here are the same IDs you'll select in the [paywall editor](/dashboard/dashboard-creating-paywalls/paywall-editor-local-resources) when configuring an image or video component.
-
-
- Local resources must be set **before** calling `configure()`. Resources added after configuration
- will not be available for paywalls that have already loaded.
-
-
-## Supported file types
-
-The SDK supports a wide range of media formats:
-
-| Category | Formats |
-| -------- | ------- |
-| Images | PNG, JPEG, GIF, WebP, SVG, HEIC, HEIF, AVIF, BMP, TIFF |
-| Videos | MP4, MOV, WebM, AVI, HEVC/H.265 |
-
-## Choosing resource IDs
-
-Resource IDs are simple strings that act as the key between your app and the paywall editor. A few tips:
-
-- **Use descriptive names** like `"hero-image"` or `"onboarding-video"` rather than `"img1"`.
-- **Keep them stable.** If you change a resource ID, you'll need to update any paywalls that reference it in the editor.
-- **They're case-sensitive.** `"Hero-Image"` and `"hero-image"` are different IDs.
-
-## Fallback behavior
-
-In the paywall editor, you can set both a local resource and a remote URL on the same image or video component. If the local file can't be loaded (for example, the resource ID isn't registered or the file is missing from the bundle), the paywall automatically falls back to the remote URL. This ensures paywalls still work on older SDK versions or if a resource is accidentally removed from the app bundle.
-
-## Debugging
-
-The SDK includes a built-in debug view for verifying your local resources are set up correctly. It shows each registered resource ID, its file path, and a preview of the content.
-
-
- If a resource isn't showing up in the paywall editor dropdown, make sure your test device has
- opened a paywall (or otherwise triggered a device attributes event) after configuring
- `localResources`. The editor only shows resource IDs reported in the last 7 days.
-
-
-## Example: Onboarding paywall with a bundled video
-
-A common use case is bundling an onboarding video so it loads instantly the first time a user sees your paywall:
-
-```swift Swift
-// In your app's initialization
-let options = SuperwallOptions()
-options.localResources = [
- "onboarding-video": Bundle.main.url(forResource: "welcome", withExtension: "mp4")!,
- "app-logo": Bundle.main.url(forResource: "logo", withExtension: "png")!
-]
-
-Superwall.configure(apiKey: "pk_your_api_key", options: options)
-```
-
-Then in the paywall editor, select "onboarding-video" as the local resource for your video component and "app-logo" for the logo image. Set remote URLs as fallbacks for both.
-
-## Related
-
-- [`SuperwallOptions`](/ios/sdk-reference/SuperwallOptions): Full configuration reference.
-- [Paywall Editor: Local Resources](/dashboard/dashboard-creating-paywalls/paywall-editor-local-resources): How to use local resources in the paywall editor.
+../../../shared/local-resources.mdx
diff --git a/content/docs/ios/meta.json b/content/docs/ios/meta.json
index 4c432f4d..3ddcb55a 100644
--- a/content/docs/ios/meta.json
+++ b/content/docs/ios/meta.json
@@ -49,6 +49,7 @@
"sdk-reference/PurchaseController",
"sdk-reference/SuperwallEvent",
"sdk-reference/SuperwallOptions",
+ "sdk-reference/localResources",
"sdk-reference/PaywallOptions",
"sdk-reference/advanced",
diff --git a/content/docs/ios/sdk-reference/SuperwallOptions.mdx b/content/docs/ios/sdk-reference/SuperwallOptions.mdx
index a251b1fe..92d05b0f 100644
--- a/content/docs/ios/sdk-reference/SuperwallOptions.mdx
+++ b/content/docs/ios/sdk-reference/SuperwallOptions.mdx
@@ -75,7 +75,7 @@ public final class SuperwallOptions: NSObject {
localResources: {
type: "[String: URL]",
description:
- "A dictionary mapping resource IDs to local file URLs. Paywalls can reference these IDs to load images, videos, and other media directly from the app bundle instead of a remote URL. Must be set before calling `configure()`. See the [Local Resources guide](/ios/guides/local-resources) for details. Available in version 4.13.0+.",
+ "A dictionary mapping resource IDs to local file URLs. See [`localResources`](/ios/sdk-reference/localResources) for the property schema and the [Local Resources guide](/ios/guides/local-resources) for setup details.",
default: "[:]",
},
}}
@@ -191,28 +191,10 @@ func configureSuperwallForDebug() {
}
```
-Local resources configuration:
-
-```swift
-let options = SuperwallOptions()
-
-// Register bundled media for use in paywalls
-options.localResources = [
- "hero-image": Bundle.main.url(forResource: "hero", withExtension: "png")!,
- "onboarding-video": Bundle.main.url(forResource: "onboarding", withExtension: "mp4")!
-]
-
-Superwall.configure(
- apiKey: "pk_your_api_key",
- options: options
-)
-```
-
-See the [Local Resources guide](/ios/guides/local-resources) for a full walkthrough.
-
## Related
- [`PaywallOptions`](/ios/sdk-reference/PaywallOptions)
+- [`localResources`](/ios/sdk-reference/localResources)
## Runtime Interface Style Configuration
diff --git a/content/docs/ios/sdk-reference/localResources.mdx b/content/docs/ios/sdk-reference/localResources.mdx
new file mode 100644
index 00000000..160353e0
--- /dev/null
+++ b/content/docs/ios/sdk-reference/localResources.mdx
@@ -0,0 +1,43 @@
+---
+title: "localResources"
+description: "Register local file URLs so paywalls can load bundled media by resource ID."
+---
+
+
+Available in iOS SDK `4.13.0+`.
+
+
+## Purpose
+
+`localResources` maps resource IDs to local file URLs so paywalls can request bundled media with `swlocal://resource-id` instead of downloading them from a remote URL.
+
+## Signature
+
+```swift
+public var localResources: [String: URL]
+```
+
+## Schema
+
+
+
+## Returns / State
+
+This is a mutable property on [`SuperwallOptions`](/ios/sdk-reference/SuperwallOptions). Set it before calling [`configure()`](/ios/sdk-reference/configure).
+
+## Related
+
+- [Local Resources guide](/ios/guides/local-resources)
diff --git a/content/shared/local-resources.mdx b/content/shared/local-resources.mdx
new file mode 100644
index 00000000..f7cc882c
--- /dev/null
+++ b/content/shared/local-resources.mdx
@@ -0,0 +1,133 @@
+---
+title: "Local Resources"
+description: "Bundle images, videos, and other assets in your app so paywalls can load them instantly from the device."
+---
+
+Local resources let your paywalls load bundled assets directly from the device instead of fetching them over the network. This is useful for hero images, onboarding videos, and other media that should appear immediately even when the connection is slow.
+
+:::ios
+
+ Local resources require **iOS SDK v4.13.0+**.
+
+:::
+
+:::android
+
+ Local resources require **Android SDK v2.7.7+**.
+
+:::
+
+## Registering local resources
+
+Choose a stable resource ID for each asset you want to serve locally. That same ID is what you'll select in the [paywall editor](/dashboard/dashboard-creating-paywalls/paywall-editor-local-resources) when configuring image or video components.
+
+:::ios
+On iOS, local resources are configured on `SuperwallOptions.localResources` before calling [`configure()`](/ios/sdk-reference/configure).
+
+```swift Swift
+let options = SuperwallOptions()
+options.localResources = [
+ "hero-image": Bundle.main.url(forResource: "hero", withExtension: "png")!,
+ "onboarding-video": Bundle.main.url(forResource: "welcome", withExtension: "mp4")!
+]
+
+Superwall.configure(
+ apiKey: "pk_your_api_key",
+ options: options
+)
+```
+
+
+ Set `localResources` before calling `configure()`. Resources added later will not be available to
+ paywalls that already loaded.
+
+:::
+
+:::android
+On Android, local resources are configured on `Superwall.instance.localResources` after calling [`configure()`](/android/sdk-reference/configure), and before presenting paywalls that depend on those assets.
+
+```kotlin Kotlin
+import android.app.Application
+import android.net.Uri
+import com.superwall.sdk.Superwall
+import com.superwall.sdk.paywall.view.webview.PaywallResource
+import java.io.File
+
+class MyApplication : Application() {
+ override fun onCreate() {
+ super.onCreate()
+
+ Superwall.configure(
+ application = this,
+ apiKey = "pk_your_api_key",
+ )
+
+ Superwall.instance.localResources = mapOf(
+ "hero-image" to PaywallResource.FromResources(R.drawable.hero),
+ "onboarding-video" to PaywallResource.FromUri(
+ Uri.fromFile(File(filesDir, "welcome.mp4"))
+ ),
+ "background-animation" to PaywallResource.FromResources(R.raw.paywall_bg)
+ )
+ }
+}
+```
+
+
+ Set `localResources` before presenting paywalls that depend on those assets. Updating the map later
+ only affects paywalls loaded after the change.
+
+:::
+
+## Supported source types
+
+:::ios
+iOS maps each resource ID to a local file URL:
+
+| Type | Use for |
+| ---- | ------- |
+| `URL` | Files in your app bundle or sandbox that can be loaded directly from disk |
+:::
+
+:::android
+Android supports two local resource source types:
+
+| Type | Use for |
+| ---- | ------- |
+| `PaywallResource.FromResources(resId)` | Assets packaged in `res/drawable`, `res/raw`, and other Android resource folders |
+| `PaywallResource.FromUri(uri)` | Files addressed by a `Uri`, such as files in app storage or content provider URLs |
+:::
+
+## Choosing resource IDs
+
+Resource IDs are the contract between your app and the paywall editor. A few guidelines:
+
+- Use stable, descriptive names like `"hero-image"` and `"onboarding-video"`.
+- Keep the casing consistent. `"Hero-Image"` and `"hero-image"` are different IDs.
+- If you rename an ID, update any paywalls that reference it.
+
+## Referencing local resources in a paywall
+
+In the paywall editor, set a local resource on an image or video component and select the resource ID you registered in the SDK. You can still provide a remote URL as a fallback.
+
+Under the hood, paywalls load these resources through `swlocal://` URLs. For example:
+
+```html
+
+```
+
+If the SDK cannot resolve a local resource, the paywall can fall back to the remote URL configured in the editor.
+
+## Debugging
+
+If a resource ID does not appear in the editor or fails to load:
+
+- Make sure the app is running a compatible SDK version.
+- Confirm the resource ID in your paywall exactly matches the key you registered in the SDK.
+- Open a paywall on a test device after configuring local resources so the editor can discover recently used IDs.
+- Keep a remote fallback URL on critical media so older builds still render correctly.
+
+## Related
+
+- [`localResources`](/sdk/sdk-reference/localResources): SDK reference for the property on supported platforms.
+- [Paywall Editor: Local Resources](/dashboard/dashboard-creating-paywalls/paywall-editor-local-resources): How to assign local resource IDs in the dashboard.