{"id":39511,"date":"2023-05-26T14:41:04","date_gmt":"2023-05-26T18:41:04","guid":{"rendered":"https:\/\/mjtsai.com\/blog\/?p=39511"},"modified":"2025-12-16T15:49:06","modified_gmt":"2025-12-16T20:49:06","slug":"wallaroo-and-swiftui-on-macos","status":"publish","type":"post","link":"https:\/\/mjtsai.com\/blog\/2023\/05\/26\/wallaroo-and-swiftui-on-macos\/","title":{"rendered":"Wallaroo and SwiftUI on macOS"},"content":{"rendered":"<p><a href=\"https:\/\/blog.iconfactory.com\/2023\/03\/wallaroo-a-journey-from-ios-to-macos-part-1\/\">Craig Hockenberry<\/a>:<\/p>\n<blockquote cite=\"https:\/\/blog.iconfactory.com\/2023\/03\/wallaroo-a-journey-from-ios-to-macos-part-1\/\"><p>We have a <code>View<\/code> in the app that uses SceneKit to display a 3D scene with a confetti flourish after a customer starts a subscription. I was expecting stuff like that to take a lot of effort to get working on the Mac. Instead, it ran 100% out of the box with no modifications at all.<\/p><p>And the stuff that I was expecting to be easy, like a settings view, buttons, and menu commands, turned out to be hard. To put that &ldquo;hard&rdquo; in context, it took me about a month to go from an app that we were proud of on iOS to one that we were equally proud of on macOS[&#8230;]<\/p><p>[&#8230;]<\/p><p>We also found that many of the issues encountered on macOS were things we had done wrong on iOS. Porting the app to the Mac made <em>both<\/em> platforms better. We&rsquo;re also rethinking our View architecture so things that are currently Mac-only can be used to improve the iPad experience.<\/p><p>[&#8230;]<\/p><p>You&rsquo;re going to have platform-specific code. More than you realize: certainly more than I expected!<\/p>\n<p>[&#8230;]<\/p>\n<p>It took me three days to hook up a Save menu item and have it work reliably.<\/p><\/blockquote>\n\n<p>I don&rsquo;t understand why they had to use a custom button style and a hard-coded radius to get buttons that looked good on both platforms. I thought the point of SwiftUI was that you&rsquo;re supposed ot just specify that you want a button and it does the right thing on each platform.<\/p>\n\n<p><a href=\"https:\/\/blog.iconfactory.com\/2023\/03\/wallaroo-a-journey-from-ios-to-macos-part-2\/\">Craig Hockenberry<\/a>:<\/p>\n<blockquote cite=\"https:\/\/blog.iconfactory.com\/2023\/03\/wallaroo-a-journey-from-ios-to-macos-part-2\/\">\n<p>SwiftUI&rsquo;s default mechanism for handling URLs on macOS isn&rsquo;t great. If you&rsquo;ve used <code>.onOpenURL<\/code> on a <code>View<\/code> to handle external events, you&rsquo;ll be disappointed to learn that a URL scheme on macOS opens a new window (<code>Scene<\/code>) before it hands the URL to the view modifier. And if you&rsquo;re using a single <code>Window<\/code> instead of a <code>WindowGroup<\/code>, nothing happens at all.<\/p>\n<p>[&#8230;]<\/p>\n<p>One thing we wanted to do was use the Escape key to pop the <code>NavigationStack<\/code>. On the Mac, it&rsquo;s a key that has always represented &ldquo;go back&rdquo; and many apps (like Photos) take advantage of that. Unfortunately, to accomplish that in Wallaroo, we had to use an <em>invisible<\/em> <code>ToolbarItem<\/code> with a <code>.keyboardShortcut(.escape, modifiers: [])<\/code>.<\/p>\n<p>[&#8230;]<\/p>\n<p>While you&rsquo;re struggling with window and sidebar size constraints, you end up with <code>UserDefaults<\/code> for frames that have &ldquo;fun&rdquo; keys like <code>\"NSWindow Frame SwiftUI.(unknown context at $1acd3d768).SceneBridgeReader, SwiftUI.ModifiedContent&gt;, SwiftUI._PreferenceWritingModifierSwiftUI.PreferredColorSchemeKey&gt;, SwiftUI._EnvironmentKeyWritingModifier&gt;&gt;&gt;&gt;-1-AppWindow-1\"<\/code>.<\/p>\n<\/blockquote>\n\n<p><a href=\"https:\/\/blog.iconfactory.com\/2023\/03\/wallaroo-a-journey-from-ios-to-macos-part-3\/\">Craig Hockenberry<\/a> (<a href=\"https:\/\/mastodon.social\/@chockenberry\/110079033972964761\">Mastodon<\/a>):<\/p>\n<blockquote cite=\"https:\/\/blog.iconfactory.com\/2023\/03\/wallaroo-a-journey-from-ios-to-macos-part-3\/\"><p>You won&rsquo;t realize how much you depend on SwiftUI previews until you start working on macOS.<\/p><p>Sometimes they kinda work, but more often than not you&rsquo;ll get an endless spinner. When they do work, they&rsquo;re not interactive (you have to use the selectable mode with the arrow icon).<\/p><p>[&#8230;]<\/p><p>The similarity of the key names makes this a hard lesson to learn. I looked at &ldquo;aps-environment&rdquo; and &ldquo;com.apple.developer.aps-environment&rdquo; for hours before I realized they were different. After you add <a href=\"https:\/\/developer.apple.com\/documentation\/bundleresources\/entitlements\/com_apple_developer_aps-environment\">the one for macOS<\/a> manually, everything is golden.<\/p><p>[&#8230;]<\/p><p>We weren&rsquo;t able to get <code>FileRepresentation<\/code> working in a <code>Transferable<\/code>. Other applications can&rsquo;t access a temporary file that&rsquo;s created for the export. This feels like a sandbox problem that we couldn&rsquo;t work around.<\/p><\/blockquote>\n\n<p>Previously:<\/p>\n<ul>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2023\/05\/02\/wallaroo-and-swiftui-on-ios\/\">Wallaroo and SwiftUI on iOS<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2023\/03\/14\/converting-the-streaks-apple-watch-app-to-swiftui\/\">Converting the Streaks Apple Watch App to SwiftUI<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2022\/06\/17\/thoughts-on-swiftui-after-wwdc-2022\/\">Thoughts on SwiftUI After WWDC 2022<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2022\/06\/17\/swiftui-changes-at-wwdc-2022\/\">SwiftUI Changes at WWDC 2022<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2018\/07\/26\/conditional-compilation-in-swift\/\">Conditional Compilation in Swift<\/a><\/li>\n<\/ul>","protected":false},"excerpt":{"rendered":"<p>Craig Hockenberry: We have a View in the app that uses SceneKit to display a 3D scene with a confetti flourish after a customer starts a subscription. I was expecting stuff like that to take a lot of effort to get working on the Mac. Instead, it ran 100% out of the box with no [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"apple_news_api_created_at":"2023-05-26T18:41:08Z","apple_news_api_id":"a223a88d-5976-4de5-b2b3-8373113cd1ea","apple_news_api_modified_at":"2023-12-06T19:19:25Z","apple_news_api_revision":"AAAAAAAAAAAAAAAAAAAAAA==","apple_news_api_share_url":"https:\/\/apple.news\/AoiOojVl2TeWys4NzETzR6g","apple_news_coverimage":0,"apple_news_coverimage_caption":"","apple_news_is_hidden":false,"apple_news_is_paid":false,"apple_news_is_preview":false,"apple_news_is_sponsored":false,"apple_news_maturity_rating":"","apple_news_metadata":"\"\"","apple_news_pullquote":"","apple_news_pullquote_position":"","apple_news_slug":"","apple_news_sections":"\"\"","apple_news_suppress_video_url":false,"apple_news_use_image_component":false,"footnotes":""},"categories":[4],"tags":[2518,30,32,2223,2868,71,53,901,1812,2016,2373,226],"class_list":["post-39511","post","type-post","status-publish","format-standard","hentry","category-programming-category","tag-entitlements","tag-mac","tag-macapp","tag-macos-13-ventura","tag-nsuserdefaults","tag-programming","tag-sandboxing","tag-swift-programming-language","tag-swiftui","tag-testflight","tag-wallaroo","tag-xcode"],"apple_news_notices":[],"_links":{"self":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/39511","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/comments?post=39511"}],"version-history":[{"count":1,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/39511\/revisions"}],"predecessor-version":[{"id":39512,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/39511\/revisions\/39512"}],"wp:attachment":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/media?parent=39511"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/categories?post=39511"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/tags?post=39511"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}