Skip to content

Commit 049bf85

Browse files
committed
refactor(macos): adopt SwiftUI App lifecycle with NSViewRepresentable
Replace the AppKit-based AppDelegate template with a SwiftUI App that hosts the React Native root view via NSViewRepresentable. - Use @main on a SwiftUI App struct; remove main.swift - Use Window scene with .defaultSize(1280x720) instead of manual NSWindow - Wrap factory.rootViewFactory.view(withModuleName:) in NSViewRepresentable - Drop the ~130 line NSMenu.standardMenu extension; SwiftUI provides the standard App, Edit, View, Window, and Help menus by default - Initialize RCTReactNativeFactory in AppDelegate.init() instead of applicationDidFinishLaunching, so it's ready when the scene body evaluates Reduces the template AppDelegate from 181 to 59 lines.
1 parent a2e7825 commit 049bf85

3 files changed

Lines changed: 30 additions & 157 deletions

File tree

Lines changed: 26 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,32 @@
1-
import Cocoa
1+
import SwiftUI
22
import React
33
import React_RCTAppDelegate
44
import ReactAppDependencyProvider
55

6+
@main
7+
struct HelloWorldApp: App {
8+
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
9+
10+
var body: some Scene {
11+
Window("HelloWorld", id: "main") {
12+
ReactNativeView(factory: appDelegate.reactNativeFactory)
13+
}
14+
.defaultSize(width: 1280, height: 720)
15+
}
16+
}
17+
18+
// MARK: - App Delegate
19+
620
class AppDelegate: NSObject, NSApplicationDelegate {
7-
var window: NSWindow?
8-
var reactNativeDelegate: ReactNativeDelegate?
9-
var reactNativeFactory: RCTReactNativeFactory?
21+
private let reactNativeDelegate: ReactNativeDelegate
22+
let reactNativeFactory: RCTReactNativeFactory
1023

11-
func applicationDidFinishLaunching(_ notification: Notification) {
24+
override init() {
1225
let delegate = ReactNativeDelegate()
13-
let factory = RCTReactNativeFactory(delegate: delegate)
1426
delegate.dependencyProvider = RCTAppDependencyProvider()
15-
1627
reactNativeDelegate = delegate
17-
reactNativeFactory = factory
18-
19-
window = NSWindow(
20-
contentRect: NSMakeRect(0, 0, 1280, 720),
21-
styleMask: [.titled, .closable, .resizable, .miniaturizable],
22-
backing: .buffered,
23-
defer: false
24-
)
25-
26-
NSApp.mainMenu = .standardMenu(appName: "HelloWorld")
27-
28-
factory.startReactNative(withModuleName: "HelloWorld", in: window)
28+
reactNativeFactory = RCTReactNativeFactory(delegate: delegate)
29+
super.init()
2930
}
3031
}
3132

@@ -45,137 +46,14 @@ class ReactNativeDelegate: RCTDefaultReactNativeFactoryDelegate {
4546
}
4647
}
4748

48-
// MARK: - Standard macOS Menu Bar
49-
50-
extension NSMenu {
51-
/// Creates a standard macOS menu bar with App, Edit, View, Window, and Help menus.
52-
///
53-
/// This provides the default set of menu items that most macOS apps need, including
54-
/// keyboard shortcuts for Quit (⌘Q), Close (⌘W), Copy (⌘C), Paste (⌘V), and others.
55-
/// You can customize the returned menu by adding, removing, or modifying items.
56-
static func standardMenu(appName: String) -> NSMenu {
57-
let mainMenu = NSMenu()
58-
59-
mainMenu.addItem(appMenuItem(appName: appName))
60-
mainMenu.addItem(editMenuItem())
61-
mainMenu.addItem(viewMenuItem())
62-
mainMenu.addItem(windowMenuItem())
63-
mainMenu.addItem(helpMenuItem())
64-
65-
return mainMenu
66-
}
67-
68-
// MARK: App Menu
69-
70-
private static func appMenuItem(appName: String) -> NSMenuItem {
71-
let item = NSMenuItem()
72-
let menu = NSMenu()
73-
74-
menu.addItem(title: "About \(appName)",
75-
action: #selector(NSApplication.orderFrontStandardAboutPanel(_:)))
76-
menu.addItem(.separator())
77-
78-
let servicesMenu = NSMenu(title: "Services")
79-
let servicesItem = NSMenuItem(title: "Services", action: nil, keyEquivalent: "")
80-
servicesItem.submenu = servicesMenu
81-
menu.addItem(servicesItem)
82-
NSApp.servicesMenu = servicesMenu
83-
menu.addItem(.separator())
84-
85-
menu.addItem(title: "Hide \(appName)",
86-
action: #selector(NSApplication.hide(_:)), key: "h")
87-
menu.addItem(title: "Hide Others",
88-
action: #selector(NSApplication.hideOtherApplications(_:)),
89-
key: "h", modifiers: [.command, .option])
90-
menu.addItem(title: "Show All",
91-
action: #selector(NSApplication.unhideAllApplications(_:)))
92-
menu.addItem(.separator())
93-
menu.addItem(title: "Quit \(appName)",
94-
action: #selector(NSApplication.terminate(_:)), key: "q")
95-
96-
item.submenu = menu
97-
return item
98-
}
99-
100-
// MARK: Edit Menu
101-
102-
private static func editMenuItem() -> NSMenuItem {
103-
let item = NSMenuItem()
104-
let menu = NSMenu(title: "Edit")
105-
106-
menu.addItem(title: "Undo", action: Selector(("undo:")), key: "z")
107-
menu.addItem(title: "Redo", action: Selector(("redo:")), key: "Z")
108-
menu.addItem(.separator())
109-
menu.addItem(title: "Cut", action: #selector(NSText.cut(_:)), key: "x")
110-
menu.addItem(title: "Copy", action: #selector(NSText.copy(_:)), key: "c")
111-
menu.addItem(title: "Paste", action: #selector(NSText.paste(_:)), key: "v")
112-
menu.addItem(title: "Paste and Match Style",
113-
action: #selector(NSTextView.pasteAsPlainText(_:)),
114-
key: "v", modifiers: [.command, .option])
115-
menu.addItem(title: "Delete", action: #selector(NSText.delete(_:)))
116-
menu.addItem(title: "Select All", action: #selector(NSText.selectAll(_:)), key: "a")
117-
118-
item.submenu = menu
119-
return item
120-
}
121-
122-
// MARK: View Menu
123-
124-
private static func viewMenuItem() -> NSMenuItem {
125-
let item = NSMenuItem()
126-
let menu = NSMenu(title: "View")
127-
128-
menu.addItem(title: "Enter Full Screen",
129-
action: #selector(NSWindow.toggleFullScreen(_:)),
130-
key: "f", modifiers: [.command, .control])
131-
132-
item.submenu = menu
133-
return item
134-
}
135-
136-
// MARK: Window Menu
137-
138-
private static func windowMenuItem() -> NSMenuItem {
139-
let item = NSMenuItem()
140-
let menu = NSMenu(title: "Window")
141-
142-
menu.addItem(title: "Close", action: #selector(NSWindow.performClose(_:)), key: "w")
143-
menu.addItem(title: "Minimize", action: #selector(NSWindow.performMiniaturize(_:)), key: "m")
144-
menu.addItem(title: "Zoom", action: #selector(NSWindow.performZoom(_:)))
145-
menu.addItem(.separator())
146-
menu.addItem(title: "Bring All to Front",
147-
action: #selector(NSApplication.arrangeInFront(_:)))
148-
149-
NSApp.windowsMenu = menu
150-
151-
item.submenu = menu
152-
return item
153-
}
154-
155-
// MARK: Help Menu
156-
157-
private static func helpMenuItem() -> NSMenuItem {
158-
let item = NSMenuItem()
159-
let menu = NSMenu(title: "Help")
49+
// MARK: - React Native SwiftUI View
16050

161-
NSApp.helpMenu = menu
51+
struct ReactNativeView: NSViewRepresentable {
52+
let factory: RCTReactNativeFactory
16253

163-
item.submenu = menu
164-
return item
54+
func makeNSView(context: Context) -> NSView {
55+
factory.rootViewFactory.view(withModuleName: "HelloWorld")
16556
}
16657

167-
// MARK: Convenience
168-
169-
/// Adds a menu item with an optional keyboard shortcut and modifier keys.
170-
@discardableResult
171-
fileprivate func addItem(
172-
title: String,
173-
action: Selector?,
174-
key: String = "",
175-
modifiers: NSEvent.ModifierFlags = .command
176-
) -> NSMenuItem {
177-
let item = addItem(withTitle: title, action: action, keyEquivalent: key)
178-
item.keyEquivalentModifierMask = modifiers
179-
return item
180-
}
58+
func updateNSView(_ nsView: NSView, context: Context) {}
18159
}

packages/react-native/local-cli/generator-macos/templates/macos/HelloWorld-macOS/main.swift

Lines changed: 0 additions & 5 deletions
This file was deleted.

packages/react-native/local-cli/generator-macos/templates/macos/HelloWorld.xcodeproj/project.pbxproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@
99
/* Begin PBXBuildFile section */
1010
5142014D2437B4B30078DB4F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5142014C2437B4B30078DB4F /* AppDelegate.swift */; };
1111
514201522437B4B40078DB4F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 514201512437B4B40078DB4F /* Assets.xcassets */; };
12-
514201582437B4B40078DB4F /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 514201572437B4B40078DB4F /* main.swift */; };
12+
1313
/* End PBXBuildFile section */
1414

1515
/* Begin PBXFileReference section */
1616
13B07F961A680F5B00A75B9A /* HelloWorld.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HelloWorld.app; sourceTree = BUILT_PRODUCTS_DIR; };
1717
514201492437B4B30078DB4F /* HelloWorld.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HelloWorld.app; sourceTree = BUILT_PRODUCTS_DIR; };
1818
5142014C2437B4B30078DB4F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
1919
514201512437B4B40078DB4F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
20-
514201572437B4B40078DB4F /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
20+
2121
514201562437B4B40078DB4F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
2222
514201592437B4B40078DB4F /* HelloWorld.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = HelloWorld.entitlements; sourceTree = "<group>"; };
2323
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
@@ -53,7 +53,7 @@
5353
isa = PBXGroup;
5454
children = (
5555
5142014C2437B4B30078DB4F /* AppDelegate.swift */,
56-
514201572437B4B40078DB4F /* main.swift */,
56+
5757
514201512437B4B40078DB4F /* Assets.xcassets */,
5858
514201562437B4B40078DB4F /* Info.plist */,
5959
514201592437B4B40078DB4F /* HelloWorld.entitlements */,
@@ -275,7 +275,7 @@
275275
isa = PBXSourcesBuildPhase;
276276
buildActionMask = 2147483647;
277277
files = (
278-
514201582437B4B40078DB4F /* main.swift in Sources */,
278+
279279
5142014D2437B4B30078DB4F /* AppDelegate.swift in Sources */,
280280
);
281281
runOnlyForDeploymentPostprocessing = 0;

0 commit comments

Comments
 (0)