Skip to content

Commit b75b2b5

Browse files
committed
Support macOS SMAppService for login items
Integrate macOS ServiceManagement SMAppService into the LaunchAtLogin implementation and update docs/examples. Changes: - src/platform/macos/launch_at_login_macos.mm: Add conditional import for ServiceManagement, gate macOS 13+ SMAppService usage, remove legacy LaunchAgents file plumbing, and implement registration/unregistration via SMAppService. Add helpers (IsCurrentProgram, CanUseConfiguredProgram) and track default id/program to enforce that SMAppService can only register the main app or bundled helper (no arbitrary executable paths or ProgramArguments). - src/CMakeLists.txt: Link the ServiceManagement framework when building for Apple targets. - src/launch_at_login.h and src/capi/launch_at_login_c.h: Update API docs to note macOS SMAppService behavior and limitations (no arbitrary executable paths/arguments for main-app login items). - examples/*: Update C and C++ examples to use the default constructor on macOS (which registers the main app) and avoid setting program/arguments on macOS where not supported. Reasoning: On modern macOS (13+), SMAppService is the supported API for login items and does not accept arbitrary program paths or ProgramArguments for the main app; this change adopts SMAppService where available, documents the platform limitations, and prevents attempts to register unsupported configurations.
1 parent ce2af9a commit b75b2b5

6 files changed

Lines changed: 143 additions & 124 deletions

File tree

examples/launch_at_login_c_example/main.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,17 @@ int main(void) {
1414

1515
printf("LaunchAtLogin is supported on this platform.\n\n");
1616

17+
#if defined(__APPLE__)
18+
/*
19+
* On macOS, the default constructor registers the main app with SMAppService.
20+
* Custom identifiers are for bundled login item helpers.
21+
*/
22+
native_launch_at_login_t launch_at_login = native_launch_at_login_create();
23+
#else
1724
/* Create a LaunchAtLogin manager with a custom identifier and display name */
1825
native_launch_at_login_t launch_at_login =
1926
native_launch_at_login_create_with_id_and_name("com.example.myapp.c", "My C Example App");
27+
#endif
2028
if (!launch_at_login) {
2129
printf("Failed to create LaunchAtLogin instance.\n");
2230
return 1;
@@ -35,16 +43,22 @@ int main(void) {
3543
free_c_str(id);
3644
free_c_str(display_name);
3745

46+
#if !defined(__APPLE__)
3847
/* Set a custom program path and arguments */
3948
const char* arguments[] = {"--minimized", "--launch_at_login"};
4049
native_launch_at_login_set_program(launch_at_login, executable ? executable : "", arguments, 2);
50+
#endif
4151
free_c_str(executable);
4252

4353
/* Retrieve and display the updated executable path */
4454
char* exec_path = native_launch_at_login_get_executable_path(launch_at_login);
4555
printf("After SetProgram:\n");
4656
printf(" Executable: %s\n", exec_path ? exec_path : "(null)");
57+
#if defined(__APPLE__)
58+
printf(" Arguments: (not supported by macOS SMAppService main-app login items)\n\n");
59+
#else
4760
printf(" Arguments: --minimized --launch_at_login\n\n");
61+
#endif
4862
free_c_str(exec_path);
4963

5064
/* Check current state before enabling */

examples/launch_at_login_example/main.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,26 @@ int main() {
1818

1919
std::cout << "LaunchAtLogin is supported on this platform.\n\n";
2020

21-
// Create a LaunchAtLogin manager with a custom identifier and display name
21+
// On macOS, the default constructor registers the main app with SMAppService.
22+
// Custom identifiers are for bundled login item helpers.
23+
#if defined(__APPLE__)
24+
LaunchAtLogin launch_at_login;
25+
#else
2226
LaunchAtLogin launch_at_login("com.example.myapp", "My Example App");
27+
#endif
2328

2429
// Display current configuration
2530
std::cout << "LaunchAtLogin configuration:\n";
2631
std::cout << " ID: " << launch_at_login.GetId() << "\n";
2732
std::cout << " Display name: " << launch_at_login.GetDisplayName() << "\n";
2833
std::cout << " Executable: " << launch_at_login.GetExecutablePath() << "\n\n";
2934

35+
// macOS SMAppService main-app login items do not support arbitrary arguments.
36+
#if !defined(__APPLE__)
3037
// Set a custom program path and arguments
3138
launch_at_login.SetProgram(launch_at_login.GetExecutablePath(),
3239
{"--minimized", "--launch_at_login"});
40+
#endif
3341

3442
std::cout << "After SetProgram:\n";
3543
std::cout << " Executable: " << launch_at_login.GetExecutablePath() << "\n";

src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux")
7676
elseif(APPLE)
7777
target_link_libraries(nativeapi PUBLIC "-framework Cocoa")
7878
target_link_libraries(nativeapi PUBLIC "-framework Carbon")
79+
target_link_libraries(nativeapi PUBLIC "-framework ServiceManagement")
7980
target_compile_options(nativeapi PRIVATE "-x" "objective-c++")
8081
elseif(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
8182
target_link_libraries(nativeapi PUBLIC hilog_ndk.z)

src/capi/launch_at_login_c.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ extern "C" {
1717
*
1818
* Platform behavior:
1919
* - Windows: HKCU\Software\Microsoft\Windows\CurrentVersion\Run registry value
20-
* - macOS: Launch Agents in ~/Library/LaunchAgents (plist with ProgramArguments)
20+
* - macOS: ServiceManagement SMAppService for the main app or bundled login item helpers
2121
* - Linux: XDG autostart in ~/.config/autostart (Desktop Entry Exec line)
2222
* - Mobile (Android/iOS/OHOS): Typically unsupported (functions return false)
2323
*
@@ -106,6 +106,8 @@ bool native_launch_at_login_set_display_name(native_launch_at_login_t launch_at_
106106
*
107107
* If not set, the implementation attempts to use the current process executable.
108108
* Pass NULL for arguments or argument_count == 0 when no arguments are needed.
109+
* On macOS, SMAppService does not support arbitrary executable paths or arguments for
110+
* main-app login items.
109111
*
110112
* @param launch_at_login LaunchAtLogin handle.
111113
* @param executable_path Absolute path to the executable to launch on login.

src/launch_at_login.h

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,18 @@ namespace nativeapi {
1515
*
1616
* Platform implementations:
1717
* - Windows: HKCU\Software\Microsoft\Windows\CurrentVersion\Run registry key
18-
* - macOS: Launch Agents (~/Library/LaunchAgents/[app_id].plist) with ProgramArguments
18+
* - macOS: ServiceManagement SMAppService for the main app or bundled login item helpers
1919
* - Linux (XDG): ~/.config/autostart/[app_id].desktop
2020
* - Android/iOS/OHOS: Not supported (returns false from IsSupported/operations)
2121
*
2222
* Notes:
2323
* - This API is intended for desktop environments. On mobile platforms this API is
2424
* typically unsupported by design. Methods will fail gracefully.
2525
* - You can let the implementation determine the current executable path, or call
26-
* SetProgram() to customize the target binary and arguments recorded in the OS.
26+
* SetProgram() to customize the target binary and arguments recorded in the OS where the
27+
* platform supports arbitrary launch commands. On macOS, SMAppService can only register
28+
* the main app or a bundled helper, so custom executable paths and arguments are not
29+
* supported.
2730
* - Some platforms may require application-specific permissions or entitlements
2831
* (e.g., sandbox restrictions on macOS). In such cases, operations may fail.
2932
*
@@ -33,7 +36,7 @@ namespace nativeapi {
3336
*
3437
* if (LaunchAtLogin::IsSupported()) {
3538
* LaunchAtLogin launch_at_login("com.example.myapp", "MyApp");
36-
* // Optionally override the program and arguments (defaults to current executable):
39+
* // Optionally override the program and arguments where supported:
3740
* launch_at_login.SetProgram("/usr/local/bin/myapp", {"--minimized"});
3841
*
3942
* launch_at_login.Enable();
@@ -65,7 +68,8 @@ class LaunchAtLogin {
6568
* @param id A stable, unique identifier for your app.
6669
* Examples:
6770
* - Windows: used as the registry value name, e.g., "MyApp"
68-
* - macOS: used as LaunchAgent Label, e.g., "com.example.myapp"
71+
* - macOS: bundle identifier for a bundled LoginItem helper, e.g.,
72+
* "com.example.myapp.Helper"; the default constructor registers the main app
6973
* - Linux: used as the .desktop file name (without extension), e.g., "myapp"
7074
*
7175
* Recommendation: Use a reverse-DNS identifier when possible, e.g., "com.example.myapp".
@@ -99,8 +103,7 @@ class LaunchAtLogin {
99103
/**
100104
* @brief Set a human-readable display name used where applicable.
101105
*
102-
* Some platforms surface a name in their UI (e.g., Linux .desktop Name, macOS LaunchAgent Label
103-
* often mirrors the identifier but may display the name in some tools).
106+
* Some platforms surface a name in their UI (e.g., Linux .desktop Name).
104107
*
105108
* @param display_name The human-readable name.
106109
* @return true if the value was stored locally; does not change OS registration until Enable().
@@ -112,8 +115,9 @@ class LaunchAtLogin {
112115
*
113116
* If not set, implementations will try to use the current process executable path.
114117
* On platforms that require a single string (e.g., Windows registry), arguments will
115-
* be encoded appropriately (quoted when needed). On macOS/Linux, arguments are stored
116-
* as vector items (.plist ProgramArguments / .desktop Exec respectively).
118+
* be encoded appropriately (quoted when needed). On Linux, arguments are stored in
119+
* the .desktop Exec line. On macOS, SMAppService does not support arbitrary
120+
* executable paths or arguments for main-app login items.
117121
*
118122
* @param executable_path Absolute path to the executable to run on login.
119123
* @param arguments Optional arguments; order is preserved.

0 commit comments

Comments
 (0)