Comments (11)
I tried this one and it seems to work: https://hisaac.net/how-to-detect-if-your-macos-app-was-launched-as-a-login-item/
from launchatlogin.
I was able to implement the solution linked to by @longseespace. Link has changed though: https://hisaac.net/blog/how-to-detect-if-your-macos-app-was-launched-as-a-login-item/.
from launchatlogin.
Another solution could be to launch the main app with a command line flag (for example "LaunchedAtLogin", so that it can look if it was launched with that flag. It would be great if the name of that flag is customizable.
from launchatlogin.
@cbjeukendrup That's the obvious solution, yes, but unfortunately doesn't work with sandboxing:
If the calling process is sandboxed, the system ignores the value of this property. - https://developer.apple.com/documentation/appkit/nsworkspace/openconfiguration/3172708-arguments
from launchatlogin.
Ah, sorry, missed that one. That makes it a lot more complicated...
from launchatlogin.
I was able to do this by creating an App Group and in the launcher set:
UserDefaults(suiteName: "[TeamId][AppGroupId]")?.set(true, forKey: "didLaunchAtLogin")
Then in the main app, read that value then clear it.
It might not be feasible for a drop in library like this to set that up, but at least it's a simple solution.
from launchatlogin.
@gpoitch That's a good workaround. Thanks for sharing. However, I don't want to require App Group for LaunchAtLogin users, so I think the more complicated solution is a better fit for LaunchAtLogin.
from launchatlogin.
I took the approach opposite to that of @sindresorhus by having the main app asking the helper app to terminate. IMO, this approach ensures that both the main and helper app are initialized properly and only then, can exchange messages.
I used the simple DistributedNotificationCenter
mechanism, which seems to work well for my case so far. Here's my code:
In LaunchAtLogin
enum:
// Requires AppKit to be imported
public static let wasLaunchedOnLogin: Bool = {
let bundleIdentifier = Bundle.main.bundleIdentifier!
let mainBundleIdentifier = "\(bundleIdentifier)-LaunchAtLoginHelper"
// If helper is not running
if NSRunningApplication.runningApplications(withBundleIdentifier: mainBundleIdentifier).isEmpty {
return false
} else {
// Helper is running, ask it to terminate
let nc = DistributedNotificationCenter.default()
nc.post(name: NSNotification.Name("TerminateHelper"), object: nil)
return true
}
}()
AppDelegate
class in LaunchAtLoginHelper
:
final class AppDelegate: NSObject, NSApplicationDelegate {
let nc = DistributedNotificationCenter.default()
@objc func terminateApp() {
NSLog("LaunchAtLogin helper terminateApp called")
nc.removeObserver(self)
NSApp.terminate(nil)
}
func applicationDidFinishLaunching(_ notification: Notification) {
let bundleIdentifier = Bundle.main.bundleIdentifier!
let mainBundleIdentifier = bundleIdentifier.replacingOccurrences(of: #"-LaunchAtLoginHelper$"#, with: "", options: .regularExpression)
// Ensures the app is not already running.
guard NSRunningApplication.runningApplications(withBundleIdentifier: mainBundleIdentifier).isEmpty else {
NSApp.terminate(nil)
return
}
let pathComponents = (Bundle.main.bundlePath as NSString).pathComponents
let mainPath = NSString.path(withComponents: Array(pathComponents[0...(pathComponents.count - 5)]))
NSWorkspace.shared.launchApplication(mainPath)
nc.addObserver(self, selector: #selector(terminateApp), name: NSNotification.Name("TerminateHelper"), object: nil)
NSLog("LaunchAtLogin helper exited")
}
}
This seems to work on my sandboxed app running on my developer MacBook Pro. Any feedback is appreciated.
from launchatlogin.
The tricks mentioned in this thread will no longer work with the new API for launch at login in macOS 13: https://developer.apple.com/documentation/servicemanagement/smappservice/3945412-mainapp?changes=latest_minor
So we need some other ideas.
Please also submit feedback to Apple (using the Feedback Assistant app) that there needs to be an API for this. Like I have done in: feedback-assistant/reports#337
from launchatlogin.
@sindresorhus I agree that a helper app is no longer required and with new API, one could simply register mainAppService to be launched at login, but I think it would still be necessary for this use case, as the API doesn't provide any way to differentiate between automatic login launch and manual launches.
To that end, I've explored Apple's documentation on sandboxed applications and came across this peculiar bit on this link:
With App Sandbox, you can receive Apple events and respond to Apple events, but you cannot send Apple events to arbitrary apps.
I found an answer on StackOverflow related to Apple events: https://stackoverflow.com/a/19890943/4385319
And here's what I've tried so far in my main app:
In my AppDelegate, I added the following code:
func applicationWillFinishLaunching(_ notification: Notification) {
NSAppleEventManager.shared().setEventHandler(self, andSelector: #selector(AppDelegate.handleEvent(_:withReplyEvent:)), forEventClass: AEEventClass(kCoreEventClass), andEventID: AEEventID(kAEOpenApplication))
}
Now my trouble begins:
@objc func handleEvent(_ event: NSAppleEventDescriptor!, withReplyEvent: NSAppleEventDescriptor!) {
// I'm trying to compare the following two things (as per the answer found on StackOverflow):
// event?.paramDescriptor(forKeyword: keyAEPropData)?.enumCodeValue
// keyAELaunchedAsLogInItem
}
So far, the expression event?.paramDescriptor(forKeyword: keyAEPropData)?.enumCodeValue
returns nil when my app is launched at login (using LaunchAtLogin framework on macOS 12) and is non-nil optional value otherwise (when I manually open it).
To those who have deeper knowledge on this topic, here's my question: Is checking the above expression for nil and non-nil a reliable way to say that my app was launched at login or not?
Apologies if I missed something.
EDIT: Upon further testing, I can see that whenever the packaged app is launched from:
- Finder/Autologin/Xcode: Expression is nil
- Launchpad/Spotlight: Expression is non-nil
I have no words here... 😖
from launchatlogin.
I see this thread has gone quiet but I'll add a not-so-great option to the mix. Perhaps we could use the time since last login
as a proxy for whether the app was launched at login or not. It is by no means perfect, however, if the OS doesn't emit an event when an app is started at login, what reliable options are available?
from launchatlogin.
Related Issues (20)
- New login item added for every app build
- "Invalid Code Signature Identifier". I can't validate my app for App Store HOT 2
- Not working with intel Mac. HOT 2
- Xcode 14.0 - warning build: Run script build phase 'Copy “Launch at Login Helper”' will be run during every build ...
- Parent application is being launched instead of the launcher HOT 1
- App not launching at login HOT 3
- Add a "modern" target that doesn't include the old helper app HOT 1
- Signing for "LaunchAtLogin_LaunchAtLogin" requires a development team in xcode 14 HOT 1
- Allow the usage of legacy approach on macOS 13.0 HOT 2
- Cannot find 'SMAppService' in scope HOT 1
- Could not enable login item, even when using Carthage
- `migrateIfNeeded` is undocumented HOT 2
- Is there some way to enable it by default for Mac apps outside the AppStore? HOT 4
- Invalid Provisioning Profile Signature HOT 2
- Cannot find 'SMAppService' in scope HOT 2
- ITMS-90886: 'Cannot be used with TestFlight because the signature for the bundle at LaunchAtLoginHelper.app is missing an application identifier but has an application identifier in the provisioning profile for the bundle. HOT 1
- Platform filter to exclude iOS in xcode multiplatform targets HOT 2
- Build failed in sandboxed app on Xcode 15.0 with User Script Sandboxing set to YES by default HOT 2
- Command PhaseScriptExecution failed with a nonzero exit code HOT 1
- Error when archive app HOT 3
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from launchatlogin.