Skip to content

SDK-31 Fix full-position IAM safe area handling#996

Merged
joaodordio merged 11 commits intomasterfrom
feature/SDK-31-fix-iam-full-position-safe-area
Jan 29, 2026
Merged

SDK-31 Fix full-position IAM safe area handling#996
joaodordio merged 11 commits intomasterfrom
feature/SDK-31-fix-iam-full-position-safe-area

Conversation

@sumeruchat
Copy link
Copy Markdown
Contributor

@sumeruchat sumeruchat commented Jan 14, 2026

What

Fixes the safe area gap (behind the status bar / Dynamic Island / home indicator) for full-position in-app messages. The background now extends edge-to-edge, matching the HTML content's background.

Approach

Uses standard .overFullScreen modal presentation (already set in InAppDisplayer) for all message types. For full-position IAMs, two things ensure the webview content fills the safe area:

  1. viewport-fit=cover injection — The SDK injects viewport-fit=cover into the HTML's viewport meta tag before loading. This tells WkWebView the content wants to extend into the safe area. Handles existing viewport tags (appends to content), missing viewport tags (inserts into <head>), and HTML without <head> (prepends). Supports both single and double quotes in HTML attributes.

  2. contentInsetAdjustmentBehavior = .never — Prevents the WKWebView's scroll view from automatically insetting content away from the safe area, which would override the viewport-fit directive.

  3. Background color fallback — Sets the VC's view.backgroundColor to the message's background color (when available) for full-position IAMs, so any remaining safe area gaps are filled with the correct color during load/animation.

  4. Animation initial bgColor — For full-position messages, the animation starts with the message's background color instead of .clear, preventing a transparent-to-colored flash during fade-in.

Why this doesn't break non-full-position messages

All changes are gated on location == .full or input.location == .full:

  • injectViewportFitCover: Only called when location == .full. Non-full HTML is loaded unmodified.
  • contentInsetAdjustmentBehavior: Only set to .never when location == .full. Non-full webviews keep the default behavior.
  • view.backgroundColor in loadView(): Only changed for .full with a non-nil backgroundColor. Non-full messages use the same initialViewBackgroundColor(isModal:) as before.
  • calculateAnimationDetail: initialBgColor only differs from .clear when input.location == .full. The existing animation logic for top/center/bottom is completely unchanged.

Changes

  • IterableHtmlMessageViewController.swift — Added injectViewportFitCover(html:) for viewport-fit injection (supports both single and double quotes), contentInsetAdjustmentBehavior = .never for full-position, view.backgroundColor set to message bgColor for full-position.
  • InAppCalculations.swift — Fixed animation initial bgColor for .full to use message's background color instead of .clear, preventing transparent flash during fade-in.
  • Test app — Added POC button and full-screen test view controller for manual verification.

Testing

Unit tests: All 97 tests pass (InAppTests, InAppPresenterTests, InAppParsingTests, IterableHtmlMessageViewControllerTests).

Manual testing:

  1. Build and run the integration test app
  2. Trigger a full-position IAM — verify the background extends edge-to-edge behind the status bar / Dynamic Island
  3. Test the "Local Full-Screen IAM (POC)" button — verify identical edge-to-edge behavior
  4. Test non-full-position messages (top, center, bottom) — verify no regression in positioning or animation

sumchattering and others added 2 commits January 14, 2026 07:27
* 'master' of github-iterable:Iterable/iterable-swift-sdk:
  SDK-218: BCIT End-to-End Deep Link Routing Tests (#993)
Apply safe area insets to WKWebView scrollView content for full-position
in-app messages. This ensures content is not hidden behind notch, status
bar, or home indicator while maintaining edge-to-edge background coverage.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@codecov
Copy link
Copy Markdown

codecov Bot commented Jan 14, 2026

Codecov Report

❌ Patch coverage is 98.03922% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 70.12%. Comparing base (0cd2c62) to head (7a9ba67).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
...k/Internal/IterableHtmlMessageViewController.swift 97.87% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #996      +/-   ##
==========================================
+ Coverage   69.78%   70.12%   +0.33%     
==========================================
  Files         111      111              
  Lines        8983     9030      +47     
==========================================
+ Hits         6269     6332      +63     
+ Misses       2714     2698      -16     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@joaodordio
Copy link
Copy Markdown
Member

CleanShot 2026-01-27 at 15 34 57@2x

Safe area behind the notch and status bar not working.

sumchattering and others added 2 commits January 29, 2026 12:39
…tInsetAdjustment

Replace overlay UIWindow approach with standard .overFullScreen modal presentation.
For full-position IAMs, inject viewport-fit=cover into the HTML and set
contentInsetAdjustmentBehavior=.never so the webview content extends behind
the safe area (status bar / Dynamic Island / home indicator). Set the VC's
view.backgroundColor to the message's background color as a fallback.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@sumeruchat
Copy link
Copy Markdown
Contributor Author

Simulator Screenshot - iPhone 16e - 2026-01-29 at 16 18 43 ![Uploading Simulator Screenshot - iPad Air 11-inch (M3) - 2026-01-29 at 16.49.40.png…]() Tested on both iphone and ipad

Copy link
Copy Markdown
Member

@joaodordio joaodordio left a comment

Choose a reason for hiding this comment

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

Good solution on the HTML injection!

Marking this as Request Changes so we are 100% sure on the regex comment.

Other comments:
The PR description needs to be updated, the file InAppPresenter.swift only has whitespaces changes and the description mentions other changes.
Same thing for InAppCalculations.swift which mentions "Removed overlay window dismisser" but I don't see that.

}

// Has a viewport meta tag — append viewport-fit=cover to its content
if let range = html.range(of: #"(<meta\s+name\s*=\s*"viewport"\s+content\s*=\s*")"#,
Copy link
Copy Markdown
Member

@joaodordio joaodordio Jan 29, 2026

Choose a reason for hiding this comment

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

This regex is using double quotes, htmk with content='...' won't work.

Do we know for sure that the HTML we get on the in-app uses "" and never ''


class InAppPresenter {
static var isPresenting = false
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nit pick: This file was probably previously modified but now just has whitespaces changes.

The viewport injection now handles both:
- content="..." (double quotes)
- content='...' (single quotes)

Extracts the quote character from the regex match and uses it to find the matching closing quote.
@sumeruchat
Copy link
Copy Markdown
Contributor Author

@joaodordio Fixed both issues.

Regex now supports single and double quotes. Updated the pattern to match both content="..." and content='...', extracts the quote character, and finds the matching closing quote.

Removed whitespace changes from InAppPresenter.swift and updated PR description to reflect actual changes only.

Copy link
Copy Markdown
Member

@joaodordio joaodordio left a comment

Choose a reason for hiding this comment

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

LGTM

… bgColor

Cover all 5 code paths of the viewport-fit injection (already present,
name-first meta, content-first meta, no viewport meta with head, no head
tag) plus edge cases (idempotency, empty string, non-viewport meta).
Also verify calculateAnimationDetail uses the actual background color for
.full location and .clear for other positions.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@joaodordio joaodordio merged commit 4cd08e1 into master Jan 29, 2026
14 checks passed
@joaodordio joaodordio deleted the feature/SDK-31-fix-iam-full-position-safe-area branch January 29, 2026 18:10
sumeruchat added a commit that referenced this pull request Feb 19, 2026
Co-authored-by: Sumeru Chatterjee <sumeru.chatterjee@me.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants