Contribute this app's own rows to the phone's search (MICA-286).
The home-screen search knows how to find an app, a contact, a conversation, a photo, a
mail and a listing, because the shell holds all six. It holds nothing an app owns — and
for a core: false add-on it never can: core may not name an app the Store installs
(sdk/coreBoundary.test.ts), and an add-on's rows live inside a sandboxed frame the
shell cannot read. This is how they get listed anyway.
search runs where the app runs, on the app's own rows, and only the hits it returns
are handed to the shell. needle is already trimmed and lower-cased, and is never empty:
an empty query clears this app's rows without asking it anything.
props is a deep link into this app, the same object useDeepLink reads back —
openApp(appId, props) is what a tap does. An app with no deep link may omit it and the
row opens the app root, which is the honest fallback rather than a dead tap.
Three things follow from residency, and they are the reason this is a hook rather than a
manifest field:
The app has to be running. Apps mount on first open and stay mounted, so a phone
that has never opened this app in this session lists nothing from it. That is a real
limit and not a bug this hook can fix — an app that is not running has no code in
memory to ask.
Nothing is fetched for a search.search is synchronous by construction, so it
answers from what the app already holds. An app that wants its rows findable before it
is opened should declare a preload in its manifest, which is what fills those caches.
The subscription ends with the component. The returned function releases it early
for a caller that wants a provider scoped to one screen; unmount does it otherwise.
A search that throws is caught here: the phone's search must not break because one app
did, so this app contributes nothing for that keystroke and the rest of the list is
unaffected.
Contribute this app's own rows to the phone's search (MICA-286).
The home-screen search knows how to find an app, a contact, a conversation, a photo, a mail and a listing, because the shell holds all six. It holds nothing an app owns — and for a
core: falseadd-on it never can: core may not name an app the Store installs (sdk/coreBoundary.test.ts), and an add-on's rows live inside a sandboxed frame the shell cannot read. This is how they get listed anyway.searchruns where the app runs, on the app's own rows, and only the hits it returns are handed to the shell.needleis already trimmed and lower-cased, and is never empty: an empty query clears this app's rows without asking it anything.propsis a deep link into this app, the same objectuseDeepLinkreads back —openApp(appId, props)is what a tap does. An app with no deep link may omit it and the row opens the app root, which is the honest fallback rather than a dead tap.Three things follow from residency, and they are the reason this is a hook rather than a manifest field:
searchis synchronous by construction, so it answers from what the app already holds. An app that wants its rows findable before it is opened should declare apreloadin its manifest, which is what fills those caches.A
searchthat throws is caught here: the phone's search must not break because one app did, so this app contributes nothing for that keystroke and the rest of the list is unaffected.