Skip to content

feat!: harden iOS and Android SDKs for 1.0.0 - #27

Merged
gcg merged 7 commits into
mainfrom
release/v1.0.0-hardening
Oct 5, 2026
Merged

gcg merged 7 commits into
mainfrom
release/v1.0.0-hardening

Conversation

@gcg

@gcg gcg commented Oct 2, 2026

Copy link
Copy Markdown
Member

Final pre-1.0 pass over both native SDKs. It locks the public API to what we intend to support, fixes host-app crash and privacy risks, and makes CI actually compile the iOS UI layer.

⚠️ Breaking changes (why this should be the major release)

  • iOS public API reduced to PoltioSDK, PoltioLogLevel and PoltioPurchaseItem. PoltioOverlayManager, PoltioWebViewController, PoltioPassthroughWindow, the trigger views, PoltioWidgetResponse/PoltioOverlayOptions, CachedWidgetResult, PoltioLogger and AnyCodable are now internal. AnyCodable was renamed to PoltioAnyCodable so it can't clash with the popular AnyCodable package. The new PoltioSDK.hideTrigger() replaces PoltioOverlayManager.shared.hideTrigger(). The instance-level cacheTTL/cacheLimit/clearCache() are internal; use the static ones.
  • Android public API reduced: PoltioLogger, PoltioWidgetResponse, PoltioOverlayOptions and PoltioWebViewActivity are internal.
  • Android PoltioSDK.onWidgetEvent is now a PoltioWidgetEventListener fun interface instead of a Kotlin function type: PoltioSDK.onWidgetEvent = PoltioWidgetEventListener { event, data -> }.
  • The default log level is warning on both platforms (was info).

Changes

  1. API surface: see above.
  2. Java interop (Android): @JvmStatic on every PoltioSDK member, and @JvmOverloads on configure/track/recordPurchase/PoltioPurchaseItem. A Java test (PoltioSDKJavaInteropTest) compiles against the API the way a Java host would.
  3. Android renderer crash: onRenderProcessGone now closes the sheet instead of letting Android kill the host process.
  4. iOS WebView navigation policy, matching Android. Only *.poltio.com loads in the sheet. External links, target="_blank"/window.open, tel:/mailto: and deep links go to the system. The sheet reloads if the web content process is terminated. Both platforms share a tested isTrustedWidgetURL check.
  5. iOS privacy manifest: declares UserDefaults (CA92.1) and the collected data types (Device ID, User ID, Purchase History, Product Interaction), all with tracking = false. Documented in docs/IOS.md. Please sanity-check the linked/purpose choices against our privacy policy.
  6. CI and Makefile:
    • make test-ios now runs on an iOS Simulator. Previously, swift test on macOS compiled out the whole UIKit layer: 58 tests instead of 46 run now, 62 with the new ones.
    • make build-ios also builds for the simulator SDK.
    • New make test-ios-macos keeps the quick host-only run.
    • New lint-pod CI job (pod lib lint). The podspec moved to the repo root so local lint can resolve paths; the publish workflow and bump script are updated.
  7. Repo hygiene: untracked ~94 MB / ~2,200 committed example/ios/.build + .swiftpm files. They're already gitignored. History is not rewritten.
  8. Android main-thread I/O: configure/track/identify/recordPurchase do no disk I/O on the caller's thread. Work runs on a serial background executor, so view events keep their call order.
  9. Logging: iOS uses os_log (subsystem com.poltio.sdk) instead of print. PUID, sdk_id and event params are only logged at debug.
  10. SDK version headers: every request sends X-Poltio-SDK-Version and X-Poltio-SDK-Platform, and PoltioSDK.version is public. Android takes the version from the published Maven version; iOS reads PoltioSDKInfo.version, which make version now bumps.
  11. track() docs: non-view events are documented as local-only. The iOS example's "TrackConversion" button now calls recordPurchase.
  12. Android sheet insets: the sheet is edge-to-edge and applies the navigation-bar and keyboard (IME) insets, so lead-form inputs stay above the keyboard.
  13. Accessibility: triggers expose a button role, a label built from their visible text, and close/collapse custom actions on iOS. With VoiceOver or TalkBack on, activating a collapsed trigger opens the widget directly.
  14. Android activity tracking: if configure() is called late with an Activity, that Activity is used for triggers right away. Dismissals are persisted against the application context.

Small fixes: iOS force unwraps in recordPurchase removed; view request-ID race fixed on both platforms; consumer R8 rule keeps @JavascriptInterface methods.

