MOBILE-328: Web bridge action handler registry, embedded blocks on the shared bridge - #757
Open
Vailence wants to merge 16 commits into
Open
MOBILE-328: Web bridge action handler registry, embedded blocks on the shared bridge#757Vailence wants to merge 16 commits into
Vailence wants to merge 16 commits into
Conversation
added 15 commits
August 13, 2026 13:22
Bridge action handling lives inside TransparentView today, in one switch on a 1000-line view, so nothing but the modal popup can reach log, localState or openLink. This is the seam that lets any WebView reuse them. A handler knows nothing about the page talking to it: everything it may need arrives through WebBridgeHost. Surfaces differ by what they conform to, not by a type the handlers switch on — a page sending an action its host does not listen for is journalled and dropped, never an error. That is what lets an in-app pick up contentRendered later by adding one conformance. The registry indexes actions to owners at construction. Not for speed at this size, but so that two handlers claiming one action is caught while the set is assembled and reported, instead of resolving silently by array order. Nothing is wired yet: TransparentView is untouched and behaviour is unchanged. The Handlers folders are synchronized groups, so the handlers moving in next need no project file edits.
First action out of the switch, and the first time the registry runs on the real path: TransparentView now asks it before falling through to what has not moved yet. log is the cheapest action there is, which is the point — the risky part of this step is the wiring, not the behaviour, so it is proved on something that cannot hide a regression. The handler takes its category from the host instead of naming one. For the popup that resolves to the same category it always logged under; an embedded block will get its own without the handler knowing either exists. WebViewAction.onLog and its implementation go with it: the call that was moved was the only one. payloadString replaces the inline payload unwrapping, same logic, now shared with the handler. WKWebView leaves WebBridgeHost. It was there for safe-area insets, but those belong to the start payload, which the host builds and which already holds the web view — no handler ever needed it.
localState.get/set/init leave the switch, taking their two helpers with them. The storage property goes too: nothing in the view referenced it any more. Envelopes are unchanged. sendBridgeError produced the same shape respondError does, so the error texts, the log formats and the .info level all survive the move verbatim; only the log category now comes from the host, which for the popup resolves to the one it always used. Storage resolves lazily and through a seam. A handler set is built for every show, so a page that never touches storage should not reach into the container for it — and the tests get a double instead of the real UserDefaults suite. payloadObject moves onto BridgeMessage: the same unwrapping is about to be needed by motion, navigate, permission and the operations. set and init carried two identical copies of the JSONValue to [String: String?] conversion. They are now one function — a string stays a string, null stays an erase distinct from empty, anything richer keeps its JSON text.
…egistry These three travel together because they already did: settings.open has always reached the system through the navigate handler's URL opening, so that path becomes a small shared seam both handlers hold rather than a copy each. The seam is also what makes the routing testable. Which way a link opens could previously only be checked by actually leaving the app; now the decision is observable and the fallback to Safari is not. Two behaviours are corrected rather than carried: The universal-link check no longer depends on the handler being alive. The original called UIApplication.open unconditionally and only guarded the answer with self?; translating that to self?.urlOpener would have silently dropped the open itself if the show ended mid-check. Nothing captures self now, and the page is held weakly — a show that ended gets no answer, and waiting on the system is not what keeps it alive. openLink and permission.request now accept an object payload, not only a JSON string. localState and settings.open always accepted both; the difference read as an accident, and it meant a page whose settings.open worked would be refused on openLink for the same shape. SettingsRequestParser drops the fourth copy of the payload unwrapping. The navigate prefix in the shared opener's log is left as it was, including for settings.open, which has always logged under it. It deserves fixing, but not in a change whose point is that nothing moved.
Both services are registered transient, so a second resolution is a second engine. Leaving one in the view for init/close while the handler owned another would have preparing, playing and stopping land on three different objects and silently do nothing — so the device state lives in the handlers alone, and the view reaches them through the registry. close now tears the registry down in the position it used to stop the pattern and the sensors: devices are released before the window goes, because a pattern playing into a closed show and a sensor callback reaching a dead page are the shape crashes come in. The lazy-initialisation guard is carried over for motion and added for haptics, which never had one: today every close builds a haptic engine just to stop a pattern that was never started. Stopping an engine that does not exist is a no-op, so this is the same behaviour minus an allocation on every show. A gesture arrives from the sensors with no request behind it, so the page is remembered from the request that started monitoring — weakly, since a gesture must not be what keeps a finished show alive. handleSystemShake stays on the view because the system delivers a shake to the responder, not to the bridge. The file_length exemption goes: the file no longer needs it, and leaving it raises a new warning.
The previous commit moved asyncOperation and syncOperation into a handler but never added it to the factory, so nothing owned them: the switch cases were gone, the registry answered that it had no owner, and both actions are deferred — meaning RequestMessageHandler had not answered either. The page would wait for a response that was never coming. It went unnoticed because the suite that covers these actions injects a registry of its own, so it exercised the handler without ever consulting the set the app actually ships. A guard against exactly this follows with the next commit, once the last actions have left the switch and the shipped set is supposed to be complete.
close, init, click and hide were the last actions in the switch, and with them gone it collapses to a single guard for ready, which waits on its payload builder. They are also the first use of what the capability protocols are for. The handler is registered everywhere like any other, and a host that has no window to close simply does not conform: the message is journalled and dropped rather than refused. That is the whole mechanism by which a surface picks one of these up later — a conformance, and nothing else changes. bridgeDidInit sets hasReceivedInit before it does anything else. The no-cache retry policy decides whether a failed subresource is worth healing by asking whether the page ever booted, and a flag set late silently disables the healing with no other symptom. The factory guard arrives here: every action a page can send must have an owner in the shipped set, save ready and the two that only travel native to JS. It was written against the omission fixed in the previous commit and does fail on it, naming both actions.
The last action leaves the switch, and with it the switch itself: dispatch is now the registry and nothing else. Composing the payload moves into a builder of its own. Three callers need it rather than one — ready, the config-update push, and shortly the embedded block — and the assembly was private to the WebView facade, which is neither the right owner nor reachable from a block. What goes in belongs to the host, not the handler: an in-app knows its operation, a block will know its configuration entry, while answering ready with it is the same everywhere. The facade hands the payload over instead of sending it, and sendReadyEvent goes with it. The order the fields are applied in is preserved and now pinned: the configuration's own params are merged before the operation and the track-visit fields, so a colliding key resolves towards the operation. Serialization is guarded before it runs. JSONSerialization raises an Objective-C exception for a NaN or an infinity instead of throwing, so the existing catch could never have run and the host app would have gone down — the empty-object fallback the code already promised was not reachable. Values arriving here are decoded from JSON today, which can express neither, but this is the payload every surface is answered with and the assumption is not worth a crash. Found by the test that pins the fallback.
The block spoke a dialect of its own: its own handler name, its own envelope, one stub action, and no way for the SDK to say anything back. It now speaks what in-apps speak, which is the whole point — a page written once works in either surface, and the block gets logging, local state, links and operations without a line of block-specific code. Readiness comes from contentRendered instead of a height. The page reports how much it drew, once per load: nothing drawn is a valid outcome and collapses the block without an error screen, which is precisely why this is not init — that one is welded to presenting a window and has no such ending. height and heightChanged go with it. The host has always owned the block's height; the page never had a say, and the site will not be sending a number. Two files are deleted rather than adapted. EmbeddedBlockPageMessage was the whole ad-hoc envelope, and EmbeddedBlockActionRouter knew a single action which it only logged — the shared openLink replaces it outright. The load's navigation is handed to the bridge. Its staleness gate drops every script message until that exact document commits, so a reused web view cannot deliver a previous owner's — and without this the page's messages would vanish in silence while the block waited out its whole budget. Navigation policy comes with the bridge too: a tap on a link is refused in place. A block is a piece of the host's own layout, and replacing the feed with the destination there is a dead end with no way back; links belong in openLink. Whether a user is actually looking is now the page's business to know. The provider keeps it current, and the bridge reads it before acting on the user's behalf — a block off screen keeps a live page that can still deliver whatever its setTimeout scheduled. The waiting budget goes to twelve seconds. It now covers strictly more than before: load, bridge boot, the start payload, the page's own pipeline including up to three seconds of waiting on targeting, and only then the report.
Both are answered but neither is implemented: the web page already calls checkInappsTargeting, and until something owns it the registry reports no owner, the page waits out its three seconds and renders an empty feed. Answering is what makes the rest of the contract reachable. checkInappsTargeting lets every id through, in the order it was asked about — the page maps the answer back onto its own cards. showInApp journals the request and acknowledges it, so the page can finish its own flow instead of sitting on a promise nothing will settle. The shapes are taken from the page rather than from the ticket: it reads payload.inappIds and throws when that key is absent, so a reply of the wrong shape and no reply at all look identical from the feed. The tests pin those shapes for the same reason. What each will actually do is written down where it will be written, as comments: the targeting source and its cold-cache behaviour, the thread the segmentation cache is written from, and for the show — bypassing the gates by not entering the pipeline at all, tracking the show anyway, and merging params last so it overwrites everything including service keys. The block address moves to the staging host, which is where the page that implements this contract is published.
The base captured the view weakly here — handler.request { [weak self] … } —
and the move into a handler turned that into a strong capture of the host. A
system dialog stands for as long as the user leaves it standing, so a request
that actually shows one pinned TransparentView and with it the whole handler
set: an uncancelled ready checker, and a live sensor subscription if the page
had asked for motion.
settings.open had the same asymmetry inside one switch: the .application route
reached the system through BridgeURLOpening with [weak host], the .notifications
route did not. The hold there is short — the seconds of a screen change — but a
rule that holds on one branch of a switch and not the other is not a rule.
Both routes now answer only a page that is still around, which is what the base
did. The spies learned to hold their answer back: that pause is the only window
in which what the SDK keeps alive is observable at all.
openLink answers the page from the presentation completion, and UIKit does not call that completion when the presentation does not happen. A controller that is already presenting refuses to present anything else — and the block asked the window's root, which is exactly the controller that is already presenting whenever anything modal stands above it: the block inside a bottom sheet, an alert, a share sheet, another in-app from this same SDK. The tap opened nothing, nothing was journalled, and the promise in JS never settled. The presenter is now the topmost of the presentation chain, the same walk the snackbar strategy makes. The change is in the block's own answer to "what do I present from" rather than in the handler, so the in-app surface stays as it is in develop: for TransparentView the presenter is still its own view controller. Presenting for real in a test would drag a visible window and a transition into it, so the controllers report what the test put on top of them — what is checked is the walk, not UIKit's animation. The answer still leaves from the completion, so a presenter caught mid-dismissal can still swallow it. That is the remaining half of this finding, not of this commit.
OperationActionHandler was the largest handler of the move with no file of its own. Its two neighbours keep what they already cover — makeSyncOperationResponse as the pure mapping it is, and the tag merge through the view — so the new suite takes the wiring between them: the queue write and the branch where it fails, the answer to a sync operation in both outcomes, the shapes of payload it refuses, and that a request in flight does not hold the page alive. Its event repository holds its answer back, because that pause is the only window in which what the SDK keeps alive is observable at all. The block speaking the shared bridge was the point of 09bef19 and had no test at all. Now a request from its document reaches the registry with the page as the host, an answer is not routed as if it were a request, an action nobody owns is dropped without disturbing the block, and the identity the page lends a handler is its own. BridgeURLOpening gets the half that has a decision in it: which outcome becomes which answer. SystemURLOpener stays out, as its own documentation says it must. WebBridgeHostResponseTests moves out of the registry's file, where it was a stranger. Three branches of moved code were reachable but unasserted: a url that does not parse, a url with no scheme, and the Safari fallback with no presenter to fall back on — the last being the path the presenter walk was changed for. Motion's first guard is covered too, where nothing arrived at all as against something in the wrong shape. loadRegistersItsNavigation still does not prove what it is named for. contentLoadIssued and contentURL are private to MindboxWebBridge, and a WKScriptMessage cannot be built in a test to drive the gate from outside, so it pins the visible half — the load goes to the block's own address — and says so where it stands.
A weak local declared empty and assigned later carries no "never mutated" warning, so watching a page's release needs no box to hold the reference.
Contributor
|
The two `swiftlint:disable empty_count` directives read as superfluous to SwiftLint 0.65, while older versions do flag those lines — dropping the directives alone would only trade one error for the other. Naming the value `renderedCount` leaves neither version anything to say.
Contributor
There was a problem hiding this comment.
Pull request overview
Unifies in-app and embedded-block WebViews behind a shared action-handler registry.
Changes:
- Extracts bridge actions into session-scoped handlers and a registry.
- Migrates embedded blocks to the shared bridge.
- Adds extensive handler, routing, lifecycle, and payload tests.
Reviewed changes
Copilot reviewed 58 out of 58 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
MindboxTests/InApp/Tests/WebView/TransparentViewJSBridgeTests.swift |
Updates bridge routing tests. |
MindboxTests/InApp/Tests/WebView/BridgeHandlers/WebViewStartPayloadBuilderTests.swift |
Tests start payload construction. |
MindboxTests/InApp/Tests/WebView/BridgeHandlers/WebBridgeHostTests.swift |
Tests response envelopes. |
MindboxTests/InApp/Tests/WebView/BridgeHandlers/WebBridgeActionRegistryTests.swift |
Tests registry routing and teardown. |
MindboxTests/InApp/Tests/WebView/BridgeHandlers/WebBridgeActionHandlerFactoryTests.swift |
Tests shipped handler composition. |
MindboxTests/InApp/Tests/WebView/BridgeHandlers/TargetingAndShowStubTests.swift |
Tests future-action stubs. |
MindboxTests/InApp/Tests/WebView/BridgeHandlers/SettingsActionHandlerTests.swift |
Tests settings handling. |
MindboxTests/InApp/Tests/WebView/BridgeHandlers/ReadyActionHandlerTests.swift |
Tests ready payload responses. |
MindboxTests/InApp/Tests/WebView/BridgeHandlers/PermissionActionHandlerTests.swift |
Tests permission requests. |
MindboxTests/InApp/Tests/WebView/BridgeHandlers/OperationActionHandlerTests.swift |
Tests operation dispatch. |
MindboxTests/InApp/Tests/WebView/BridgeHandlers/OpenLinkActionHandlerTests.swift |
Tests URL routing. |
MindboxTests/InApp/Tests/WebView/BridgeHandlers/MotionActionHandlerTests.swift |
Tests motion lifecycle. |
MindboxTests/InApp/Tests/WebView/BridgeHandlers/LogActionHandlerTests.swift |
Tests bridge logging. |
MindboxTests/InApp/Tests/WebView/BridgeHandlers/LocalStateActionHandlerTests.swift |
Tests local-state actions. |
MindboxTests/InApp/Tests/WebView/BridgeHandlers/LifecycleActionHandlerTests.swift |
Tests lifecycle callbacks. |
MindboxTests/InApp/Tests/WebView/BridgeHandlers/HapticActionHandlerTests.swift |
Tests haptic lifecycle. |
MindboxTests/InApp/Tests/WebView/BridgeHandlers/ContentRenderedActionHandlerTests.swift |
Tests rendered-content reports. |
MindboxTests/InApp/Tests/WebView/BridgeHandlers/BridgeURLOpeningTests.swift |
Tests system URL opening. |
MindboxTests/InApp/Tests/WebView/BridgeHandlers/BridgeHandlerDoubles.swift |
Adds shared test doubles. |
MindboxTests/InApp/Tests/TransparentViewSyncOperationResponseTests.swift |
Retargets operation response tests. |
MindboxTests/EmbeddedBlocks/MindboxEmbeddedBlockViewTests.swift |
Updates block presentation tests. |
MindboxTests/EmbeddedBlocks/EmbeddedBlockWebViewProviderTests.swift |
Tests content and presence state. |
MindboxTests/EmbeddedBlocks/EmbeddedBlockWebViewPageTests.swift |
Tests shared bridge hosting. |
MindboxTests/EmbeddedBlocks/EmbeddedBlockMocks.swift |
Updates embedded-block mocks. |
MindboxTests/EmbeddedBlocks/EmbeddedBlockContentProviderFactoryTests.swift |
Updates factory tests. |
Mindbox/Utilities/Constants.swift |
Extends embedded-block timeout. |
Mindbox/InAppMessages/Presentation/Views/WebView/WebViewController.swift |
Removes legacy log callback. |
Mindbox/InAppMessages/Presentation/Views/WebView/TransparentView.swift |
Delegates actions to the registry. |
Mindbox/InAppMessages/Presentation/Views/WebView/Settings/SettingsRequestParser.swift |
Reuses shared payload parsing. |
Mindbox/InAppMessages/Presentation/Views/WebView/Debug/MindboxWebViewFacade.swift |
Delegates start payload building. |
Mindbox/InAppMessages/Presentation/Views/WebView/Bridge/Handlers/WebViewStartPayloadBuilder.swift |
Adds shared payload builder. |
Mindbox/InAppMessages/Presentation/Views/WebView/Bridge/Handlers/WebBridgeHost.swift |
Defines bridge host capabilities. |
Mindbox/InAppMessages/Presentation/Views/WebView/Bridge/Handlers/WebBridgeActionHandlerFactory.swift |
Builds per-session handlers. |
Mindbox/InAppMessages/Presentation/Views/WebView/Bridge/Handlers/WebBridgeActionHandler.swift |
Defines handlers and registry. |
Mindbox/InAppMessages/Presentation/Views/WebView/Bridge/Handlers/ShowInAppActionHandler.swift |
Adds show-in-app stub. |
Mindbox/InAppMessages/Presentation/Views/WebView/Bridge/Handlers/SettingsActionHandler.swift |
Extracts settings handling. |
Mindbox/InAppMessages/Presentation/Views/WebView/Bridge/Handlers/ReadyActionHandler.swift |
Extracts ready handling. |
Mindbox/InAppMessages/Presentation/Views/WebView/Bridge/Handlers/PermissionActionHandler.swift |
Extracts permission handling. |
Mindbox/InAppMessages/Presentation/Views/WebView/Bridge/Handlers/OperationActionHandler.swift |
Extracts operation handling. |
Mindbox/InAppMessages/Presentation/Views/WebView/Bridge/Handlers/OpenLinkActionHandler.swift |
Extracts link handling. |
Mindbox/InAppMessages/Presentation/Views/WebView/Bridge/Handlers/MotionActionHandler.swift |
Extracts motion handling. |
Mindbox/InAppMessages/Presentation/Views/WebView/Bridge/Handlers/LogActionHandler.swift |
Extracts logging. |
Mindbox/InAppMessages/Presentation/Views/WebView/Bridge/Handlers/LocalStateActionHandler.swift |
Extracts local-state handling. |
Mindbox/InAppMessages/Presentation/Views/WebView/Bridge/Handlers/LifecycleActionHandler.swift |
Extracts lifecycle handling. |
Mindbox/InAppMessages/Presentation/Views/WebView/Bridge/Handlers/HapticActionHandler.swift |
Extracts haptic handling. |
Mindbox/InAppMessages/Presentation/Views/WebView/Bridge/Handlers/ContentRenderedActionHandler.swift |
Handles content counts. |
Mindbox/InAppMessages/Presentation/Views/WebView/Bridge/Handlers/CheckInappsTargetingActionHandler.swift |
Adds targeting stub. |
Mindbox/InAppMessages/Presentation/Views/WebView/Bridge/Handlers/BridgeURLOpening.swift |
Adds shared URL-opening seam. |
Mindbox/InAppMessages/Presentation/Views/WebView/Bridge/BridgeMessage.swift |
Extends action vocabulary and payload helpers. |
Mindbox/EmbeddedBlocks/WebView/EmbeddedBlockWebViewProvider.swift |
Uses content reports and presence state. |
Mindbox/EmbeddedBlocks/WebView/EmbeddedBlockWebViewPage.swift |
Migrates blocks to the shared bridge. |
Mindbox/EmbeddedBlocks/WebView/EmbeddedBlockPageMessage.swift |
Removes the legacy message channel. |
Mindbox/EmbeddedBlocks/WebView/EmbeddedBlockPageHosting.swift |
Updates the page-host contract. |
Mindbox/EmbeddedBlocks/Resolver/EmbeddedBlockResolver.swift |
Changes the stories endpoint. |
Mindbox/EmbeddedBlocks/Container/EmbeddedBlockContentProviderFactory.swift |
Removes legacy router injection. |
Mindbox/EmbeddedBlocks/Actions/EmbeddedBlockActionRouter.swift |
Removes the legacy action router. |
Mindbox/DI/Injections/InjectEmbeddedBlocks.swift |
Updates embedded-block registrations. |
Mindbox.xcodeproj/project.pbxproj |
Adds synchronized handler groups. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| /// delivers whatever its `setTimeout` scheduled. Not a single touch stands behind such a | ||
| /// message, so anything acting on the user's behalf — opening a link, showing a window — | ||
| /// must not run on it. | ||
| var isUserPresent: Bool { get } |
| // deciding whether the page may act on the user's behalf. | ||
| outcome = nil | ||
| didReportContent = false | ||
| page?.isUserPresent = true |
| case .int(let count): | ||
| return count | ||
| case .double(let count): | ||
| return Int(exactly: count.rounded()) |
Comment on lines
+323
to
+327
| /// JS asks which of these in-apps currently pass targeting. | ||
| /// | ||
| /// Answered from targeting the SDK has already computed, without going to the network: | ||
| /// the page gives up on the answer after three seconds and renders nothing rather than | ||
| /// wait. The reply keeps the ids that pass, in the order they were asked about. |
Comment on lines
+339
to
+346
| /// JS asks for an in-app to be shown by id. | ||
| /// | ||
| /// `params` is merged into that in-app's start payload and overwrites whatever it | ||
| /// collides with. The SDK neither validates nor limits it: what the page sends is what | ||
| /// the page gets, and avoiding collisions is the page's business. | ||
| /// | ||
| /// `index` and `sourceInappId` describe where the request came from and are journalled, | ||
| /// not passed on — the page already puts everything it needs into `params`. |
| /// The stories feed page on static hosting. Hardcoded for now: once the admin panel config | ||
| /// arrives, the address will come from there together with the id → content mapping. | ||
| private static let storiesPageURL = "https://mobile-static.mindbox.ru/beta/inapps/webview/content/stories.html" | ||
| private static let storiesPageURL = "https://mobile-static-staging.mindbox.ru/inapps/webview/content/stories.html" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
MOBILE-328 разбирает JS-бридж вебвью на реестр обработчиков действий и переводит встроенные блоки на тот же бридж, что и in-app вебвью.
До этого вся логика бриджа жила внутри
TransparentView(~800 строк), а встроенные блоки имели собственный параллельный канал сообщений (EmbeddedBlockPageMessage+EmbeddedBlockActionRouter). Теперь есть один общий набор обработчиков и два хоста, которые его обслуживают.Что появилось
WebBridgeActionHandler— протокол одного действия (или семейства): объявляет свой наборactions, обрабатывает запрос и умеетtearDown(). Обработчик ничего не знает о конкретной странице — всё нужное приходит черезWebBridgeHost.WebBridgeActionRegistry— индексирует действия один раз при сборке, дальше диспетчеризация это словарный lookup. Дубль действия в двух обработчиках логируется как ошибка конфигурации, побеждает первый. Неизвестное действие возвращаетfalse— веб-словарь имеет право быть новее SDK.WebBridgeActionHandlerFactory— один общий набор обработчиков для всех вебвью, новые инстансы на каждую сессию (хаптик-движок и подписка на motion должны умирать вместе со страницей).WebBridgeHost— то, что обработчик видит от страницы. РеализуютTransparentViewиEmbeddedBlockWebViewPage.Ready,Log,LocalState,OpenLink,Settings,Permission,Haptic,Motion,Operation,Lifecycle,ContentRendered, плюс заглушкиCheckInappsTargetingиShowInApp.WebViewStartPayloadBuilder— сборка стартового payload вынесена из вью.BridgeURLOpening— общее открытие ссылок; Safari для блока показывается от топового контроллера.Что убрано
EmbeddedBlockPageMessageиEmbeddedBlockActionRouter— их заменил общий бридж.TransparentView(−800 строк) иMindboxWebViewFacade.Прочее
Type of Change
checkInappsTargeting/showInAppпод будущую задачу)Test Procedure
Покрытие юнит-тестами на каждый обработчик и на реестр:
WebBridgeActionRegistryTests,WebBridgeActionHandlerFactoryTests,WebBridgeHostTestsLocalState,OpenLink,Settings,Permission,Haptic,Motion,Operation,Lifecycle,ContentRendered,Ready,Log)WebViewStartPayloadBuilderTests,BridgeURLOpeningTests,TargetingAndShowStubTestsEmbeddedBlockWebViewPageTests,EmbeddedBlockWebViewProviderTests,MindboxEmbeddedBlockViewTests,TransparentViewJSBridgeTestsПрогон всей сюиты: 1571 / 1571 passed, 0 failed (iPhone 16 Pro, iOS 18.5).
Pre-flight Checklist