[{"data":1,"prerenderedAt":2556},["ShallowReactive",2],{"navigation":3,"\u002Fguides\u002Ftesting-with-xcode":455,"\u002Fguides\u002Ftesting-with-xcode-surround":2551},[4,34,51,301,327,332,337,406],{"title":5,"path":6,"stem":7,"children":8,"package":11,"icon":33},"Getting Started","\u002Fgetting-started","1.getting-started\u002F1.index",[9,13,18,23,28],{"title":10,"path":6,"stem":7,"package":11,"icon":12},"Introduction",null,"i-lucide-house",{"title":14,"path":15,"stem":16,"package":11,"icon":17},"Installation","\u002Fgetting-started\u002Finstallation","1.getting-started\u002F2.installation","i-lucide-download",{"title":19,"path":20,"stem":21,"package":11,"icon":22},"Icons","\u002Fgetting-started\u002Ficons","1.getting-started\u002F3.icons","i-lucide-sparkles",{"title":24,"path":25,"stem":26,"package":11,"icon":27},"CLI","\u002Fgetting-started\u002Fcli","1.getting-started\u002F4.cli","i-lucide-terminal",{"title":29,"path":30,"stem":31,"package":11,"icon":32},"Roadmap","\u002Fgetting-started\u002Froadmap","1.getting-started\u002F5.roadmap","i-lucide-map","i-lucide-rocket",{"title":35,"package":11,"icon":36,"path":37,"stem":38,"children":39,"page":50},"Packages","i-lucide-package","\u002Fpackages","2.packages",[40,45],{"title":41,"path":42,"stem":43,"package":11,"icon":44},"@vyui\u002Fcore","\u002Fpackages\u002Fcore","2.packages\u002F1.core","i-lucide-box",{"title":46,"path":47,"stem":48,"package":11,"icon":49},"@vyui\u002Fkit","\u002Fpackages\u002Fkit","2.packages\u002F2.kit","i-lucide-layers",false,{"title":52,"package":11,"icon":53,"path":54,"stem":55,"children":56},"Components","i-lucide-layout-grid","\u002Fcomponents","3.components",[57,59,65,70,75,80,85,90,95,100,105,110,115,120,125,130,135,140,144,149,153,158,163,168,173,178,183,188,193,198,203,208,213,218,223,228,233,238,242,246,251,256,261,266,271,276,280,284,288,292,296],{"title":52,"path":54,"stem":58,"package":11,"icon":53},"3.components\u002Findex",{"title":60,"path":61,"stem":62,"package":63,"icon":64},"Accordion","\u002Fcomponents\u002Faccordion","3.components\u002Faccordion","kit","i-lucide-list-collapse",{"title":66,"path":67,"stem":68,"package":63,"icon":69},"Action Sheet","\u002Fcomponents\u002Faction-sheet","3.components\u002Faction-sheet","i-lucide-panel-bottom",{"title":71,"path":72,"stem":73,"package":63,"icon":74},"Alert","\u002Fcomponents\u002Falert","3.components\u002Falert","i-lucide-triangle-alert",{"title":76,"path":77,"stem":78,"package":79,"icon":74},"Alert Dialog","\u002Fcomponents\u002Falert-dialog","3.components\u002Falert-dialog","core",{"title":81,"path":82,"stem":83,"package":63,"icon":84},"App","\u002Fcomponents\u002Fapp","3.components\u002Fapp","i-lucide-smartphone",{"title":86,"path":87,"stem":88,"package":63,"icon":89},"Aspect Ratio","\u002Fcomponents\u002Faspect-ratio","3.components\u002Faspect-ratio","i-lucide-ratio",{"title":91,"path":92,"stem":93,"package":63,"icon":94},"Avatar","\u002Fcomponents\u002Favatar","3.components\u002Favatar","i-lucide-circle-user-round",{"title":96,"path":97,"stem":98,"package":63,"icon":99},"Avatar Group","\u002Fcomponents\u002Favatar-group","3.components\u002Favatar-group","i-lucide-users-round",{"title":101,"path":102,"stem":103,"package":63,"icon":104},"Button","\u002Fcomponents\u002Fbutton","3.components\u002Fbutton","i-lucide-square-mouse-pointer",{"title":106,"path":107,"stem":108,"package":63,"icon":109},"Calendar","\u002Fcomponents\u002Fcalendar","3.components\u002Fcalendar","i-lucide-calendar-days",{"title":111,"path":112,"stem":113,"package":63,"icon":114},"Card","\u002Fcomponents\u002Fcard","3.components\u002Fcard","i-lucide-square",{"title":116,"path":117,"stem":118,"package":63,"icon":119},"Checkbox","\u002Fcomponents\u002Fcheckbox","3.components\u002Fcheckbox","i-lucide-square-check",{"title":121,"path":122,"stem":123,"package":79,"icon":124},"Collapsible","\u002Fcomponents\u002Fcollapsible","3.components\u002Fcollapsible","i-lucide-chevrons-down-up",{"title":126,"path":127,"stem":128,"package":79,"icon":129},"Dialog","\u002Fcomponents\u002Fdialog","3.components\u002Fdialog","i-lucide-app-window",{"title":131,"path":132,"stem":133,"package":79,"icon":134},"Draggable","\u002Fcomponents\u002Fdraggable","3.components\u002Fdraggable","i-lucide-move",{"title":136,"path":137,"stem":138,"package":63,"icon":139},"Form","\u002Fcomponents\u002Fform","3.components\u002Fform","i-lucide-clipboard-list",{"title":141,"path":142,"stem":143,"package":79,"icon":22},"Icon","\u002Fcomponents\u002Ficon","3.components\u002Ficon",{"title":145,"path":146,"stem":147,"package":63,"icon":148},"Input","\u002Fcomponents\u002Finput","3.components\u002Finput","i-lucide-text-cursor-input",{"title":150,"path":151,"stem":152,"package":63,"icon":69},"Island","\u002Fcomponents\u002Fisland","3.components\u002Fisland",{"title":154,"path":155,"stem":156,"package":63,"icon":157},"Island Button","\u002Fcomponents\u002Fisland-button","3.components\u002Fisland-button","i-lucide-circle-ellipsis",{"title":159,"path":160,"stem":161,"package":63,"icon":162},"Island Group","\u002Fcomponents\u002Fisland-group","3.components\u002Fisland-group","i-lucide-panels-top-left",{"title":164,"path":165,"stem":166,"package":63,"icon":167},"KeyboardAware","\u002Fcomponents\u002Fkeyboard-aware","3.components\u002Fkeyboard-aware","i-lucide-keyboard",{"title":169,"path":170,"stem":171,"package":63,"icon":172},"Label","\u002Fcomponents\u002Flabel","3.components\u002Flabel","i-lucide-tag",{"title":174,"path":175,"stem":176,"package":63,"icon":177},"Modal","\u002Fcomponents\u002Fmodal","3.components\u002Fmodal","i-lucide-gallery-vertical-end",{"title":179,"path":180,"stem":181,"package":79,"icon":182},"Navigation","\u002Fcomponents\u002Fnavigation","3.components\u002Fnavigation","i-lucide-route",{"title":184,"path":185,"stem":186,"package":63,"icon":187},"Number Field","\u002Fcomponents\u002Fnumber-field","3.components\u002Fnumber-field","i-lucide-list-plus",{"title":189,"path":190,"stem":191,"package":79,"icon":192},"Pagination","\u002Fcomponents\u002Fpagination","3.components\u002Fpagination","i-lucide-ellipsis",{"title":194,"path":195,"stem":196,"package":63,"icon":197},"Pin Input","\u002Fcomponents\u002Fpin-input","3.components\u002Fpin-input","i-lucide-key-round",{"title":199,"path":200,"stem":201,"package":63,"icon":202},"Placeholder","\u002Fcomponents\u002Fplaceholder","3.components\u002Fplaceholder","i-lucide-square-dashed",{"title":204,"path":205,"stem":206,"package":63,"icon":207},"Popover","\u002Fcomponents\u002Fpopover","3.components\u002Fpopover","i-lucide-message-square-more",{"title":209,"path":210,"stem":211,"package":63,"icon":212},"Progress","\u002Fcomponents\u002Fprogress","3.components\u002Fprogress","i-lucide-chart-no-axes-column-increasing",{"title":214,"path":215,"stem":216,"package":63,"icon":217},"Radio Group","\u002Fcomponents\u002Fradio-group","3.components\u002Fradio-group","i-lucide-circle-dot",{"title":219,"path":220,"stem":221,"package":63,"icon":222},"Rating","\u002Fcomponents\u002Frating","3.components\u002Frating","i-lucide-star",{"title":224,"path":225,"stem":226,"package":79,"icon":227},"Scroll View","\u002Fcomponents\u002Fscroll-view","3.components\u002Fscroll-view","i-lucide-scroll",{"title":229,"path":230,"stem":231,"package":63,"icon":232},"Select","\u002Fcomponents\u002Fselect","3.components\u002Fselect","i-lucide-list-check",{"title":234,"path":235,"stem":236,"package":63,"icon":237},"Separator","\u002Fcomponents\u002Fseparator","3.components\u002Fseparator","i-lucide-separator-horizontal",{"title":239,"path":240,"stem":241,"package":79,"icon":69},"Sheet","\u002Fcomponents\u002Fsheet","3.components\u002Fsheet",{"title":243,"path":244,"stem":245,"package":63,"icon":162},"Skeleton","\u002Fcomponents\u002Fskeleton","3.components\u002Fskeleton",{"title":247,"path":248,"stem":249,"package":63,"icon":250},"Slider","\u002Fcomponents\u002Fslider","3.components\u002Fslider","i-lucide-sliders-horizontal",{"title":252,"path":253,"stem":254,"package":63,"icon":255},"Sortable","\u002Fcomponents\u002Fsortable","3.components\u002Fsortable","i-lucide-list-restart",{"title":257,"path":258,"stem":259,"package":63,"icon":260},"Stepper","\u002Fcomponents\u002Fstepper","3.components\u002Fstepper","i-lucide-list-ordered",{"title":262,"path":263,"stem":264,"package":63,"icon":265},"Swipe Action","\u002Fcomponents\u002Fswipe-action","3.components\u002Fswipe-action","i-lucide-move-horizontal",{"title":267,"path":268,"stem":269,"package":63,"icon":270},"Swiper","\u002Fcomponents\u002Fswiper","3.components\u002Fswiper","i-lucide-gallery-horizontal-end",{"title":272,"path":273,"stem":274,"package":63,"icon":275},"Switch","\u002Fcomponents\u002Fswitch","3.components\u002Fswitch","i-lucide-toggle-right",{"title":277,"path":278,"stem":279,"package":63,"icon":162},"Tabs","\u002Fcomponents\u002Ftabs","3.components\u002Ftabs",{"title":281,"path":282,"stem":283,"package":63,"icon":148},"Textarea","\u002Fcomponents\u002Ftextarea","3.components\u002Ftextarea",{"title":285,"path":286,"stem":287,"package":63,"icon":207},"Toast","\u002Fcomponents\u002Ftoast","3.components\u002Ftoast",{"title":289,"path":290,"stem":291,"package":63,"icon":275},"Toggle","\u002Fcomponents\u002Ftoggle","3.components\u002Ftoggle",{"title":293,"path":294,"stem":295,"package":63,"icon":162},"Toggle Group","\u002Fcomponents\u002Ftoggle-group","3.components\u002Ftoggle-group",{"title":297,"path":298,"stem":299,"package":63,"icon":300},"Tray","\u002Fcomponents\u002Ftray","3.components\u002Ftray","i-lucide-square-dashed-bottom",{"title":302,"path":303,"stem":304,"children":305,"package":11,"icon":307},"Theming","\u002Ftheming","4.theming\u002F1.index",[306,308,312,317,322],{"title":302,"path":303,"stem":304,"package":11,"icon":307},"i-lucide-palette",{"title":309,"path":310,"stem":311,"package":11,"icon":250},"Overrides","\u002Ftheming\u002Foverrides","4.theming\u002F2.overrides",{"title":313,"path":314,"stem":315,"package":11,"icon":316},"Colors","\u002Ftheming\u002Fcolors","4.theming\u002F3.colors","i-lucide-swatch-book",{"title":318,"path":319,"stem":320,"package":11,"icon":321},"Dark Mode","\u002Ftheming\u002Fdark-mode","4.theming\u002F4.dark-mode","i-lucide-moon",{"title":323,"path":324,"stem":325,"package":11,"icon":326},"Fonts","\u002Ftheming\u002Ffonts","4.theming\u002F5.fonts","i-lucide-type",{"title":328,"path":329,"stem":330,"package":11,"icon":331},"Accessibility","\u002Faccessibility","5.accessibility","i-lucide-accessibility",{"title":333,"path":334,"stem":335,"package":11,"icon":336},"i18n","\u002Fi18n","6.i18n","i-lucide-languages",{"title":338,"path":339,"stem":340,"children":341,"package":11,"icon":343},"Composables","\u002Fcomposables","7.composables\u002F1.index",[342,344,349,354,359,364,369,374,378,383,387,391,396,401],{"title":338,"path":339,"stem":340,"package":11,"icon":343},"i-lucide-square-function",{"title":345,"path":346,"stem":347,"package":11,"icon":348},"useEmitAsProps","\u002Fcomposables\u002Fuse-emit-as-props","7.composables\u002F10.use-emit-as-props","i-lucide-zap",{"title":350,"path":351,"stem":352,"package":11,"icon":353},"useForwardPropsEmits","\u002Fcomposables\u002Fuse-forward-props-emits","7.composables\u002F11.use-forward-props-emits","i-lucide-merge",{"title":355,"path":356,"stem":357,"package":11,"icon":358},"useForwardExpose","\u002Fcomposables\u002Fuse-forward-expose","7.composables\u002F12.use-forward-expose","i-lucide-share-2",{"title":360,"path":361,"stem":362,"package":11,"icon":363},"useStateMachine","\u002Fcomposables\u002Fuse-state-machine","7.composables\u002F13.use-state-machine","i-lucide-workflow",{"title":365,"path":366,"stem":367,"package":11,"icon":368},"usePrimitiveElement","\u002Fcomposables\u002Fuse-primitive-element","7.composables\u002F14.use-primitive-element","i-lucide-crosshair",{"title":370,"path":371,"stem":372,"package":11,"icon":373},"useAppConfig","\u002Fcomposables\u002Fuse-app-config","7.composables\u002F2.use-app-config","i-lucide-settings",{"title":375,"path":376,"stem":377,"package":11,"icon":321},"useColorMode","\u002Fcomposables\u002Fuse-color-mode","7.composables\u002F3.use-color-mode",{"title":379,"path":380,"stem":381,"package":11,"icon":382},"useComponentIcons","\u002Fcomposables\u002Fuse-component-icons","7.composables\u002F4.use-component-icons","i-lucide-shapes",{"title":384,"path":385,"stem":386,"package":11,"icon":69},"useTray","\u002Fcomposables\u002Fuse-tray","7.composables\u002F5.use-tray",{"title":388,"path":389,"stem":390,"package":11,"icon":336},"i18n composables","\u002Fcomposables\u002Fi18n-composables","7.composables\u002F6.i18n-composables",{"title":392,"path":393,"stem":394,"package":11,"icon":395},"useId","\u002Fcomposables\u002Fuse-id","7.composables\u002F7.use-id","i-lucide-hash",{"title":397,"path":398,"stem":399,"package":11,"icon":400},"useFilter","\u002Fcomposables\u002Fuse-filter","7.composables\u002F8.use-filter","i-lucide-filter",{"title":402,"path":403,"stem":404,"package":11,"icon":405},"useForwardProps","\u002Fcomposables\u002Fuse-forward-props","7.composables\u002F9.use-forward-props","i-lucide-arrow-right-left",{"title":407,"path":408,"stem":409,"children":410,"package":11,"icon":412},"Guides","\u002Fguides","8.guides\u002F1.index",[411,413,417,422,427,432,436,441,445,450],{"title":407,"path":408,"stem":409,"package":11,"icon":412},"i-lucide-book-open",{"title":414,"path":415,"stem":416,"package":11,"icon":348},"Instant first-frame rendering","\u002Fguides\u002Finstant-first-frame-rendering","8.guides\u002F10.instant-first-frame-rendering",{"title":418,"path":419,"stem":420,"package":11,"icon":421},"Lynx UI frameworks","\u002Fguides\u002Flynx-ui-frameworks","8.guides\u002F2.lynx-ui-frameworks","i-lucide-library",{"title":423,"path":424,"stem":425,"package":11,"icon":426},"What is Vue Lynx","\u002Fguides\u002Fvue-lynx","8.guides\u002F3.vue-lynx","i-lucide-component",{"title":428,"path":429,"stem":430,"package":11,"icon":431},"Lynx vs React Native","\u002Fguides\u002Flynx-vs-react-native","8.guides\u002F4.lynx-vs-react-native","i-lucide-git-compare",{"title":433,"path":434,"stem":435,"package":11,"icon":84},"Testing on your phone","\u002Fguides\u002Ftesting-on-device","8.guides\u002F5.testing-on-device",{"title":437,"path":438,"stem":439,"package":11,"icon":440},"Testing with Xcode","\u002Fguides\u002Ftesting-with-xcode","8.guides\u002F6.testing-with-xcode","i-lucide-apple",{"title":442,"path":443,"stem":444,"package":11,"icon":27},"The rspeedy dev server","\u002Fguides\u002Frspeedy-dev-server","8.guides\u002F7.rspeedy-dev-server",{"title":446,"path":447,"stem":448,"package":11,"icon":449},"Tailwind on Lynx","\u002Fguides\u002Ftailwind-on-lynx","8.guides\u002F8.tailwind-on-lynx","i-lucide-wind",{"title":451,"path":452,"stem":453,"package":11,"icon":454},"Theming on Lynx","\u002Fguides\u002Ftheming-on-lynx","8.guides\u002F9.theming-on-lynx","i-lucide-paintbrush",{"id":456,"title":437,"body":457,"description":2543,"extension":2544,"links":11,"meta":2545,"navigation":2546,"package":11,"path":438,"seo":2547,"stem":439,"__hash__":2550},"docs\u002F8.guides\u002F6.testing-with-xcode.md",{"type":458,"value":459,"toc":2530},"minimark",[460,468,476,483,488,508,512,523,960,964,967,1159,1165,1169,1172,1244,1367,1477,1609,1623,1645,1656,1660,1670,1764,1771,1921,1932,1936,1983,1995,1999,2009,2034,2037,2098,2116,2120,2135,2203,2206,2210,2213,2324,2335,2338,2394,2401,2430,2441,2445,2452,2461,2467,2483,2494,2503,2507,2526],[461,462,463,467],"p",{},[464,465,466],"a",{"href":434},"Lynx Go"," stops being enough the moment your app needs something the runtime does not ship. Camera capture, biometrics, keychain storage, push notifications, custom fonts, permission strings, and every third-party SDK live in native code compiled into an application, and Lynx Go carries only what its authors compiled into theirs.",[461,469,470,471,475],{},"A host of your own is one Xcode target, four Swift files, and a Podfile, and it does five things: boot the Lynx runtime, provide a template that fetches bytes, build a ",[472,473,474],"code",{},"LynxView"," sized to the screen, load a URL into it, and fall back to an embedded bundle in Release. Each file below is one of those steps.",[461,477,478,479,482],{},"Everything here comes from a host running Vy UI components and a custom camera module on a physical iPhone, verified against Lynx 3.9.0 and Xcode 26. Your ",[472,480,481],{},"pnpm dev"," loop keeps working, because the host fetches the same bundle the QR code points at.",[484,485,487],"h2",{"id":486},"what-you-need-once","What you need once",[489,490,491,495,502,505],"ul",{},[492,493,494],"li",{},"Full Xcode with any Apple ID signed in is enough, since a free personal team can install on your own device.",[492,496,497,498,501],{},"CocoaPods and XcodeGen come from ",[472,499,500],{},"brew install cocoapods xcodegen",".",[492,503,504],{},"The iPhone needs Developer Mode, under Settings → Privacy & Security.",[492,506,507],{},"The phone and the Mac have to share a network, because the host fetches the bundle over HTTP.",[484,509,511],{"id":510},"generating-the-project-instead-of-committing-it","Generating the project instead of committing it",[461,513,514,515,518,519,522],{},"An ",[472,516,517],{},".xcodeproj"," in git is a reliable way to generate merge conflicts nobody can read, so XcodeGen generates this one from a declarative spec. You commit the spec, the Podfile, and the Swift sources, and everything else is disposable. Create ",[472,520,521],{},"native\u002Fios-host\u002Fproject.yml",":",[524,525,531],"pre",{"className":526,"code":527,"filename":528,"language":529,"meta":530,"style":530},"language-yaml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","name: VyHost\noptions:\n  bundleIdPrefix: com.example\n  deploymentTarget:\n    iOS: '15.0'\n  createIntermediateGroups: true\nschemes:\n  VyHost:\n    build:\n      targets:\n        VyHost: all\n    run:\n      config: Debug\ntargets:\n  VyHost:\n    type: application\n    platform: iOS\n    sources:\n      - path: HostSources\n      - path: Assets.xcassets\n    info:\n      path: Info.plist\n      properties:\n        UILaunchScreen:\n          UIColorName: LaunchBackground\n        UISupportedInterfaceOrientations: [UIInterfaceOrientationPortrait]\n        # UIKit warns and eventually asserts without a scene manifest.\n        UIApplicationSceneManifest:\n          UIApplicationSupportsMultipleScenes: false\n          UISceneConfigurations:\n            UIWindowSceneSessionRoleApplication:\n              - UISceneConfigurationName: Default\n                UISceneDelegateClassName: $(PRODUCT_MODULE_NAME).SceneDelegate\n        NSLocalNetworkUsageDescription: Dev builds load the app from your Mac over the local network.\n        NSAppTransportSecurity:\n          # Dev only. The Release build phase below strips this from the product.\n          NSAllowsArbitraryLoads: true\n    settings:\n      base:\n        SWIFT_VERSION: '5.9'\n        IPHONEOS_DEPLOYMENT_TARGET: '15.0'\n        CODE_SIGN_STYLE: Automatic\n","project.yml","yaml","",[472,532,533,549,558,569,577,594,606,614,622,630,638,649,657,668,676,683,694,705,713,727,739,747,758,766,774,785,802,809,817,828,836,844,858,869,880,888,894,904,912,920,935,949],{"__ignoreMap":530},[534,535,538,542,545],"span",{"class":536,"line":537},"line",1,[534,539,541],{"class":540},"swJcz","name",[534,543,522],{"class":544},"sMK4o",[534,546,548],{"class":547},"sfazB"," VyHost\n",[534,550,552,555],{"class":536,"line":551},2,[534,553,554],{"class":540},"options",[534,556,557],{"class":544},":\n",[534,559,561,564,566],{"class":536,"line":560},3,[534,562,563],{"class":540},"  bundleIdPrefix",[534,565,522],{"class":544},[534,567,568],{"class":547}," com.example\n",[534,570,572,575],{"class":536,"line":571},4,[534,573,574],{"class":540},"  deploymentTarget",[534,576,557],{"class":544},[534,578,580,583,585,588,591],{"class":536,"line":579},5,[534,581,582],{"class":540},"    iOS",[534,584,522],{"class":544},[534,586,587],{"class":544}," '",[534,589,590],{"class":547},"15.0",[534,592,593],{"class":544},"'\n",[534,595,597,600,602],{"class":536,"line":596},6,[534,598,599],{"class":540},"  createIntermediateGroups",[534,601,522],{"class":544},[534,603,605],{"class":604},"sfNiH"," true\n",[534,607,609,612],{"class":536,"line":608},7,[534,610,611],{"class":540},"schemes",[534,613,557],{"class":544},[534,615,617,620],{"class":536,"line":616},8,[534,618,619],{"class":540},"  VyHost",[534,621,557],{"class":544},[534,623,625,628],{"class":536,"line":624},9,[534,626,627],{"class":540},"    build",[534,629,557],{"class":544},[534,631,633,636],{"class":536,"line":632},10,[534,634,635],{"class":540},"      targets",[534,637,557],{"class":544},[534,639,641,644,646],{"class":536,"line":640},11,[534,642,643],{"class":540},"        VyHost",[534,645,522],{"class":544},[534,647,648],{"class":547}," all\n",[534,650,652,655],{"class":536,"line":651},12,[534,653,654],{"class":540},"    run",[534,656,557],{"class":544},[534,658,660,663,665],{"class":536,"line":659},13,[534,661,662],{"class":540},"      config",[534,664,522],{"class":544},[534,666,667],{"class":547}," Debug\n",[534,669,671,674],{"class":536,"line":670},14,[534,672,673],{"class":540},"targets",[534,675,557],{"class":544},[534,677,679,681],{"class":536,"line":678},15,[534,680,619],{"class":540},[534,682,557],{"class":544},[534,684,686,689,691],{"class":536,"line":685},16,[534,687,688],{"class":540},"    type",[534,690,522],{"class":544},[534,692,693],{"class":547}," application\n",[534,695,697,700,702],{"class":536,"line":696},17,[534,698,699],{"class":540},"    platform",[534,701,522],{"class":544},[534,703,704],{"class":547}," iOS\n",[534,706,708,711],{"class":536,"line":707},18,[534,709,710],{"class":540},"    sources",[534,712,557],{"class":544},[534,714,716,719,722,724],{"class":536,"line":715},19,[534,717,718],{"class":544},"      -",[534,720,721],{"class":540}," path",[534,723,522],{"class":544},[534,725,726],{"class":547}," HostSources\n",[534,728,730,732,734,736],{"class":536,"line":729},20,[534,731,718],{"class":544},[534,733,721],{"class":540},[534,735,522],{"class":544},[534,737,738],{"class":547}," Assets.xcassets\n",[534,740,742,745],{"class":536,"line":741},21,[534,743,744],{"class":540},"    info",[534,746,557],{"class":544},[534,748,750,753,755],{"class":536,"line":749},22,[534,751,752],{"class":540},"      path",[534,754,522],{"class":544},[534,756,757],{"class":547}," Info.plist\n",[534,759,761,764],{"class":536,"line":760},23,[534,762,763],{"class":540},"      properties",[534,765,557],{"class":544},[534,767,769,772],{"class":536,"line":768},24,[534,770,771],{"class":540},"        UILaunchScreen",[534,773,557],{"class":544},[534,775,777,780,782],{"class":536,"line":776},25,[534,778,779],{"class":540},"          UIColorName",[534,781,522],{"class":544},[534,783,784],{"class":547}," LaunchBackground\n",[534,786,788,791,793,796,799],{"class":536,"line":787},26,[534,789,790],{"class":540},"        UISupportedInterfaceOrientations",[534,792,522],{"class":544},[534,794,795],{"class":544}," [",[534,797,798],{"class":547},"UIInterfaceOrientationPortrait",[534,800,801],{"class":544},"]\n",[534,803,805],{"class":536,"line":804},27,[534,806,808],{"class":807},"sHwdD","        # UIKit warns and eventually asserts without a scene manifest.\n",[534,810,812,815],{"class":536,"line":811},28,[534,813,814],{"class":540},"        UIApplicationSceneManifest",[534,816,557],{"class":544},[534,818,820,823,825],{"class":536,"line":819},29,[534,821,822],{"class":540},"          UIApplicationSupportsMultipleScenes",[534,824,522],{"class":544},[534,826,827],{"class":604}," false\n",[534,829,831,834],{"class":536,"line":830},30,[534,832,833],{"class":540},"          UISceneConfigurations",[534,835,557],{"class":544},[534,837,839,842],{"class":536,"line":838},31,[534,840,841],{"class":540},"            UIWindowSceneSessionRoleApplication",[534,843,557],{"class":544},[534,845,847,850,853,855],{"class":536,"line":846},32,[534,848,849],{"class":544},"              -",[534,851,852],{"class":540}," UISceneConfigurationName",[534,854,522],{"class":544},[534,856,857],{"class":547}," Default\n",[534,859,861,864,866],{"class":536,"line":860},33,[534,862,863],{"class":540},"                UISceneDelegateClassName",[534,865,522],{"class":544},[534,867,868],{"class":547}," $(PRODUCT_MODULE_NAME).SceneDelegate\n",[534,870,872,875,877],{"class":536,"line":871},34,[534,873,874],{"class":540},"        NSLocalNetworkUsageDescription",[534,876,522],{"class":544},[534,878,879],{"class":547}," Dev builds load the app from your Mac over the local network.\n",[534,881,883,886],{"class":536,"line":882},35,[534,884,885],{"class":540},"        NSAppTransportSecurity",[534,887,557],{"class":544},[534,889,891],{"class":536,"line":890},36,[534,892,893],{"class":807},"          # Dev only. The Release build phase below strips this from the product.\n",[534,895,897,900,902],{"class":536,"line":896},37,[534,898,899],{"class":540},"          NSAllowsArbitraryLoads",[534,901,522],{"class":544},[534,903,605],{"class":604},[534,905,907,910],{"class":536,"line":906},38,[534,908,909],{"class":540},"    settings",[534,911,557],{"class":544},[534,913,915,918],{"class":536,"line":914},39,[534,916,917],{"class":540},"      base",[534,919,557],{"class":544},[534,921,923,926,928,930,933],{"class":536,"line":922},40,[534,924,925],{"class":540},"        SWIFT_VERSION",[534,927,522],{"class":544},[534,929,587],{"class":544},[534,931,932],{"class":547},"5.9",[534,934,593],{"class":544},[534,936,938,941,943,945,947],{"class":536,"line":937},41,[534,939,940],{"class":540},"        IPHONEOS_DEPLOYMENT_TARGET",[534,942,522],{"class":544},[534,944,587],{"class":544},[534,946,590],{"class":547},[534,948,593],{"class":544},[534,950,952,955,957],{"class":536,"line":951},42,[534,953,954],{"class":540},"        CODE_SIGN_STYLE",[534,956,522],{"class":544},[534,958,959],{"class":547}," Automatic\n",[484,961,963],{"id":962},"pinning-the-pods","Pinning the pods",[461,965,966],{},"Lynx and PrimJS version independently, which is what a hand-written Podfile gets wrong first. Lynx 3.9.0 pairs with PrimJS 3.8.0-alpha.6, and CocoaPods will not resolve a prerelease unless you pin it explicitly. Take the current compatible set from the \"Integrate with Existing Apps\" guide on lynxjs.org before copying these numbers.",[524,968,973],{"className":969,"code":970,"filename":971,"language":972,"meta":530,"style":530},"language-ruby shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","source 'https:\u002F\u002Fcdn.cocoapods.org\u002F'\n\nplatform :ios, '15.0'\n\n# Swift code doing `import Lynx` needs the pods built as importable modules.\nuse_modular_headers!\n\ntarget 'VyHost' do\n  pod 'Lynx', '3.9.0', :subspecs => ['Framework']\n  pod 'PrimJS', '3.8.0-alpha.6', :subspecs => ['quickjs', 'napi']\n\n  # Host services: image loading, logging, HTTP.\n  pod 'LynxService', '3.9.0', :subspecs => ['Image', 'Log', 'Http']\n  pod 'SDWebImage', '5.15.5'\n  pod 'SDWebImageWebPCoder', '0.11.0'\n\n  # Extended built-in elements (\u003Cx-input> and friends) that Vy UI renders.\n  pod 'XElement', '3.9.0'\nend\n\npost_install do |installer|\n  installer.pods_project.targets.each do |target|\n    target.build_configurations.each do |config|\n      config.build_settings['GCC_TREAT_WARNINGS_AS_ERRORS'] = 'NO'\n      config.build_settings['SWIFT_TREAT_WARNINGS_AS_ERRORS'] = 'NO'\n      config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '15.0'\n    end\n    # The Lynx podspec attaches -Werror as a per-file compiler flag, which the\n    # build-settings override above cannot reach. On recent Xcode this fails the\n    # build on iOS-13-era deprecations inside Lynx itself.\n    next unless target.respond_to?(:source_build_phase)\n    target.source_build_phase.files.each do |file|\n      flags = file.settings && file.settings['COMPILER_FLAGS']\n      next unless flags && flags.include?('-Werror')\n      file.settings['COMPILER_FLAGS'] = flags.gsub('-Werror', '-Wno-error')\n    end\n  end\nend\n","Podfile","ruby",[472,974,975,980,986,991,995,1000,1005,1009,1014,1019,1024,1028,1033,1038,1043,1048,1052,1057,1062,1067,1071,1076,1081,1086,1091,1096,1101,1106,1111,1116,1121,1126,1131,1136,1141,1146,1150,1155],{"__ignoreMap":530},[534,976,977],{"class":536,"line":537},[534,978,979],{},"source 'https:\u002F\u002Fcdn.cocoapods.org\u002F'\n",[534,981,982],{"class":536,"line":551},[534,983,985],{"emptyLinePlaceholder":984},true,"\n",[534,987,988],{"class":536,"line":560},[534,989,990],{},"platform :ios, '15.0'\n",[534,992,993],{"class":536,"line":571},[534,994,985],{"emptyLinePlaceholder":984},[534,996,997],{"class":536,"line":579},[534,998,999],{},"# Swift code doing `import Lynx` needs the pods built as importable modules.\n",[534,1001,1002],{"class":536,"line":596},[534,1003,1004],{},"use_modular_headers!\n",[534,1006,1007],{"class":536,"line":608},[534,1008,985],{"emptyLinePlaceholder":984},[534,1010,1011],{"class":536,"line":616},[534,1012,1013],{},"target 'VyHost' do\n",[534,1015,1016],{"class":536,"line":624},[534,1017,1018],{},"  pod 'Lynx', '3.9.0', :subspecs => ['Framework']\n",[534,1020,1021],{"class":536,"line":632},[534,1022,1023],{},"  pod 'PrimJS', '3.8.0-alpha.6', :subspecs => ['quickjs', 'napi']\n",[534,1025,1026],{"class":536,"line":640},[534,1027,985],{"emptyLinePlaceholder":984},[534,1029,1030],{"class":536,"line":651},[534,1031,1032],{},"  # Host services: image loading, logging, HTTP.\n",[534,1034,1035],{"class":536,"line":659},[534,1036,1037],{},"  pod 'LynxService', '3.9.0', :subspecs => ['Image', 'Log', 'Http']\n",[534,1039,1040],{"class":536,"line":670},[534,1041,1042],{},"  pod 'SDWebImage', '5.15.5'\n",[534,1044,1045],{"class":536,"line":678},[534,1046,1047],{},"  pod 'SDWebImageWebPCoder', '0.11.0'\n",[534,1049,1050],{"class":536,"line":685},[534,1051,985],{"emptyLinePlaceholder":984},[534,1053,1054],{"class":536,"line":696},[534,1055,1056],{},"  # Extended built-in elements (\u003Cx-input> and friends) that Vy UI renders.\n",[534,1058,1059],{"class":536,"line":707},[534,1060,1061],{},"  pod 'XElement', '3.9.0'\n",[534,1063,1064],{"class":536,"line":715},[534,1065,1066],{},"end\n",[534,1068,1069],{"class":536,"line":729},[534,1070,985],{"emptyLinePlaceholder":984},[534,1072,1073],{"class":536,"line":741},[534,1074,1075],{},"post_install do |installer|\n",[534,1077,1078],{"class":536,"line":749},[534,1079,1080],{},"  installer.pods_project.targets.each do |target|\n",[534,1082,1083],{"class":536,"line":760},[534,1084,1085],{},"    target.build_configurations.each do |config|\n",[534,1087,1088],{"class":536,"line":768},[534,1089,1090],{},"      config.build_settings['GCC_TREAT_WARNINGS_AS_ERRORS'] = 'NO'\n",[534,1092,1093],{"class":536,"line":776},[534,1094,1095],{},"      config.build_settings['SWIFT_TREAT_WARNINGS_AS_ERRORS'] = 'NO'\n",[534,1097,1098],{"class":536,"line":787},[534,1099,1100],{},"      config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '15.0'\n",[534,1102,1103],{"class":536,"line":804},[534,1104,1105],{},"    end\n",[534,1107,1108],{"class":536,"line":811},[534,1109,1110],{},"    # The Lynx podspec attaches -Werror as a per-file compiler flag, which the\n",[534,1112,1113],{"class":536,"line":819},[534,1114,1115],{},"    # build-settings override above cannot reach. On recent Xcode this fails the\n",[534,1117,1118],{"class":536,"line":830},[534,1119,1120],{},"    # build on iOS-13-era deprecations inside Lynx itself.\n",[534,1122,1123],{"class":536,"line":838},[534,1124,1125],{},"    next unless target.respond_to?(:source_build_phase)\n",[534,1127,1128],{"class":536,"line":846},[534,1129,1130],{},"    target.source_build_phase.files.each do |file|\n",[534,1132,1133],{"class":536,"line":860},[534,1134,1135],{},"      flags = file.settings && file.settings['COMPILER_FLAGS']\n",[534,1137,1138],{"class":536,"line":871},[534,1139,1140],{},"      next unless flags && flags.include?('-Werror')\n",[534,1142,1143],{"class":536,"line":882},[534,1144,1145],{},"      file.settings['COMPILER_FLAGS'] = flags.gsub('-Werror', '-Wno-error')\n",[534,1147,1148],{"class":536,"line":890},[534,1149,1105],{},[534,1151,1152],{"class":536,"line":896},[534,1153,1154],{},"  end\n",[534,1156,1157],{"class":536,"line":906},[534,1158,1066],{},[461,1160,1161,1164],{},[472,1162,1163],{},"XElement"," is easy to leave out and annoying to debug, because several Vy UI components render Lynx's extended elements and come up empty rather than throwing without it.",[484,1166,1168],{"id":1167},"the-swift-side","The Swift side",[461,1170,1171],{},"Here are all four files in step order, and the three non-obvious lines are explained underneath. Skip ahead if your host already boots.",[524,1173,1178],{"className":1174,"code":1175,"filename":1176,"language":1177,"meta":530,"style":530},"language-swift shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import Lynx\nimport UIKit\n\n@main\nclass AppDelegate: UIResponder, UIApplicationDelegate {\n    func application(\n        _ application: UIApplication,\n        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?\n    ) -> Bool {\n        LynxEnv.sharedInstance()\n        return true\n    }\n}\n","HostSources\u002FAppDelegate.swift","swift",[472,1179,1180,1185,1190,1194,1199,1204,1209,1214,1219,1224,1229,1234,1239],{"__ignoreMap":530},[534,1181,1182],{"class":536,"line":537},[534,1183,1184],{},"import Lynx\n",[534,1186,1187],{"class":536,"line":551},[534,1188,1189],{},"import UIKit\n",[534,1191,1192],{"class":536,"line":560},[534,1193,985],{"emptyLinePlaceholder":984},[534,1195,1196],{"class":536,"line":571},[534,1197,1198],{},"@main\n",[534,1200,1201],{"class":536,"line":579},[534,1202,1203],{},"class AppDelegate: UIResponder, UIApplicationDelegate {\n",[534,1205,1206],{"class":536,"line":596},[534,1207,1208],{},"    func application(\n",[534,1210,1211],{"class":536,"line":608},[534,1212,1213],{},"        _ application: UIApplication,\n",[534,1215,1216],{"class":536,"line":616},[534,1217,1218],{},"        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?\n",[534,1220,1221],{"class":536,"line":624},[534,1222,1223],{},"    ) -> Bool {\n",[534,1225,1226],{"class":536,"line":632},[534,1227,1228],{},"        LynxEnv.sharedInstance()\n",[534,1230,1231],{"class":536,"line":640},[534,1232,1233],{},"        return true\n",[534,1235,1236],{"class":536,"line":651},[534,1237,1238],{},"    }\n",[534,1240,1241],{"class":536,"line":659},[534,1242,1243],{},"}\n",[524,1245,1248],{"className":1174,"code":1246,"filename":1247,"language":1177,"meta":530,"style":530},"import Lynx\nimport UIKit\n\nclass SceneDelegate: UIResponder, UIWindowSceneDelegate {\n    var window: UIWindow?\n\n    func scene(\n        _ scene: UIScene,\n        willConnectTo session: UISceneSession,\n        options connectionOptions: UIScene.ConnectionOptions\n    ) {\n        guard let windowScene = scene as? UIWindowScene else { return }\n        let window = UIWindow(windowScene: windowScene)\n        window.rootViewController = ViewController()\n        window.makeKeyAndVisible()\n        self.window = window\n    }\n\n    func sceneDidBecomeActive(_ scene: UIScene) {\n        \u002F\u002F safe-area insets read as 0\u002F0 when initLayoutConfig runs before the\n        \u002F\u002F window is key. Re-running it here is a harmless refresh.\n        guard let window else { return }\n        LynxEnv.sharedInstance().initLayoutConfig(window.bounds.size)\n    }\n}\n","HostSources\u002FSceneDelegate.swift",[472,1249,1250,1254,1258,1262,1267,1272,1276,1281,1286,1291,1296,1301,1306,1311,1316,1321,1326,1330,1334,1339,1344,1349,1354,1359,1363],{"__ignoreMap":530},[534,1251,1252],{"class":536,"line":537},[534,1253,1184],{},[534,1255,1256],{"class":536,"line":551},[534,1257,1189],{},[534,1259,1260],{"class":536,"line":560},[534,1261,985],{"emptyLinePlaceholder":984},[534,1263,1264],{"class":536,"line":571},[534,1265,1266],{},"class SceneDelegate: UIResponder, UIWindowSceneDelegate {\n",[534,1268,1269],{"class":536,"line":579},[534,1270,1271],{},"    var window: UIWindow?\n",[534,1273,1274],{"class":536,"line":596},[534,1275,985],{"emptyLinePlaceholder":984},[534,1277,1278],{"class":536,"line":608},[534,1279,1280],{},"    func scene(\n",[534,1282,1283],{"class":536,"line":616},[534,1284,1285],{},"        _ scene: UIScene,\n",[534,1287,1288],{"class":536,"line":624},[534,1289,1290],{},"        willConnectTo session: UISceneSession,\n",[534,1292,1293],{"class":536,"line":632},[534,1294,1295],{},"        options connectionOptions: UIScene.ConnectionOptions\n",[534,1297,1298],{"class":536,"line":640},[534,1299,1300],{},"    ) {\n",[534,1302,1303],{"class":536,"line":651},[534,1304,1305],{},"        guard let windowScene = scene as? UIWindowScene else { return }\n",[534,1307,1308],{"class":536,"line":659},[534,1309,1310],{},"        let window = UIWindow(windowScene: windowScene)\n",[534,1312,1313],{"class":536,"line":670},[534,1314,1315],{},"        window.rootViewController = ViewController()\n",[534,1317,1318],{"class":536,"line":678},[534,1319,1320],{},"        window.makeKeyAndVisible()\n",[534,1322,1323],{"class":536,"line":685},[534,1324,1325],{},"        self.window = window\n",[534,1327,1328],{"class":536,"line":696},[534,1329,1238],{},[534,1331,1332],{"class":536,"line":707},[534,1333,985],{"emptyLinePlaceholder":984},[534,1335,1336],{"class":536,"line":715},[534,1337,1338],{},"    func sceneDidBecomeActive(_ scene: UIScene) {\n",[534,1340,1341],{"class":536,"line":729},[534,1342,1343],{},"        \u002F\u002F safe-area insets read as 0\u002F0 when initLayoutConfig runs before the\n",[534,1345,1346],{"class":536,"line":741},[534,1347,1348],{},"        \u002F\u002F window is key. Re-running it here is a harmless refresh.\n",[534,1350,1351],{"class":536,"line":749},[534,1352,1353],{},"        guard let window else { return }\n",[534,1355,1356],{"class":536,"line":760},[534,1357,1358],{},"        LynxEnv.sharedInstance().initLayoutConfig(window.bounds.size)\n",[534,1360,1361],{"class":536,"line":768},[534,1362,1238],{},[534,1364,1365],{"class":536,"line":776},[534,1366,1243],{},[524,1368,1371],{"className":1174,"code":1369,"filename":1370,"language":1177,"meta":530,"style":530},"import Foundation\nimport Lynx\n\nfinal class TemplateProvider: NSObject, LynxTemplateProvider {\n    func loadTemplate(withUrl urlString: String!, onComplete callback: LynxTemplateLoadBlock!) {\n        guard let urlString, let url = URL(string: urlString) else {\n            callback?(nil, NSError(\n                domain: \"VyHost\",\n                code: 1,\n                userInfo: [NSLocalizedDescriptionKey: \"Bad bundle URL: \\(urlString ?? \"nil\")\"]\n            ))\n            return\n        }\n\n        var request = URLRequest(url: url)\n        request.cachePolicy = .reloadIgnoringLocalCacheData\n\n        URLSession.shared.dataTask(with: request) { data, _, error in\n            callback?(data, error)\n        }.resume()\n    }\n}\n","HostSources\u002FTemplateProvider.swift",[472,1372,1373,1378,1382,1386,1391,1396,1401,1406,1411,1416,1421,1426,1431,1436,1440,1445,1450,1454,1459,1464,1469,1473],{"__ignoreMap":530},[534,1374,1375],{"class":536,"line":537},[534,1376,1377],{},"import Foundation\n",[534,1379,1380],{"class":536,"line":551},[534,1381,1184],{},[534,1383,1384],{"class":536,"line":560},[534,1385,985],{"emptyLinePlaceholder":984},[534,1387,1388],{"class":536,"line":571},[534,1389,1390],{},"final class TemplateProvider: NSObject, LynxTemplateProvider {\n",[534,1392,1393],{"class":536,"line":579},[534,1394,1395],{},"    func loadTemplate(withUrl urlString: String!, onComplete callback: LynxTemplateLoadBlock!) {\n",[534,1397,1398],{"class":536,"line":596},[534,1399,1400],{},"        guard let urlString, let url = URL(string: urlString) else {\n",[534,1402,1403],{"class":536,"line":608},[534,1404,1405],{},"            callback?(nil, NSError(\n",[534,1407,1408],{"class":536,"line":616},[534,1409,1410],{},"                domain: \"VyHost\",\n",[534,1412,1413],{"class":536,"line":624},[534,1414,1415],{},"                code: 1,\n",[534,1417,1418],{"class":536,"line":632},[534,1419,1420],{},"                userInfo: [NSLocalizedDescriptionKey: \"Bad bundle URL: \\(urlString ?? \"nil\")\"]\n",[534,1422,1423],{"class":536,"line":640},[534,1424,1425],{},"            ))\n",[534,1427,1428],{"class":536,"line":651},[534,1429,1430],{},"            return\n",[534,1432,1433],{"class":536,"line":659},[534,1434,1435],{},"        }\n",[534,1437,1438],{"class":536,"line":670},[534,1439,985],{"emptyLinePlaceholder":984},[534,1441,1442],{"class":536,"line":678},[534,1443,1444],{},"        var request = URLRequest(url: url)\n",[534,1446,1447],{"class":536,"line":685},[534,1448,1449],{},"        request.cachePolicy = .reloadIgnoringLocalCacheData\n",[534,1451,1452],{"class":536,"line":696},[534,1453,985],{"emptyLinePlaceholder":984},[534,1455,1456],{"class":536,"line":707},[534,1457,1458],{},"        URLSession.shared.dataTask(with: request) { data, _, error in\n",[534,1460,1461],{"class":536,"line":715},[534,1462,1463],{},"            callback?(data, error)\n",[534,1465,1466],{"class":536,"line":729},[534,1467,1468],{},"        }.resume()\n",[534,1470,1471],{"class":536,"line":741},[534,1472,1238],{},[534,1474,1475],{"class":536,"line":749},[534,1476,1243],{},[524,1478,1481],{"className":1174,"code":1479,"filename":1480,"language":1177,"meta":530,"style":530},"import Lynx\nimport UIKit\n\nclass ViewController: UIViewController {\n    private var lynxView: LynxView?\n\n    override func viewDidLoad() {\n        super.viewDidLoad()\n\n        let lynxView = LynxView { builder in\n            let config = LynxConfig(provider: TemplateProvider())\n            \u002F\u002F Register custom native modules here:\n            \u002F\u002F config.register(MyNativeModule.self)\n            builder.config = config\n            builder.screenSize = self.view.frame.size\n            builder.fontScale = 1.0\n        }\n        lynxView.preferredLayoutWidth = view.frame.size.width\n        lynxView.preferredLayoutHeight = view.frame.size.height\n        lynxView.layoutWidthMode = .exact\n        lynxView.layoutHeightMode = .exact\n        view.addSubview(lynxView)\n        self.lynxView = lynxView\n\n        lynxView.loadTemplate(fromURL: bundleURL, initData: nil)\n    }\n}\n","HostSources\u002FViewController.swift",[472,1482,1483,1487,1491,1495,1500,1505,1509,1514,1519,1523,1528,1533,1538,1543,1548,1553,1558,1562,1567,1572,1577,1582,1587,1592,1596,1601,1605],{"__ignoreMap":530},[534,1484,1485],{"class":536,"line":537},[534,1486,1184],{},[534,1488,1489],{"class":536,"line":551},[534,1490,1189],{},[534,1492,1493],{"class":536,"line":560},[534,1494,985],{"emptyLinePlaceholder":984},[534,1496,1497],{"class":536,"line":571},[534,1498,1499],{},"class ViewController: UIViewController {\n",[534,1501,1502],{"class":536,"line":579},[534,1503,1504],{},"    private var lynxView: LynxView?\n",[534,1506,1507],{"class":536,"line":596},[534,1508,985],{"emptyLinePlaceholder":984},[534,1510,1511],{"class":536,"line":608},[534,1512,1513],{},"    override func viewDidLoad() {\n",[534,1515,1516],{"class":536,"line":616},[534,1517,1518],{},"        super.viewDidLoad()\n",[534,1520,1521],{"class":536,"line":624},[534,1522,985],{"emptyLinePlaceholder":984},[534,1524,1525],{"class":536,"line":632},[534,1526,1527],{},"        let lynxView = LynxView { builder in\n",[534,1529,1530],{"class":536,"line":640},[534,1531,1532],{},"            let config = LynxConfig(provider: TemplateProvider())\n",[534,1534,1535],{"class":536,"line":651},[534,1536,1537],{},"            \u002F\u002F Register custom native modules here:\n",[534,1539,1540],{"class":536,"line":659},[534,1541,1542],{},"            \u002F\u002F config.register(MyNativeModule.self)\n",[534,1544,1545],{"class":536,"line":670},[534,1546,1547],{},"            builder.config = config\n",[534,1549,1550],{"class":536,"line":678},[534,1551,1552],{},"            builder.screenSize = self.view.frame.size\n",[534,1554,1555],{"class":536,"line":685},[534,1556,1557],{},"            builder.fontScale = 1.0\n",[534,1559,1560],{"class":536,"line":696},[534,1561,1435],{},[534,1563,1564],{"class":536,"line":707},[534,1565,1566],{},"        lynxView.preferredLayoutWidth = view.frame.size.width\n",[534,1568,1569],{"class":536,"line":715},[534,1570,1571],{},"        lynxView.preferredLayoutHeight = view.frame.size.height\n",[534,1573,1574],{"class":536,"line":729},[534,1575,1576],{},"        lynxView.layoutWidthMode = .exact\n",[534,1578,1579],{"class":536,"line":741},[534,1580,1581],{},"        lynxView.layoutHeightMode = .exact\n",[534,1583,1584],{"class":536,"line":749},[534,1585,1586],{},"        view.addSubview(lynxView)\n",[534,1588,1589],{"class":536,"line":760},[534,1590,1591],{},"        self.lynxView = lynxView\n",[534,1593,1594],{"class":536,"line":768},[534,1595,985],{"emptyLinePlaceholder":984},[534,1597,1598],{"class":536,"line":776},[534,1599,1600],{},"        lynxView.loadTemplate(fromURL: bundleURL, initData: nil)\n",[534,1602,1603],{"class":536,"line":787},[534,1604,1238],{},[534,1606,1607],{"class":536,"line":804},[534,1608,1243],{},[461,1610,1611,1614,1615,1618,1619,1622],{},[472,1612,1613],{},"AppDelegate"," has nothing to wire beyond ",[472,1616,1617],{},"LynxEnv",", because the ",[472,1620,1621],{},"LynxService"," pods register themselves once linked.",[461,1624,1625,1626,1629,1630,1633,1634,1637,1638,1641,1642,1644],{},"The ",[472,1627,1628],{},"initLayoutConfig"," call in ",[472,1631,1632],{},"sceneDidBecomeActive"," costs an afternoon if you meet it cold. Lynx reads ",[472,1635,1636],{},"env(safe-area-inset-*)"," from statics that call fills from the key window, which is still nil while ",[472,1639,1640],{},"viewDidLoad"," builds the ",[472,1643,474],{},", so the insets arrive as zero and the layout sits under the notch with nothing logged. Re-running it once the scene is active is the fix.",[461,1646,1647,1648,1651,1652,1655],{},"The template provider asks for ",[472,1649,1650],{},"reloadIgnoringLocalCacheData"," because the dev server rewrites the bundle on every save, and ",[472,1653,1654],{},"URLSession"," otherwise hands you the previous one.",[484,1657,1659],{"id":1658},"making-the-dev-loop-bearable","Making the dev loop bearable",[461,1661,1662,1663,1666,1667,1669],{},"Hardcoding ",[472,1664,1665],{},"http:\u002F\u002F192.168.1.x:3000\u002Fmain.lynx.bundle"," works until you join a different network, and then it produces a blank screen with no explanation. A build phase under the target in ",[472,1668,528],{}," writes the Mac's current LAN IP into the app bundle instead:",[524,1671,1673],{"className":526,"code":1672,"filename":528,"language":529,"meta":530,"style":530},"    postBuildScripts:\n      - name: Write dev server host\n        basedOnDependencyAnalysis: false\n        script: |\n          set -u\n          if [ \"${CONFIGURATION}\" = \"Release\" ]; then\n            echo \"Release build — skipping DevServerHost.txt.\"\n            exit 0\n          fi\n          ip=$(ipconfig getifaddr en0 2>\u002Fdev\u002Fnull || ipconfig getifaddr en1 2>\u002Fdev\u002Fnull || echo localhost)\n          DEST_DIR=\"${TARGET_BUILD_DIR}\u002F${UNLOCALIZED_RESOURCES_FOLDER_PATH}\"\n          mkdir -p \"${DEST_DIR}\"\n          printf '%s\\n' \"${ip}\" > \"${DEST_DIR}\u002FDevServerHost.txt\"\n          echo \"Dev server host: ${ip}\"\n",[472,1674,1675,1682,1694,1703,1714,1719,1724,1729,1734,1739,1744,1749,1754,1759],{"__ignoreMap":530},[534,1676,1677,1680],{"class":536,"line":537},[534,1678,1679],{"class":540},"    postBuildScripts",[534,1681,557],{"class":544},[534,1683,1684,1686,1689,1691],{"class":536,"line":551},[534,1685,718],{"class":544},[534,1687,1688],{"class":540}," name",[534,1690,522],{"class":544},[534,1692,1693],{"class":547}," Write dev server host\n",[534,1695,1696,1699,1701],{"class":536,"line":560},[534,1697,1698],{"class":540},"        basedOnDependencyAnalysis",[534,1700,522],{"class":544},[534,1702,827],{"class":604},[534,1704,1705,1708,1710],{"class":536,"line":571},[534,1706,1707],{"class":540},"        script",[534,1709,522],{"class":544},[534,1711,1713],{"class":1712},"s7zQu"," |\n",[534,1715,1716],{"class":536,"line":579},[534,1717,1718],{"class":547},"          set -u\n",[534,1720,1721],{"class":536,"line":596},[534,1722,1723],{"class":547},"          if [ \"${CONFIGURATION}\" = \"Release\" ]; then\n",[534,1725,1726],{"class":536,"line":608},[534,1727,1728],{"class":547},"            echo \"Release build — skipping DevServerHost.txt.\"\n",[534,1730,1731],{"class":536,"line":616},[534,1732,1733],{"class":547},"            exit 0\n",[534,1735,1736],{"class":536,"line":624},[534,1737,1738],{"class":547},"          fi\n",[534,1740,1741],{"class":536,"line":632},[534,1742,1743],{"class":547},"          ip=$(ipconfig getifaddr en0 2>\u002Fdev\u002Fnull || ipconfig getifaddr en1 2>\u002Fdev\u002Fnull || echo localhost)\n",[534,1745,1746],{"class":536,"line":640},[534,1747,1748],{"class":547},"          DEST_DIR=\"${TARGET_BUILD_DIR}\u002F${UNLOCALIZED_RESOURCES_FOLDER_PATH}\"\n",[534,1750,1751],{"class":536,"line":651},[534,1752,1753],{"class":547},"          mkdir -p \"${DEST_DIR}\"\n",[534,1755,1756],{"class":536,"line":659},[534,1757,1758],{"class":547},"          printf '%s\\n' \"${ip}\" > \"${DEST_DIR}\u002FDevServerHost.txt\"\n",[534,1760,1761],{"class":536,"line":670},[534,1762,1763],{"class":547},"          echo \"Dev server host: ${ip}\"\n",[461,1765,1766,1767,1770],{},"Writing to ",[472,1768,1769],{},"TARGET_BUILD_DIR"," keeps the generated file out of the repository. The app then probes ports 3000 through 3010 on that host and takes whichever answers first, so the port shuffling that happens with two projects open stops mattering:",[524,1772,1774],{"className":1174,"code":1773,"filename":1480,"language":1177,"meta":530,"style":530},"#if DEBUG\nprivate let devServerPorts = Array(3000...3010)\n\nprivate let devServerHost: String = {\n    guard let url = Bundle.main.url(forResource: \"DevServerHost\", withExtension: \"txt\"),\n          let contents = try? String(contentsOf: url, encoding: .utf8)\n    else { return \"localhost\" }\n    let host = contents.trimmingCharacters(in: .whitespacesAndNewlines)\n    return host.isEmpty ? \"localhost\" : host\n}()\n\n\u002F\u002F\u002F Probes each candidate port with a cheap HEAD request; first 2xx wins.\nprivate func discoverDevServer(completion: @escaping (String?) -> Void) {\n    func tryPort(at index: Int) {\n        guard index \u003C devServerPorts.count else { return completion(nil) }\n        let candidate = \"http:\u002F\u002F\\(devServerHost):\\(devServerPorts[index])\u002Fmain.lynx.bundle\"\n        guard let url = URL(string: candidate) else { return tryPort(at: index + 1) }\n        var request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 1.0)\n        request.httpMethod = \"HEAD\"\n        URLSession.shared.dataTask(with: request) { _, response, _ in\n            if let http = response as? HTTPURLResponse, (200...299).contains(http.statusCode) {\n                completion(candidate)\n            } else {\n                tryPort(at: index + 1)\n            }\n        }.resume()\n    }\n    tryPort(at: 0)\n}\n#endif\n",[472,1775,1776,1781,1786,1790,1795,1800,1805,1810,1815,1820,1825,1829,1834,1839,1844,1849,1854,1859,1864,1869,1874,1879,1884,1889,1894,1899,1903,1907,1912,1916],{"__ignoreMap":530},[534,1777,1778],{"class":536,"line":537},[534,1779,1780],{},"#if DEBUG\n",[534,1782,1783],{"class":536,"line":551},[534,1784,1785],{},"private let devServerPorts = Array(3000...3010)\n",[534,1787,1788],{"class":536,"line":560},[534,1789,985],{"emptyLinePlaceholder":984},[534,1791,1792],{"class":536,"line":571},[534,1793,1794],{},"private let devServerHost: String = {\n",[534,1796,1797],{"class":536,"line":579},[534,1798,1799],{},"    guard let url = Bundle.main.url(forResource: \"DevServerHost\", withExtension: \"txt\"),\n",[534,1801,1802],{"class":536,"line":596},[534,1803,1804],{},"          let contents = try? String(contentsOf: url, encoding: .utf8)\n",[534,1806,1807],{"class":536,"line":608},[534,1808,1809],{},"    else { return \"localhost\" }\n",[534,1811,1812],{"class":536,"line":616},[534,1813,1814],{},"    let host = contents.trimmingCharacters(in: .whitespacesAndNewlines)\n",[534,1816,1817],{"class":536,"line":624},[534,1818,1819],{},"    return host.isEmpty ? \"localhost\" : host\n",[534,1821,1822],{"class":536,"line":632},[534,1823,1824],{},"}()\n",[534,1826,1827],{"class":536,"line":640},[534,1828,985],{"emptyLinePlaceholder":984},[534,1830,1831],{"class":536,"line":651},[534,1832,1833],{},"\u002F\u002F\u002F Probes each candidate port with a cheap HEAD request; first 2xx wins.\n",[534,1835,1836],{"class":536,"line":659},[534,1837,1838],{},"private func discoverDevServer(completion: @escaping (String?) -> Void) {\n",[534,1840,1841],{"class":536,"line":670},[534,1842,1843],{},"    func tryPort(at index: Int) {\n",[534,1845,1846],{"class":536,"line":678},[534,1847,1848],{},"        guard index \u003C devServerPorts.count else { return completion(nil) }\n",[534,1850,1851],{"class":536,"line":685},[534,1852,1853],{},"        let candidate = \"http:\u002F\u002F\\(devServerHost):\\(devServerPorts[index])\u002Fmain.lynx.bundle\"\n",[534,1855,1856],{"class":536,"line":696},[534,1857,1858],{},"        guard let url = URL(string: candidate) else { return tryPort(at: index + 1) }\n",[534,1860,1861],{"class":536,"line":707},[534,1862,1863],{},"        var request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 1.0)\n",[534,1865,1866],{"class":536,"line":715},[534,1867,1868],{},"        request.httpMethod = \"HEAD\"\n",[534,1870,1871],{"class":536,"line":729},[534,1872,1873],{},"        URLSession.shared.dataTask(with: request) { _, response, _ in\n",[534,1875,1876],{"class":536,"line":741},[534,1877,1878],{},"            if let http = response as? HTTPURLResponse, (200...299).contains(http.statusCode) {\n",[534,1880,1881],{"class":536,"line":749},[534,1882,1883],{},"                completion(candidate)\n",[534,1885,1886],{"class":536,"line":760},[534,1887,1888],{},"            } else {\n",[534,1890,1891],{"class":536,"line":768},[534,1892,1893],{},"                tryPort(at: index + 1)\n",[534,1895,1896],{"class":536,"line":776},[534,1897,1898],{},"            }\n",[534,1900,1901],{"class":536,"line":787},[534,1902,1468],{},[534,1904,1905],{"class":536,"line":804},[534,1906,1238],{},[534,1908,1909],{"class":536,"line":811},[534,1910,1911],{},"    tryPort(at: 0)\n",[534,1913,1914],{"class":536,"line":819},[534,1915,1243],{},[534,1917,1918],{"class":536,"line":830},[534,1919,1920],{},"#endif\n",[461,1922,1923,1924,1927,1928,1931],{},"Adopting ",[472,1925,1926],{},"LynxViewLifecycle"," and rendering ",[472,1929,1930],{},"didRecieveError"," into an on-screen label is worth the twenty lines, because the default failure mode is a blank screen with the real message buried in the Xcode console.",[484,1933,1935],{"id":1934},"running-it","Running it",[524,1937,1941],{"className":1938,"code":1939,"language":1940,"meta":530,"style":530},"language-sh shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","cd native\u002Fios-host\nxcodegen generate     # writes VyHost.xcodeproj from project.yml\npod install           # writes VyHost.xcworkspace\nopen VyHost.xcworkspace\n","sh",[472,1942,1943,1952,1964,1975],{"__ignoreMap":530},[534,1944,1945,1949],{"class":536,"line":537},[534,1946,1948],{"class":1947},"s2Zo4","cd",[534,1950,1951],{"class":547}," native\u002Fios-host\n",[534,1953,1954,1958,1961],{"class":536,"line":551},[534,1955,1957],{"class":1956},"sBMFI","xcodegen",[534,1959,1960],{"class":547}," generate",[534,1962,1963],{"class":807},"     # writes VyHost.xcodeproj from project.yml\n",[534,1965,1966,1969,1972],{"class":536,"line":560},[534,1967,1968],{"class":1956},"pod",[534,1970,1971],{"class":547}," install",[534,1973,1974],{"class":807},"           # writes VyHost.xcworkspace\n",[534,1976,1977,1980],{"class":536,"line":571},[534,1978,1979],{"class":1956},"open",[534,1981,1982],{"class":547}," VyHost.xcworkspace\n",[461,1984,1985,1986,1988,1989,1991,1992,1994],{},"Open the workspace rather than the project, or the pods will not be linked, and re-run both commands after editing ",[472,1987,528],{}," or the ",[472,1990,971],{},". In Xcode, pick your iPhone as the destination, set your team under Signing & Capabilities, and run with ",[472,1993,481],{}," already going so there is a server to find. Saving a component then reloads the bundle on the device, and Xcode is only for native changes after that.",[484,1996,1998],{"id":1997},"registering-native-modules","Registering native modules",[461,2000,2001,2002,2005,2006,2008],{},"A custom native module is a Swift or Objective-C class registered on the ",[472,2003,2004],{},"LynxConfig"," the ",[472,2007,474],{}," builds with:",[524,2010,2012],{"className":1174,"code":2011,"language":1177,"meta":530,"style":530},"let config = LynxConfig(provider: TemplateProvider())\nconfig.register(MyCameraModule.self)\nconfig.register(MyStorageModule.self)\nbuilder.config = config\n",[472,2013,2014,2019,2024,2029],{"__ignoreMap":530},[534,2015,2016],{"class":536,"line":537},[534,2017,2018],{},"let config = LynxConfig(provider: TemplateProvider())\n",[534,2020,2021],{"class":536,"line":551},[534,2022,2023],{},"config.register(MyCameraModule.self)\n",[534,2025,2026],{"class":536,"line":560},[534,2027,2028],{},"config.register(MyStorageModule.self)\n",[534,2030,2031],{"class":536,"line":571},[534,2032,2033],{},"builder.config = config\n",[461,2035,2036],{},"The per-view config above is invisible to the global registration helpers some packages ship, so register explicitly on the config you construct. Compiling the module's sources directly beats wiring a podspec, and XcodeGen pulls them straight from an installed package:",[524,2038,2040],{"className":526,"code":2039,"filename":528,"language":529,"meta":530,"style":530},"    sources:\n      - path: HostSources\n      - path: ..\u002F..\u002Fnode_modules\u002F@some\u002Fnative-package\u002Fios\n        name: NativePackage\n        excludes:\n          - 'README.md'\n",[472,2041,2042,2048,2058,2069,2079,2086],{"__ignoreMap":530},[534,2043,2044,2046],{"class":536,"line":537},[534,2045,710],{"class":540},[534,2047,557],{"class":544},[534,2049,2050,2052,2054,2056],{"class":536,"line":551},[534,2051,718],{"class":544},[534,2053,721],{"class":540},[534,2055,522],{"class":544},[534,2057,726],{"class":547},[534,2059,2060,2062,2064,2066],{"class":536,"line":560},[534,2061,718],{"class":544},[534,2063,721],{"class":540},[534,2065,522],{"class":544},[534,2067,2068],{"class":547}," ..\u002F..\u002Fnode_modules\u002F@some\u002Fnative-package\u002Fios\n",[534,2070,2071,2074,2076],{"class":536,"line":571},[534,2072,2073],{"class":540},"        name",[534,2075,522],{"class":544},[534,2077,2078],{"class":547}," NativePackage\n",[534,2080,2081,2084],{"class":536,"line":579},[534,2082,2083],{"class":540},"        excludes",[534,2085,557],{"class":544},[534,2087,2088,2091,2093,2096],{"class":536,"line":596},[534,2089,2090],{"class":544},"          -",[534,2092,587],{"class":544},[534,2094,2095],{"class":547},"README.md",[534,2097,593],{"class":544},[461,2099,2100,2101,2103,2104,2107,2108,2111,2112,2115],{},"That path needs a ",[472,2102,541],{}," when ",[472,2105,2106],{},"createIntermediateGroups"," is on, because XcodeGen otherwise nests the group under the ",[472,2109,2110],{},"..\u002F.."," tree it derives for ",[472,2113,2114],{},"node_modules",", doubles the path, and finds none of the files.",[484,2117,2119],{"id":2118},"custom-fonts","Custom fonts",[461,2121,2122,2123,2126,2127,2130,2131,2134],{},"A font referenced with ",[472,2124,2125],{},"url(...)"," from your CSS gets rewritten to ",[472,2128,2129],{},"webpack:\u002F\u002F\u002Fstatic\u002Ffont\u002F…",", which native Lynx cannot fetch. Registering the font in the host and calling it ",[472,2132,2133],{},"local(\"Family Name\")"," keeps the bundle small:",[524,2136,2138],{"className":526,"code":2137,"filename":528,"language":529,"meta":530,"style":530},"    sources:\n      - path: ..\u002Fios\u002FResources\u002FFonts\u002FYourFont-Regular.ttf\n        group: Fonts\n        buildPhase: resources\n    info:\n      properties:\n        UIAppFonts:\n          - YourFont-Regular.ttf\n",[472,2139,2140,2146,2157,2167,2177,2183,2189,2196],{"__ignoreMap":530},[534,2141,2142,2144],{"class":536,"line":537},[534,2143,710],{"class":540},[534,2145,557],{"class":544},[534,2147,2148,2150,2152,2154],{"class":536,"line":551},[534,2149,718],{"class":544},[534,2151,721],{"class":540},[534,2153,522],{"class":544},[534,2155,2156],{"class":547}," ..\u002Fios\u002FResources\u002FFonts\u002FYourFont-Regular.ttf\n",[534,2158,2159,2162,2164],{"class":536,"line":560},[534,2160,2161],{"class":540},"        group",[534,2163,522],{"class":544},[534,2165,2166],{"class":547}," Fonts\n",[534,2168,2169,2172,2174],{"class":536,"line":571},[534,2170,2171],{"class":540},"        buildPhase",[534,2173,522],{"class":544},[534,2175,2176],{"class":547}," resources\n",[534,2178,2179,2181],{"class":536,"line":579},[534,2180,744],{"class":540},[534,2182,557],{"class":544},[534,2184,2185,2187],{"class":536,"line":596},[534,2186,763],{"class":540},[534,2188,557],{"class":544},[534,2190,2191,2194],{"class":536,"line":608},[534,2192,2193],{"class":540},"        UIAppFonts",[534,2195,557],{"class":544},[534,2197,2198,2200],{"class":536,"line":616},[534,2199,2090],{"class":544},[534,2201,2202],{"class":547}," YourFont-Regular.ttf\n",[461,2204,2205],{},"The name in your CSS has to match the font's internal family, full, or PostScript name rather than the filename. Inlining it as a data URI is the alternative, and the only option if the app also runs in Lynx Go.",[484,2207,2209],{"id":2208},"shipping-a-release-build","Shipping a Release build",[461,2211,2212],{},"A Release build cannot reach your dev server, so the bundle travels inside the app, and one build phase produces it and copies it in:",[524,2214,2216],{"className":526,"code":2215,"filename":528,"language":529,"meta":530,"style":530},"      - name: Embed Lynx bundle\n        basedOnDependencyAnalysis: false\n        script: |\n          set -u\n          if [ \"${CONFIGURATION}\" != \"Release\" ]; then\n            echo \"Debug build — loading from the dev server instead.\"\n            exit 0\n          fi\n          APP_DIR=\"${SRCROOT}\u002F..\u002F..\"\n          # Xcode script phases get a minimal PATH; node is often managed by fnm.\n          export PATH=\"\u002Fopt\u002Fhomebrew\u002Fbin:\u002Fusr\u002Flocal\u002Fbin:${HOME}\u002F.local\u002Fshare\u002Ffnm\u002Faliases\u002Fdefault\u002Fbin:${PATH}\"\n          PNPM=\"$(command -v pnpm || true)\"\n          if [ -z \"${PNPM}\" ]; then\n            echo \"error: pnpm not found on PATH. Run \\`pnpm build\\` manually, then rebuild.\"\n            exit 1\n          fi\n          (cd \"${APP_DIR}\" && \"${PNPM}\" build) || exit 1\n          DEST_DIR=\"${TARGET_BUILD_DIR}\u002F${UNLOCALIZED_RESOURCES_FOLDER_PATH}\"\n          mkdir -p \"${DEST_DIR}\"\n          cp \"${APP_DIR}\u002Fdist\u002Fmain.lynx.bundle\" \"${DEST_DIR}\u002Fmain.lynx.bundle\"\n",[472,2217,2218,2229,2237,2245,2249,2254,2259,2263,2267,2272,2277,2282,2287,2292,2297,2302,2306,2311,2315,2319],{"__ignoreMap":530},[534,2219,2220,2222,2224,2226],{"class":536,"line":537},[534,2221,718],{"class":544},[534,2223,1688],{"class":540},[534,2225,522],{"class":544},[534,2227,2228],{"class":547}," Embed Lynx bundle\n",[534,2230,2231,2233,2235],{"class":536,"line":551},[534,2232,1698],{"class":540},[534,2234,522],{"class":544},[534,2236,827],{"class":604},[534,2238,2239,2241,2243],{"class":536,"line":560},[534,2240,1707],{"class":540},[534,2242,522],{"class":544},[534,2244,1713],{"class":1712},[534,2246,2247],{"class":536,"line":571},[534,2248,1718],{"class":547},[534,2250,2251],{"class":536,"line":579},[534,2252,2253],{"class":547},"          if [ \"${CONFIGURATION}\" != \"Release\" ]; then\n",[534,2255,2256],{"class":536,"line":596},[534,2257,2258],{"class":547},"            echo \"Debug build — loading from the dev server instead.\"\n",[534,2260,2261],{"class":536,"line":608},[534,2262,1733],{"class":547},[534,2264,2265],{"class":536,"line":616},[534,2266,1738],{"class":547},[534,2268,2269],{"class":536,"line":624},[534,2270,2271],{"class":547},"          APP_DIR=\"${SRCROOT}\u002F..\u002F..\"\n",[534,2273,2274],{"class":536,"line":632},[534,2275,2276],{"class":547},"          # Xcode script phases get a minimal PATH; node is often managed by fnm.\n",[534,2278,2279],{"class":536,"line":640},[534,2280,2281],{"class":547},"          export PATH=\"\u002Fopt\u002Fhomebrew\u002Fbin:\u002Fusr\u002Flocal\u002Fbin:${HOME}\u002F.local\u002Fshare\u002Ffnm\u002Faliases\u002Fdefault\u002Fbin:${PATH}\"\n",[534,2283,2284],{"class":536,"line":651},[534,2285,2286],{"class":547},"          PNPM=\"$(command -v pnpm || true)\"\n",[534,2288,2289],{"class":536,"line":659},[534,2290,2291],{"class":547},"          if [ -z \"${PNPM}\" ]; then\n",[534,2293,2294],{"class":536,"line":670},[534,2295,2296],{"class":547},"            echo \"error: pnpm not found on PATH. Run \\`pnpm build\\` manually, then rebuild.\"\n",[534,2298,2299],{"class":536,"line":678},[534,2300,2301],{"class":547},"            exit 1\n",[534,2303,2304],{"class":536,"line":685},[534,2305,1738],{"class":547},[534,2307,2308],{"class":536,"line":696},[534,2309,2310],{"class":547},"          (cd \"${APP_DIR}\" && \"${PNPM}\" build) || exit 1\n",[534,2312,2313],{"class":536,"line":707},[534,2314,1748],{"class":547},[534,2316,2317],{"class":536,"line":715},[534,2318,1753],{"class":547},[534,2320,2321],{"class":536,"line":729},[534,2322,2323],{"class":547},"          cp \"${APP_DIR}\u002Fdist\u002Fmain.lynx.bundle\" \"${DEST_DIR}\u002Fmain.lynx.bundle\"\n",[461,2325,2326,2327,2330,2331,2334],{},"Running ",[472,2328,2329],{},"pnpm build"," from a script phase needs ",[472,2332,2333],{},"ENABLE_USER_SCRIPT_SANDBOXING: 'NO'"," in the target's base settings, since the sandbox otherwise blocks writes outside the build directory.",[461,2336,2337],{},"A second phase strips the development-only Info.plist keys, so the shipped app carries no ATS exemption and never prompts for local network access:",[524,2339,2341],{"className":526,"code":2340,"filename":528,"language":529,"meta":530,"style":530},"      - name: Strip dev Info.plist keys\n        basedOnDependencyAnalysis: false\n        script: |\n          set -u\n          [ \"${CONFIGURATION}\" = \"Release\" ] || exit 0\n          PLIST=\"${TARGET_BUILD_DIR}\u002F${INFOPLIST_PATH}\"\n          \u002Fusr\u002Flibexec\u002FPlistBuddy -c \"Delete :NSAppTransportSecurity\" \"${PLIST}\" 2>\u002Fdev\u002Fnull || true\n          \u002Fusr\u002Flibexec\u002FPlistBuddy -c \"Delete :NSLocalNetworkUsageDescription\" \"${PLIST}\" 2>\u002Fdev\u002Fnull || true\n",[472,2342,2343,2354,2362,2370,2374,2379,2384,2389],{"__ignoreMap":530},[534,2344,2345,2347,2349,2351],{"class":536,"line":537},[534,2346,718],{"class":544},[534,2348,1688],{"class":540},[534,2350,522],{"class":544},[534,2352,2353],{"class":547}," Strip dev Info.plist keys\n",[534,2355,2356,2358,2360],{"class":536,"line":551},[534,2357,1698],{"class":540},[534,2359,522],{"class":544},[534,2361,827],{"class":604},[534,2363,2364,2366,2368],{"class":536,"line":560},[534,2365,1707],{"class":540},[534,2367,522],{"class":544},[534,2369,1713],{"class":1712},[534,2371,2372],{"class":536,"line":571},[534,2373,1718],{"class":547},[534,2375,2376],{"class":536,"line":579},[534,2377,2378],{"class":547},"          [ \"${CONFIGURATION}\" = \"Release\" ] || exit 0\n",[534,2380,2381],{"class":536,"line":596},[534,2382,2383],{"class":547},"          PLIST=\"${TARGET_BUILD_DIR}\u002F${INFOPLIST_PATH}\"\n",[534,2385,2386],{"class":536,"line":608},[534,2387,2388],{"class":547},"          \u002Fusr\u002Flibexec\u002FPlistBuddy -c \"Delete :NSAppTransportSecurity\" \"${PLIST}\" 2>\u002Fdev\u002Fnull || true\n",[534,2390,2391],{"class":536,"line":616},[534,2392,2393],{"class":547},"          \u002Fusr\u002Flibexec\u002FPlistBuddy -c \"Delete :NSLocalNetworkUsageDescription\" \"${PLIST}\" 2>\u002Fdev\u002Fnull || true\n",[461,2395,2396,2397,2400],{},"Loading the embedded bundle is a ",[472,2398,2399],{},"#if DEBUG"," branch away from the dev-server path, which closes the fifth step:",[524,2402,2404],{"className":1174,"code":2403,"filename":1480,"language":1177,"meta":530,"style":530},"private func loadEmbeddedBundle() {\n    guard let url = Bundle.main.url(forResource: \"main.lynx\", withExtension: \"bundle\") else { return }\n    guard let data = try? Data(contentsOf: url) else { return }\n    lynxView?.loadTemplate(data, withURL: url.absoluteString)\n}\n",[472,2405,2406,2411,2416,2421,2426],{"__ignoreMap":530},[534,2407,2408],{"class":536,"line":537},[534,2409,2410],{},"private func loadEmbeddedBundle() {\n",[534,2412,2413],{"class":536,"line":551},[534,2414,2415],{},"    guard let url = Bundle.main.url(forResource: \"main.lynx\", withExtension: \"bundle\") else { return }\n",[534,2417,2418],{"class":536,"line":560},[534,2419,2420],{},"    guard let data = try? Data(contentsOf: url) else { return }\n",[534,2422,2423],{"class":536,"line":571},[534,2424,2425],{},"    lynxView?.loadTemplate(data, withURL: url.absoluteString)\n",[534,2427,2428],{"class":536,"line":579},[534,2429,1243],{},[2431,2432,2434],"callout",{"icon":2433},"i-lucide-shield-check",[461,2435,2436,2437,2440],{},"Gate the ATS exemption behind Release stripping from the start, because App Review asks about ",[472,2438,2439],{},"NSAllowsArbitraryLoads"," and a build phase that deletes it is easier to explain than keeping it.",[484,2442,2444],{"id":2443},"when-it-does-not-work","When it does not work",[461,2446,2447,2451],{},[2448,2449,2450],"strong",{},"A blank screen"," means the bundle fetch failed, so open the URL in Safari on the phone and blame the network if it fails there too.",[461,2453,2454,2457,2458,501],{},[2448,2455,2456],{},"An engine version mismatch on load"," means the Lynx pod and your rspeedy version disagree, so match the pod to what lynxjs.org pairs with your ",[472,2459,2460],{},"@lynx-js\u002Frspeedy",[461,2462,2463,2466],{},[2448,2464,2465],{},"Pod resolution failing"," usually means the pinned set has drifted, and PrimJS does not track Lynx's numbering.",[461,2468,2469,2475,2476,2479,2480,501],{},[2448,2470,2471,2474],{},[472,2472,2473],{},"-Werror"," errors inside the Lynx pods"," mean the ",[472,2477,2478],{},"post_install"," hook did not run or complete, which shows in the tail of ",[472,2481,2482],{},"pod install",[461,2484,2485,2488,2489,2491,2492,501],{},[2448,2486,2487],{},"Layout sitting under the notch"," is the safe-area problem above, so confirm ",[472,2490,1628],{}," runs from ",[472,2493,1632],{},[461,2495,2496,2499,2500,2502],{},[2448,2497,2498],{},"Components rendering empty"," where you expect an input or a text field usually means the ",[472,2501,1163],{}," pod is missing.",[484,2504,2506],{"id":2505},"related","Related",[2508,2509,2510,2516,2521],"card-group",{},[2511,2512,2513],"card",{"icon":84,"title":433,"to":434},[461,2514,2515],{},"The faster Lynx Go loop, and where it runs out.",[2511,2517,2518],{"icon":17,"title":14,"to":15},[461,2519,2520],{},"The Vy UI build configuration this host expects.",[2511,2522,2523],{"icon":426,"title":423,"to":424},[461,2524,2525],{},"The Vue renderer for Lynx and what changes in your code.",[2527,2528,2529],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}",{"title":530,"searchDepth":537,"depth":551,"links":2531},[2532,2533,2534,2535,2536,2537,2538,2539,2540,2541,2542],{"id":486,"depth":551,"text":487},{"id":510,"depth":551,"text":511},{"id":962,"depth":551,"text":963},{"id":1167,"depth":551,"text":1168},{"id":1658,"depth":551,"text":1659},{"id":1934,"depth":551,"text":1935},{"id":1997,"depth":551,"text":1998},{"id":2118,"depth":551,"text":2119},{"id":2208,"depth":551,"text":2209},{"id":2443,"depth":551,"text":2444},{"id":2505,"depth":551,"text":2506},"Build a minimal iOS host app that embeds the Lynx runtime, loads your bundle from the dev server, and compiles in the native modules Lynx Go cannot provide.","md",{},{"icon":440},{"title":2548,"description":2549},"Testing a Vue Lynx app live in Xcode on a real iPhone","A working iOS host app for Vue Lynx — XcodeGen spec, CocoaPods pin set, Swift bootstrap, dev-server discovery, and the Release path that embeds the bundle.","oSWCwkiUUWiSIMwhLJH2FgHdYvFYhBm8ENY5U9yL-4c",[2552,2554],{"title":433,"path":434,"stem":435,"description":2553,"icon":-1,"children":-1,"navigation":-1},"How to run a Vue Lynx app on a real iPhone or Android device with Lynx Go, and where the QR-code workflow stops being enough.",{"title":442,"path":443,"stem":444,"description":2555,"icon":-1,"children":-1,"navigation":-1},"What rspeedy dev prints, how to cycle the QR code between entries and URL schemas from the terminal, and which of the three URLs to use when.",1785008680369]