Skip to content

fix: [SDK-5654] lay out IAMs for iPhone Duo and resizable scenes - #1789

Open
fadi-george wants to merge 19 commits into
iam-fixesfrom
fadi/sdk-5654
Open

fadi-george wants to merge 19 commits into
iam-fixesfrom
fadi/sdk-5654

Conversation

@fadi-george

@fadi-george fadi-george commented Nov 3, 2026 •

Copy link
Copy Markdown
Collaborator

Description

One Line Summary

Lay out IAMs correctly on iPhone Duo (folded and unfolded) and other resizable scenes, keeping them clear of side safe areas and hardware.

Details

Motivation

On the iPhone Duo, UIScreen bounds report a single panel while UIKit sizes windows to the scene, so IAMs were sized and positioned for the wrong display. Banners also overlapped the side camera in landscape, and full-bleed IAMs left the status bar visible when unfolded.

Scope

  • UIApplication displayBounds uses the window scene's bounds instead of UIScreen bounds for the IAM window, webview, and layout.
  • Each side uses the larger of the margin and the safe area inset (not their sum). On iOS 27.1+, occlusion reserved regions inside a side safe area (e.g. a foldable's camera) also keep the margin clear, matching system notification banners. Insets are updated on every layout pass. The release toolchain (Xcode 15.2) predates that API, so it's declared locally and looked up at runtime; SDK-5660 tracks bumping the release Xcode and calling it directly.
  • Top and bottom use the same max(margin, safe area) through a vertical layout guide.
  • Banner window sizes come from the app's full-screen window, since the IAM window's own insets shrink once banner sized and change mid-transition.
  • addConstraintsForMessage removes previous messageView constraints before adding new ones, so a stale initial Y constraint can't keep the message offscreen after a size transition.
  • viewDidAppear refreshes status bar appearance, since resizable scenes don't re-query prefersStatusBarHidden when the IAM window becomes key.
  • Demo app: fills the top inset with the nav bar color.

Out of scope: full screen "no margin" and default HTML close buttons landing on the camera. The SDK already passes the safe area (which covers the camera) through setSafeAreaInsets; the templates ignore the side insets. Tracked in SDK-5657.

No public API changes. Behavior on non-resizable iPhones is unchanged except margins now use max(margin, safe area) instead of their sum.

Note: I did adjust the padding for safe area so things like distance to status bar (right or bottom) now are smaller.

Before:

Screenshot 2026-11-02 at 6 37 03 PM Screenshot 2026-11-02 at 6 36 45 PM

After:

Screenshot 2026-11-02 at 6 37 26 PM Screenshot 2026-11-02 at 6 37 35 PM

Testing

Manual testing

Captured with the IAM screenshot tool (capture.sh --all), covering every IAM type (top/bottom/center/full-screen, with and without margins, carousel) plus push banners on the demo app:

  • iPhone Duo (iOS 27.1 sim): folded portrait, landscape-left, landscape-right; unfolded landscape and portrait.
  • iPhone 18 Pro (iOS 27.0 sim): portrait, landscape-left, landscape-right.
  • iPad Pro 11-inch M5 sim: portrait and landscape.

Also checked rotating and folding/unfolding while an IAM is shown.

Screenshots

iPhone 18 Pro

Orientation Notification Prompt
                                                                                   
Simple Notification
                                                                                   
Image Notification
                                                                                   
IAM Top
                                                                                   
IAM Bottom
                                                                                   
IAM Center
                                                                                   
IAM Full Screen
                                                                                   
IAM Full Screen Bleed
                                                                                   
IAM Carousel
                                                                                   
IAM Carousel Transition
                                                                                   
IAM Carousel Bleed
                                                                                   
IAM Carousel Bleed Transition
                                                                                   
IAM HTML
                                                                                   
Portrait 1-notif-prompt 2-notif-simple 3-notif-image 4-iam-top 5-iam-bottom 6-iam-center 7-iam-full 8-full 9-carousel 9-carousel-trans 10-carousel-bleed 10-carousel-bleed-trans 11-html
Landscape Left 1-notif-prompt 2-notif-simple 3-notif-image 4-iam-top 5-iam-bottom 6-iam-center 7-iam-full 8-full 9-carousel 9-carousel-trans 10-carousel-bleed 10-carousel-bleed-trans 11-html
Landscape Right 1-notif-prompt 2-notif-simple 3-notif-image 4-iam-top 5-iam-bottom 6-iam-center 7-iam-full 8-full 9-carousel 9-carousel-trans 10-carousel-bleed 10-carousel-bleed-trans 11-html

iPad Pro 11-inch

Orientation Notification Prompt
                                                                                   
Simple Notification
                                                                                   
Image Notification
                                                                                   
IAM Top
                                                                                   
IAM Bottom
                                                                                   
IAM Center
                                                                                   
IAM Full Screen
                                                                                   
IAM Full Screen Bleed
                                                                                   
IAM Carousel
                                                                                   
IAM Carousel Transition
                                                                                   
IAM Carousel Bleed
                                                                                   
IAM Carousel Bleed Transition
                                                                                   
IAM HTML
                                                                                   
Portrait 1-notif-prompt 2-notif-simple 3-notif-image 4-iam-top 5-iam-bottom 6-iam-center 7-iam-full 8-full 9-carousel 9-carousel-trans 10-carousel-bleed 10-carousel-bleed-trans 11-html
Landscape 1-notif-prompt 2-notif-simple 3-notif-image 4-iam-top 5-iam-bottom 6-iam-center 7-iam-full 8-full 9-carousel 9-carousel-trans 10-carousel-bleed 10-carousel-bleed-trans 11-html

iPhone Duo

Orientation Notification Prompt
                                                                                   
Simple Notification
                                                                                   
Image Notification
                                                                                   
IAM Top
                                                                                   
IAM Bottom
                                                                                   
IAM Center
                                                                                   
IAM Full Screen
                                                                                   
IAM Full Screen Bleed
                                                                                   
IAM Carousel
                                                                                   
IAM Carousel Transition
                                                                                   
IAM Carousel Bleed
                                                                                   
IAM Carousel Bleed Transition
                                                                                   
IAM HTML
                                                                                   
Folded Portrait 1-notif-prompt 2-notif-simple 3-notif-image 4-iam-top 5-iam-bottom 6-iam-center 7-iam-full 8-full 9-carousel 9-carousel-trans 10-carousel-bleed 10-carousel-bleed-trans 11-html
Folded Landscape Left 1-notif-prompt 2-notif-simple 3-notif-image 4-iam-top 5-iam-bottom 6-iam-center 7-iam-full 8-full 9-carousel 9-carousel-trans 10-carousel-bleed 10-carousel-bleed-trans 11-html
Folded Landscape Right 1-notif-prompt 2-notif-simple 3-notif-image 4-iam-top 5-iam-bottom 6-iam-center 7-iam-full 8-full 9-carousel 9-carousel-trans 10-carousel-bleed 10-carousel-bleed-trans 11-html
Unfolded Portrait 1-notif-prompt 2-notif-simple 3-notif-image 4-iam-top 5-iam-bottom 6-iam-center 7-iam-full 8-full 9-carousel 9-carousel-trans 10-carousel-bleed 10-carousel-bleed-trans 11-html
Unfolded Landscape 1-notif-prompt 2-notif-simple 3-notif-image 4-iam-top 5-iam-bottom 6-iam-center 7-iam-full 8-full 9-carousel 9-carousel-trans 10-carousel-bleed 10-carousel-bleed-trans 11-html

Affected code checklist

  • Notifications
    • Display
    • Open
    • Push Processing
    • Confirm Deliveries
  • Outcomes
  • Sessions
  • In-App Messaging
  • REST API requests
  • Public API changes

Checklist

Overview

  • I have filled out all REQUIRED sections above
  • PR does one thing
  • Any Public API changes are explained in the PR details and conform to existing APIs

Testing

  • I have included test coverage for these changes, or explained why they are not needed
    • Layout depends on real scene geometry and safe areas, verified on simulators above
  • All automated tests pass, or I explained why that is not possible
  • I have personally tested this on my device, or explained why that is not possible

Final pass

  • Code is as readable as possible.
  • I have reviewed this PR myself, ensuring it meets each checklist item

🤖 Generated with Claude Code

fadi-george and others added 12 commits October 30, 2026 13:55
Size the IAM window and WebView from the window scene bounds instead of
UIScreen bounds, which report a single panel on foldables. Compute banner
heights from the view's safe area insets so relayouts don't collapse them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Unfolded (resizable) scenes don't re-query prefersStatusBarHidden when the
IAM window becomes key, so full-bleed IAMs kept the status bar over the
close button until a rotation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This reverts commit 3766b06.

@abdulraqeeb33 abdulraqeeb33 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.

The layout rework reads right: scene bounds over UIScreen, the constraint teardown fixes a real per-rotation leak, and the verticalGuide is equivalent to the old anchors for every placement. Two things block, both outside what the simulator matrix can show.

  1. The iOS 27.1 reserved-region branch compiles out of the release binaries (inline on the #if).
  2. sceneView falls back silently to the banner window (inline).

Not inline because the line isn't in the diff: OSInAppMessageView.m:155 never calls completion on a getPageMetaData() error, so a fold whose JS call fails leaves messageView.hidden = true under a live alert-level window. Pre-existing, but fold/unfold is the hot path now. completion(nil) and rebuilding without a height change would recover it.

Smaller:

  • updateHorizontalInsets never re-assigns messageView.horizontalInsets; harmless today since every remeasure is preceded by an assignment, but a one-liner closes it.
  • region.frame already includes UIKit's interactive margins per the header, and we add MESSAGE_MARGIN on top. Fine if intended; worth a word in the comment.
  • No log records which window sceneView chose, the scene bounds, or the computed insets. One VERBOSE line per addConstraintsForMessage (not per layout pass) would make a Duo layout report reconstructible.
  • The inset math has no seam and no tests. Pulling (margin, safeArea, width, regions, isFullscreen) -> UIEdgeInsets into a pure function would let the straddling-region and useWidthMargin == NO cases be unit tested instead of screenshot-only.

fadi-george and others added 6 commits November 3, 2026 10:19
Release frameworks are built with an SDK older than iOS 27.1, which compiled the
camera clearance out of shipped binaries.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Also keep the message view's insets in sync on layout, log the chosen
scene window and insets, and note that region frames include margins.

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

Copy link
Copy Markdown
Collaborator Author

Re the review summary (52f0398):

  • getPageMetaData error: now restores the webview constraints and calls completion(nil); the controller keeps the previous height, so a failed fold/rotation re-shows the message instead of leaving it hidden.
  • updateHorizontalInsets now keeps messageView.horizontalInsets in sync.
  • Noted in the comment that region frames already include UIKit's margins and the message margin goes on top (intended, matches system banners).
  • addConstraintsForMessage logs the chosen scene window, its bounds/safe area and the horizontal insets at VERBOSE.
  • Pure inset function + unit tests: filed as SDK-5661 to keep this PR's scope down.

@abdulraqeeb33 abdulraqeeb33 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.

Everything from both rounds is addressed. I checked the runtime shim in isolation: Class-through-protocol dispatch, nil on a missing class, and @available with a version the SDK doesn't know all behave, so the camera clearance ships under the 15.2 pin. You're right on coordinateSpace too: UIWindowSceneGeometry.coordinateSpace is 26+, not 16+, so keeping the deprecated property is the right call on a 15.0 target.

One non-blocking ask inline (ticket link for the toolchain bump). Merge once CI reports green on this head.

Co-Authored-By: Claude Opus 5.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.

2 participants