diff --git a/LaunchAtLoginHelper/LLManager.h b/LaunchAtLoginHelper/LLManager.h new file mode 100644 index 0000000..d532390 --- /dev/null +++ b/LaunchAtLoginHelper/LLManager.h @@ -0,0 +1,18 @@ +// +// LLManager.h +// LaunchAtLogin +// +// Created by David Keegan on 4/20/12. +// Copyright (c) 2012 David Keegan. All rights reserved. +// + +#import + +@interface LLManager : NSObject + ++ (BOOL)launchAtLogin; ++ (void)setLaunchAtLogin:(BOOL)value; + +@property (assign) BOOL launchAtLogin; + +@end diff --git a/LaunchAtLoginHelper/LLManager.m b/LaunchAtLoginHelper/LLManager.m new file mode 100644 index 0000000..39af3cf --- /dev/null +++ b/LaunchAtLoginHelper/LLManager.m @@ -0,0 +1,53 @@ +// +// LLManager.m +// LaunchAtLogin +// +// Created by David Keegan on 4/20/12. +// Copyright (c) 2012 David Keegan. All rights reserved. +// + +#import "LLManager.h" +#import "LLStrings.h" +#import + +@implementation LLManager + ++ (BOOL)launchAtLogin{ + BOOL launch = NO; + CFArrayRef cfJobs = SMCopyAllJobDictionaries(kSMDomainUserLaunchd); +#if __has_feature(objc_arc) + NSArray *jobs = [NSArray arrayWithArray:(__bridge NSArray *)cfJobs]; +#else + NSArray *jobs = [NSArray arrayWithArray:(NSArray *)cfJobs]; +#endif + CFRelease(cfJobs); + if([jobs count]){ + for(NSDictionary *job in jobs){ + if([job[@"Label"] isEqualToString:LLHelperBundleIdentifier]){ + launch = [job[@"OnDemand"] boolValue]; + break; + } + } + } + return launch; +} + ++ (void)setLaunchAtLogin:(BOOL)value{ + if(!SMLoginItemSetEnabled((CFStringRef)LLHelperBundleIdentifier, value)){ + NSLog(@"SMLoginItemSetEnabled failed!"); + } +} + +#pragma mark - Bindings support + +- (BOOL)launchAtLogin { + return [[self class] launchAtLogin]; +} + +- (void)setLaunchAtLogin:(BOOL)launchAtLogin { + [self willChangeValueForKey:@"launchAtLogin"]; + [[self class] setLaunchAtLogin:launchAtLogin]; + [self didChangeValueForKey:@"launchAtLogin"]; +} + +@end diff --git a/LaunchAtLoginHelper/LLStrings.h b/LaunchAtLoginHelper/LLStrings.h new file mode 100644 index 0000000..80548f9 --- /dev/null +++ b/LaunchAtLoginHelper/LLStrings.h @@ -0,0 +1,5 @@ +// strings used by LLManager and LaunchAtLoginHelper +// + +#define LLURLScheme @"com.codler.Battery-Time-Remaining" +#define LLHelperBundleIdentifier @"com.codler.Battery-Time-Remaining-Helper" diff --git a/LaunchAtLoginHelper/LaunchAtLoginHelper.xcodeproj/project.pbxproj b/LaunchAtLoginHelper/LaunchAtLoginHelper.xcodeproj/project.pbxproj new file mode 100644 index 0000000..1c604f9 --- /dev/null +++ b/LaunchAtLoginHelper/LaunchAtLoginHelper.xcodeproj/project.pbxproj @@ -0,0 +1,243 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 734C379915414CE200994189 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 734C379815414CE200994189 /* Cocoa.framework */; }; + 73C920E915414E7800C2A75A /* LLHAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 73C920E615414E7800C2A75A /* LLHAppDelegate.m */; }; + 73C920EA15414E7800C2A75A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 73C920E715414E7800C2A75A /* main.m */; }; + 73C920EB15414E7800C2A75A /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 73C920E815414E7800C2A75A /* MainMenu.xib */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 734C379415414CE200994189 /* LaunchAtLoginHelper.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LaunchAtLoginHelper.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 734C379815414CE200994189 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; + 734C379B15414CE200994189 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; + 734C379D15414CE200994189 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 73C920E515414E7800C2A75A /* LLHAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LLHAppDelegate.h; path = LaunchAtLoginHelper/LLHAppDelegate.h; sourceTree = ""; }; + 73C920E615414E7800C2A75A /* LLHAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = LLHAppDelegate.m; path = LaunchAtLoginHelper/LLHAppDelegate.m; sourceTree = ""; }; + 73C920E715414E7800C2A75A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = LaunchAtLoginHelper/main.m; sourceTree = ""; }; + 73C920E815414E7800C2A75A /* MainMenu.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = MainMenu.xib; path = LaunchAtLoginHelper/MainMenu.xib; sourceTree = ""; }; + 73C920FA15414F7000C2A75A /* LLStrings.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LLStrings.h; sourceTree = SOURCE_ROOT; }; + 73C920FB15414FC900C2A75A /* LaunchAtLoginHelper.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = LaunchAtLoginHelper.entitlements; path = LaunchAtLoginHelper/LaunchAtLoginHelper.entitlements; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 734C379115414CE200994189 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 734C379915414CE200994189 /* Cocoa.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 734C378915414CE200994189 = { + isa = PBXGroup; + children = ( + 73C920E715414E7800C2A75A /* main.m */, + 73C920FA15414F7000C2A75A /* LLStrings.h */, + 73C920E515414E7800C2A75A /* LLHAppDelegate.h */, + 73C920E615414E7800C2A75A /* LLHAppDelegate.m */, + 73C920E815414E7800C2A75A /* MainMenu.xib */, + 73C920FB15414FC900C2A75A /* LaunchAtLoginHelper.entitlements */, + 734C379715414CE200994189 /* Frameworks */, + 734C379515414CE200994189 /* Products */, + ); + sourceTree = ""; + }; + 734C379515414CE200994189 /* Products */ = { + isa = PBXGroup; + children = ( + 734C379415414CE200994189 /* LaunchAtLoginHelper.app */, + ); + name = Products; + sourceTree = ""; + }; + 734C379715414CE200994189 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 734C379B15414CE200994189 /* AppKit.framework */, + 734C379D15414CE200994189 /* Foundation.framework */, + 734C379815414CE200994189 /* Cocoa.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 734C379315414CE200994189 /* LaunchAtLoginHelper */ = { + isa = PBXNativeTarget; + buildConfigurationList = 734C37B215414CE200994189 /* Build configuration list for PBXNativeTarget "LaunchAtLoginHelper" */; + buildPhases = ( + 734C379015414CE200994189 /* Sources */, + 734C379115414CE200994189 /* Frameworks */, + 734C379215414CE200994189 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = LaunchAtLoginHelper; + productName = LaunchAtLoginHelper; + productReference = 734C379415414CE200994189 /* LaunchAtLoginHelper.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 734C378B15414CE200994189 /* Project object */ = { + isa = PBXProject; + attributes = { + CLASSPREFIX = LLH; + LastUpgradeCheck = 0450; + ORGANIZATIONNAME = "David Keegan"; + }; + buildConfigurationList = 734C378E15414CE200994189 /* Build configuration list for PBXProject "LaunchAtLoginHelper" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 734C378915414CE200994189; + productRefGroup = 734C379515414CE200994189 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 734C379315414CE200994189 /* LaunchAtLoginHelper */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 734C379215414CE200994189 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 73C920EB15414E7800C2A75A /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 734C379015414CE200994189 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 73C920E915414E7800C2A75A /* LLHAppDelegate.m in Sources */, + 73C920EA15414E7800C2A75A /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 734C37B015414CE200994189 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.6; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + 734C37B115414CE200994189 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.6; + SDKROOT = macosx; + SKIP_INSTALL = YES; + }; + name = Release; + }; + 734C37B315414CE200994189 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = LaunchAtLoginHelper/LaunchAtLoginHelper.entitlements; + CODE_SIGN_IDENTITY = "Mac Developer"; + INFOPLIST_FILE = "LaunchAtLoginHelper/LaunchAtLoginHelper-Info.plist"; + MACOSX_DEPLOYMENT_TARGET = 10.6.8; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = ""; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + 734C37B415414CE200994189 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = LaunchAtLoginHelper/LaunchAtLoginHelper.entitlements; + CODE_SIGN_IDENTITY = "3rd Party Mac Developer Application"; + INFOPLIST_FILE = "LaunchAtLoginHelper/LaunchAtLoginHelper-Info.plist"; + MACOSX_DEPLOYMENT_TARGET = 10.6.8; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = ""; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 734C378E15414CE200994189 /* Build configuration list for PBXProject "LaunchAtLoginHelper" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 734C37B015414CE200994189 /* Debug */, + 734C37B115414CE200994189 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 734C37B215414CE200994189 /* Build configuration list for PBXNativeTarget "LaunchAtLoginHelper" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 734C37B315414CE200994189 /* Debug */, + 734C37B415414CE200994189 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 734C378B15414CE200994189 /* Project object */; +} diff --git a/LaunchAtLoginHelper/LaunchAtLoginHelper/LLHAppDelegate.h b/LaunchAtLoginHelper/LaunchAtLoginHelper/LLHAppDelegate.h new file mode 100644 index 0000000..e2bcdc6 --- /dev/null +++ b/LaunchAtLoginHelper/LaunchAtLoginHelper/LLHAppDelegate.h @@ -0,0 +1,13 @@ +// +// LLHAppDelegate.h +// LaunchAtLoginHelper +// +// Created by David Keegan on 4/20/12. +// Copyright (c) 2012 David Keegan. All rights reserved. +// + +#import + +@interface LLHAppDelegate : NSObject + +@end diff --git a/LaunchAtLoginHelper/LaunchAtLoginHelper/LLHAppDelegate.m b/LaunchAtLoginHelper/LaunchAtLoginHelper/LLHAppDelegate.m new file mode 100644 index 0000000..86009c5 --- /dev/null +++ b/LaunchAtLoginHelper/LaunchAtLoginHelper/LLHAppDelegate.m @@ -0,0 +1,26 @@ +// +// LLHAppDelegate.m +// LaunchAtLoginHelper +// +// Created by David Keegan on 4/20/12. +// Copyright (c) 2012 David Keegan. All rights reserved. +// + +#import "LLHAppDelegate.h" +#import "LLStrings.h" + +@implementation LLHAppDelegate + +- (void)applicationDidFinishLaunching:(NSNotification *)notification{ + // Call the scheme to launch the app + NSString *scheme = [NSString stringWithFormat:@"%@://", LLURLScheme]; + [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:scheme]]; + + // Call the app again this time with `launchedAtLogin` so it knows how it was launched + NSString *schemeLaunchedAtLogin = + [NSString stringWithFormat:@"%@://launchedAtLogin", LLURLScheme]; + [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:schemeLaunchedAtLogin]]; + [NSApp terminate:self]; +} + +@end diff --git a/LaunchAtLoginHelper/LaunchAtLoginHelper/LaunchAtLoginHelper-Info.plist b/LaunchAtLoginHelper/LaunchAtLoginHelper/LaunchAtLoginHelper-Info.plist new file mode 100644 index 0000000..5c97b21 --- /dev/null +++ b/LaunchAtLoginHelper/LaunchAtLoginHelper/LaunchAtLoginHelper-Info.plist @@ -0,0 +1,36 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + + CFBundleIdentifier + com.codler.Battery-Time-Remaining-Helper + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSMinimumSystemVersion + ${MACOSX_DEPLOYMENT_TARGET} + LSUIElement + + NSHumanReadableCopyright + Copyright © 2012 David Keegan. All rights reserved. + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/LaunchAtLoginHelper/LaunchAtLoginHelper/LaunchAtLoginHelper-InfoBase.plist b/LaunchAtLoginHelper/LaunchAtLoginHelper/LaunchAtLoginHelper-InfoBase.plist new file mode 100644 index 0000000..a20f41e --- /dev/null +++ b/LaunchAtLoginHelper/LaunchAtLoginHelper/LaunchAtLoginHelper-InfoBase.plist @@ -0,0 +1,36 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + + CFBundleIdentifier + com.InScopeApps.ShellTo.${PRODUCT_NAME:rfc1034identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSMinimumSystemVersion + ${MACOSX_DEPLOYMENT_TARGET} + LSUIElement + + NSHumanReadableCopyright + Copyright © 2012 David Keegan. All rights reserved. + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/LaunchAtLoginHelper/LaunchAtLoginHelper/LaunchAtLoginHelper.entitlements b/LaunchAtLoginHelper/LaunchAtLoginHelper/LaunchAtLoginHelper.entitlements new file mode 100644 index 0000000..852fa1a --- /dev/null +++ b/LaunchAtLoginHelper/LaunchAtLoginHelper/LaunchAtLoginHelper.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + diff --git a/LaunchAtLoginHelper/LaunchAtLoginHelper/MainMenu.xib b/LaunchAtLoginHelper/LaunchAtLoginHelper/MainMenu.xib new file mode 100644 index 0000000..7795112 --- /dev/null +++ b/LaunchAtLoginHelper/LaunchAtLoginHelper/MainMenu.xib @@ -0,0 +1,104 @@ + + + + 1070 + 11D50 + 2182 + 1138.32 + 568.00 + + com.apple.InterfaceBuilder.CocoaPlugin + 2182 + + + NSCustomObject + + + com.apple.InterfaceBuilder.CocoaPlugin + + + PluginDependencyRecalculationVersion + + + + + NSApplication + + + FirstResponder + + + NSApplication + + + LLHAppDelegate + + + + + + + delegate + + + + 495 + + + + + + 0 + + + + + + -2 + + + File's Owner + + + -1 + + + First Responder + + + -3 + + + Application + + + 494 + + + + + + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + + + + + 535 + + + 0 + IBCocoaFramework + + com.apple.InterfaceBuilder.CocoaPlugin.macosx + + + YES + 3 + YES + + diff --git a/LaunchAtLoginHelper/LaunchAtLoginHelper/main.m b/LaunchAtLoginHelper/LaunchAtLoginHelper/main.m new file mode 100644 index 0000000..1a1c3be --- /dev/null +++ b/LaunchAtLoginHelper/LaunchAtLoginHelper/main.m @@ -0,0 +1,13 @@ +// +// main.m +// LaunchAtLoginHelper +// +// Created by David Keegan on 4/20/12. +// Copyright (c) 2012 David Keegan. All rights reserved. +// + +#import + +int main(int argc, char *argv[]){ + return NSApplicationMain(argc, (const char **)argv); +} diff --git a/LaunchAtLoginHelper/readme.md b/LaunchAtLoginHelper/readme.md new file mode 100644 index 0000000..b718dd6 --- /dev/null +++ b/LaunchAtLoginHelper/readme.md @@ -0,0 +1,56 @@ +When creating a sandboxed app `LSSharedFileListInsertItemURL` can no longer be used to launch the app at startup. Instead `SMLoginItemSetEnabled` should be used with a helper app inside the main app's bundle that launches the main app. **LaunchAtLoginHelper** is that helper app, it is designed to be as easy as possible to integrate into the main app to allow it to launch at login when sandboxed. + +A lot of research was put into this helper app. For example [Apple's docs](http://developer.apple.com/library/mac/#documentation/Security/Conceptual/AppSandboxDesignGuide/DesigningYourSandbox/DesigningYourSandbox.html#//apple_ref/doc/uid/TP40011183-CH4-SW3) state that `LSRegisterURL` should be used to register the helper app, however this never seemed to work and after further digging it turns out this is a [typo in the docs](https://devforums.apple.com/message/647212#647212). Many examples I found online used `NSWorkspace launchApplication:` to launch the main app, however this was blocked by sandboxing so a url scheme is used instead. + +**LaunchAtLoginHelper** calls the main app's scheme twice, once to launchg the app and then again with `launchedAtLogin` so the main app can know if it has been launched at login. For example [Play by Play](http://playbyplayapp.com) uses this to hide the app if it was launched at login. + +This project contains a [sample app](https://github.com/kgn/LaunchAtLoginHelper/tree/master/LaunchAtLoginSample) to demonstrate how the main app should be configured and how to setup a checkbox to enable and disable launching at login. + +# How to use + +First download or even better submodule **LaunchAtLoginHelper**. To clone the repository as a submodule use the following commands: + +``` +$ cd +$ git clone --recursive https://github.com/kgn/LaunchAtLoginHelper.git +``` + +**LaunchAtLoginHelper** uses a url scheme to launch the main app, if the main app doesn't have a url scheme yet add one. + +![](http://kgn.github.com/content/launchatlogin/url_scheme.png) + +There are two files missing from this repo that are specific to your instance of the helper app. To generate these files run the `setup.py` python script and pass in the url scheme to launch the main app and the bundle identifier of the helper app, this is usually based of of the bundle identifier of the main app but with *Helper* added onto the end. + +``` +$ cd LaunchAtLoginHelper +$ python setup.py +``` + +This will create `LLStrings.h` which is used in both the helper app and the main app and contains `#define`'s for the url scheme and the helper app's bundle identifier. `LaunchAtLoginHelper-Info.plist` is also created for the helper app with it's custom bundle identifier filled in. + +Once these two files are generated it's time to add **LaunchAtLoginHelper** to the main app. Drag `LaunchAtLoginHelper.xcodeproj`, `LLStrings.h`, `LLManager.h`, and `LLManager.m` to the main app's project. + +![](http://kgn.github.com/content/launchatlogin/drag_drop_file.png) + +Next go to the *Build Phases* for the main app and add **LaunchAtLoginHelper** as a *Target Dependency* and create a new *Copy Files Build Phase*. Set the *Destination* of this build phase to `Wrapper` and the *Subpath* to `Contents/Library/LoginItems`. + +![](http://kgn.github.com/content/launchatlogin/build_phases.png) + +Lastly add `ServiceManagement.framework` to the main app. + +Once this is done use `LLManager` to enable and disable launching at login! [**LaunchAtLoginSample**](https://github.com/kgn/LaunchAtLoginHelper/blob/master/LaunchAtLoginSample/LLAppDelegate.m) shows how to hook this up to a checkbox. + +``` obj-c +#import "LLManager.h" + +[LLManager launchAtLogin] // will the app launch at login? +[LLManager setLaunchAtLogin:YES] // set the app to launch at login +``` + +# Bindings + +The `LLManager` class supports KVO and Cocoa Bindings. This allows for a completely code-free implementaion of this class. To get started, open the Interface Builder document in which you plan to create a login toggle. Drag a generic `NSObject` from the Utilities pane, and drop it onto your canvas. Select the newly created object, and open the Identity inspector tab in the Utilities pane. Change the class from `NSObject` to `LLManager`. Now, select to your login toggle (checkbox) and open the Bindings inspector in the Utilities pane. Expand `Value`, check the "Bind to", and select the name of the `LLManager` object you created earlier. Set the key path to `self.launchAtLogin`. You're done! + +--- + +Special thanks to [Curtis Hard](http://www.geekygoodness.com) for offering some much needed advice on this project. \ No newline at end of file diff --git a/LaunchAtLoginHelper/setup.py b/LaunchAtLoginHelper/setup.py new file mode 100644 index 0000000..7814fa0 --- /dev/null +++ b/LaunchAtLoginHelper/setup.py @@ -0,0 +1,22 @@ +import sys, os +import plistlib + +urlScheme = sys.argv[1] +bundleIdentifier = sys.argv[2] + +directory = os.path.dirname(os.path.abspath(__file__)) + +stringsOutput = os.path.join(directory, 'LLStrings.h') +infoPlistOutput = os.path.join(directory, 'LaunchAtLoginHelper/LaunchAtLoginHelper-Info.plist') +infoPlist = plistlib.readPlist(os.path.join(directory, 'LaunchAtLoginHelper/LaunchAtLoginHelper-InfoBase.plist')) + +with open(stringsOutput, 'w') as strings: + strings.write("""// strings used by LLManager and LaunchAtLoginHelper +// + +#define LLURLScheme @"%(urlScheme)s" +#define LLHelperBundleIdentifier @"%(bundleIdentifier)s" +"""%locals()) + +infoPlist['CFBundleIdentifier'] = bundleIdentifier +plistlib.writePlist(infoPlist, infoPlistOutput)