Not in this PR

  • The "Close" and fallback accessibility strings are still English-only (not localized).
  • No in-memory icon cache and no Reduce Motion handling for the pulse animation.
  • iOS still uses the deprecated UIApplication.shared.windows/keyWindow fallbacks.

Release notes

  • iOS version constant: for future releases, run make version VERSION=x.y.z before merging. SPM consumers build from the tagged source, so PoltioSDKInfo.version must already be correct at tag time. It's set to 1.0.0 here.
  • Backend: check that the API accepts the two new request headers (they're plain custom headers).

Testing

  • make test-ios: 62/62 on an iOS Simulator
  • make test-ios-macos: passes
  • make test-android: 81/81 (debug + release)
  • make lint-ios, make lint-android, make lint-pod, make lint-actions: all clean
  • make build-ios, make build-android, make build-example-ios, make build-example-android: all succeed
  • Not yet checked on a device: the Android keyboard insets with a real lead form, TalkBack/VoiceOver behaviour, and iOS external-link handoff.

🤖 Generated with Claude Code

BREAKING CHANGE: the public API is reduced to the supported entry points.
iOS: PoltioOverlayManager, PoltioWebViewController, PoltioPassthroughWindow,
the trigger views, PoltioWidgetResponse/PoltioOverlayOptions,
CachedWidgetResult, PoltioLogger and AnyCodable are now internal (use the new
PoltioSDK.hideTrigger()). Android: PoltioLogger, PoltioWidgetResponse,
PoltioOverlayOptions and PoltioWebViewActivity are internal, and
PoltioSDK.onWidgetEvent is now a PoltioWidgetEventListener fun interface.
The default log level on both platforms is now WARNING.

- Android: @JvmStatic/@jvmoverloads across PoltioSDK and PoltioPurchaseItem
- Android: handle WebView renderer crashes instead of killing the host app
- iOS: WebView navigation policy matching Android (Poltio domains only;
  external links, target=_blank and deep links go to the system); reload on
  web content process termination
- iOS: privacy manifest declares UserDefaults (CA92.1) and collected data
- CI/Makefile: build and test iOS on the simulator so the UIKit layer is
  compiled and tested; add pod lib lint (podspec moved to repo root)
- Untrack committed example/ios build artifacts (~94 MB)
- Android: no main-thread disk I/O in configure/track/identify (serial
  background executor)
- iOS logs via os_log; identifiers and params only logged at debug
- Send X-Poltio-SDK-Version / X-Poltio-SDK-Platform headers; expose
  PoltioSDK.version
- Document that non-view track() events are not sent to the API
- Android sheet applies navigation bar and keyboard insets (edge-to-edge)
- Trigger accessibility: button role, labels, close/collapse actions, and
  screen-reader activation opens the widget directly
- Android: seed the current Activity when configure() is called late;
  persist dismissals against the application context
- Small fixes: iOS force unwraps, view request-id race, consumer R8 rule
  for the JS bridge

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces major enhancements to the Poltio Mobile SDK, including robust Java interop support, off-main-thread execution of disk and network operations via a new serial executor, TalkBack accessibility configurations for floating triggers, and WebView stability improvements such as edge-to-edge layouts and renderer crash handling. The code review highlights a security vulnerability where blob: and data: schemes bypass trusted URL checks, a consistency issue in PoltioFloatingBoxTriggerView when handling clicks in accessibility mode, and a bug where user identifiers (PUID) set prior to SDK configuration fail to persist to disk.

Comment thread android/poltio-sdk/src/main/java/com/poltio/sdk/PoltioSDK.kt
@gcg gcg self-assigned this Oct 2, 2026
@gcg gcg added the major Some stuff will break label Oct 2, 2026
…dget directly from box tab, persist early PUID

- Only about:blank (besides https Poltio hosts) is trusted on both platforms;
  data:/blob: documents could otherwise run script with bridge access
- Box trigger's accessibility shortcut calls onOpenWidget() directly, like
  card/pill, instead of performClick() on the expanded container
- A PUID set via identify() before configure() is now written to prefs (or
  cleared) once configure() provides a Context

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@gcg

gcg commented Oct 2, 2026

Copy link
Copy Markdown
Member Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the Poltio Mobile SDK to version 1.0.0, introducing improved background thread execution for disk I/O, enhanced Java interop, TalkBack accessibility support for floating triggers, and edge-to-edge layout handling in the WebView activity. Feedback identifies a critical security vulnerability in the trusted URL validation logic where a parser differential between android.net.Uri and Chromium could allow host validation bypass. Additionally, it is recommended to explicitly enable buildConfig in the Gradle build features to prevent compilation failures under newer Android Gradle Plugin versions.

Comment thread android/poltio-sdk/build.gradle.kts
android.net.Uri/Foundation and the WebView engines don't parse authorities
identically (e.g. Chromium treats a backslash as '/'), so a host such as
evil.com\.poltio.com could pass the .poltio.com suffix check yet load
evil.com. Only plain [a-z0-9.-] hostnames are now trusted, on both platforms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@gcg

gcg commented Oct 2, 2026

Copy link
Copy Markdown
Member Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the Poltio Android SDK to improve Java interoperability, offload disk and network operations to a background serial executor, and enhance the WebView activity with edge-to-edge layout, robust URL validation, and renderer crash handling. It also introduces accessibility support for floating triggers. The review feedback highlights a potential race condition with concurrent requests, recommends unwrapping wrapped contexts to properly resolve activities, suggests using the application context to prevent memory leaks, and points out a missing import for the Build class.

Comment thread android/poltio-sdk/src/main/java/com/poltio/sdk/PoltioSDK.kt
Comment thread android/poltio-sdk/src/main/java/com/poltio/sdk/PoltioSDK.kt Outdated
Comment thread android/poltio-sdk/src/main/java/com/poltio/sdk/ui/PoltioOverlayManager.kt Outdated
…gure()

Also check trigger dismissals against the application context, matching how
they are recorded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@gcg

gcg commented Oct 2, 2026

Copy link
Copy Markdown
Member Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces significant updates to the Poltio Mobile SDK, focusing on Java interop, background thread safety, accessibility, and security. Key changes include preloading identifiers on a background thread, adding JvmStatic and JvmOverloads annotations, introducing TalkBack support for floating triggers, and hardening the WebView with trusted URL validation and crash handling. Feedback on these changes highlights two main areas for improvement: using locale-invariant case conversion (Locale.ROOT) when validating trusted widget URLs to prevent locale-specific bugs, and preserving existing left, top, and right padding values when setting the bottom padding on the sheet view.

Comment thread android/poltio-sdk/src/main/java/com/poltio/sdk/ui/PoltioWebViewActivity.kt Outdated
Also pin the trusted-URL check's locale invariance with a tr-TR test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@gcg

gcg commented Oct 2, 2026

Copy link
Copy Markdown
Member Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces significant updates to the Poltio Mobile SDK, focusing on Java interoperability, background thread offloading for state persistence, edge-to-edge layout support, and accessibility improvements for floating triggers. Key changes include refactoring the Android SDK to use a serial background executor, adding ProGuard rules, implementing robust WebView crash handling, and introducing domain validation for trusted widget URLs. Feedback on these changes highlights a potential issue with weak reference checks when seeding the current activity, a security vulnerability from allowing unencrypted HTTP schemes in trusted URLs, and a missing callback notification to the host app when the WebView renderer process crashes.

Comment thread android/poltio-sdk/src/main/java/com/poltio/sdk/ui/PoltioOverlayManager.kt Outdated
Comment thread android/poltio-sdk/src/main/java/com/poltio/sdk/ui/PoltioWebViewActivity.kt Outdated
gcg and others added 2 commits October 2, 2026 10:16
- Widget WebView trust check accepts https only (both platforms), so a
  cleartext page can't be modified in transit to reach the JS bridge
- seedCurrentActivity replaces a weakly-held Activity that is already
  finishing/destroyed but not yet garbage-collected

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SVG trigger icons render in a small WebView, and WebView consumes every
touch in onTouchEvent even with isClickable = false. On the collapsed pill
the icon covers most of the tap target, so the pill could not be tapped or
expanded. The icon now uses PoltioNonInteractiveWebView, which never handles
touches (the Android analogue of iOS's isUserInteractionEnabled = false), and
is hidden from TalkBack since the trigger already carries the label.

Also warn in `make run-example-android` when the running emulator is
headless (-no-window), since the app then launches with no visible window.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@gcg
gcg requested review from a team, cantugdonmez, hionay and onurhanavci October 2, 2026 07:47
@gcg
gcg merged commit 124a886 into main Oct 5, 2026
7 checks passed
@gcg
gcg deleted the release/v1.0.0-hardening branch October 5, 2026 06:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

major Some stuff will break

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants