16 Commits
Author SHA1 Message Date
Sindre Sorhus c34d3c7171 Meta tweaks 2021-08-15 11:33:45 +02:00
Sindre Sorhus afaacda259 Add related package to readme 2021-05-01 18:40:27 +07:00
Sindre Sorhus 6b16bcdf7d 4.1.0 2021-03-20 18:42:16 +07:00
Sindre Sorhus f1cc3b8dd5 Add native Apple Silicon support to the helper app
Fixes #53
2021-03-20 17:36:24 +07:00
Sindre Sorhus b068272af8 Add ITSAppUsesNonExemptEncryption to the helper app
Fixes #55
2021-03-20 16:55:08 +07:00
Sindre Sorhus 389e09faf7 Minor tweaks
Fixes #49
Fixes #51
2021-03-20 16:55:07 +07:00
2badfdd7df Fix notarization issues with build configs not named "Release" (#57)
Co-authored-by: Javi Ramírez <javi.rmrz@gmail.com>
Co-authored-by: Sindre Sorhus <sindresorhus@gmail.com>
2021-03-20 16:17:12 +07:00
Sindre Sorhus 0f39982b9d 4.0.0 2020-09-17 17:42:51 +02:00
Sindre Sorhus 7f469e4511 Minor tweaks 2020-09-17 17:30:57 +02:00
Sergey KuryanovandSindre Sorhus c16509926a Swift Package Manager support (#41)
Co-authored-by: Sindre Sorhus <sindresorhus@gmail.com>
2020-09-17 17:29:51 +02:00
Sindre Sorhus 3708ba7f4f Upgrade to Swift 5.3 2020-09-17 00:30:27 +02:00
Sindre Sorhus 08b9b397c8 Cleanup 2020-08-02 14:22:39 +02:00
Sergey Kuryanov f7b255bc96 Add KVO, Combine, and SwiftUI support (#42) 2020-08-02 13:59:03 +02:00
Sindre Sorhus 090e6e069b 3.0.2 2020-07-25 08:39:32 +02:00
Philippe Casgrain 0fe5eb50ff Fix building on Apple Silicon (#43) 2020-07-25 08:38:26 +02:00
Sindre Sorhus e7c842de54 Add FAQ for build error
Closes #34
2020-06-10 02:43:09 +08:00
21 changed files with 461 additions and 74 deletions
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>
+39 -19
View File
@@ -3,10 +3,12 @@
archiveVersion = 1;
classes = {
};
objectVersion = 52;
objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
D92CFD2224C5D909005B91BE /* Toggle.swift in Sources */ = {isa = PBXBuildFile; fileRef = D92CFD2124C5D909005B91BE /* Toggle.swift */; };
D9EF7E1D24BCF9E300A1E9AA /* copy-helper-swiftpm.sh in Resources */ = {isa = PBXBuildFile; fileRef = D9EF7E1C24BCF9E300A1E9AA /* copy-helper-swiftpm.sh */; };
E32E9B681EB87D7B000FEEE9 /* LaunchAtLogin.h in Headers */ = {isa = PBXBuildFile; fileRef = E32E9B661EB87D7B000FEEE9 /* LaunchAtLogin.h */; settings = {ATTRIBUTES = (Public, ); }; };
E32E9B6F1EB87DC5000FEEE9 /* LaunchAtLogin.swift in Sources */ = {isa = PBXBuildFile; fileRef = E32E9B6E1EB87DC5000FEEE9 /* LaunchAtLogin.swift */; };
E32E9B771EB87EA3000FEEE9 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = E32E9B761EB87EA3000FEEE9 /* main.swift */; };
@@ -26,6 +28,8 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
D92CFD2124C5D909005B91BE /* Toggle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Toggle.swift; sourceTree = "<group>"; usesTabs = 1; };
D9EF7E1C24BCF9E300A1E9AA /* copy-helper-swiftpm.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "copy-helper-swiftpm.sh"; sourceTree = "<group>"; };
E32E9B631EB87D7B000FEEE9 /* LaunchAtLogin.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = LaunchAtLogin.framework; sourceTree = BUILT_PRODUCTS_DIR; };
E32E9B661EB87D7B000FEEE9 /* LaunchAtLogin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = LaunchAtLogin.h; sourceTree = "<group>"; usesTabs = 1; };
E32E9B671EB87D7B000FEEE9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -34,7 +38,7 @@
E32E9B761EB87EA3000FEEE9 /* main.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = main.swift; sourceTree = "<group>"; usesTabs = 1; };
E32E9B7F1EB87EA3000FEEE9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
E32E9B921EB889AE000FEEE9 /* copy-helper.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; lineEnding = 0; path = "copy-helper.sh"; sourceTree = "<group>"; usesTabs = 1; };
E3A6EB57249009C3004D7101 /* LaunchAtLogin.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = LaunchAtLogin.entitlements; path = LaunchAtLogin/LaunchAtLogin.entitlements; sourceTree = "<group>"; };
E3A6EB57249009C3004D7101 /* LaunchAtLogin.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = LaunchAtLogin.entitlements; sourceTree = "<group>"; };
E3B8C38A20C0003300272EC0 /* LaunchAtLoginHelper.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = LaunchAtLoginHelper.entitlements; sourceTree = "<group>"; };
/* End PBXFileReference section */
@@ -56,12 +60,19 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
D9CFA59424C0FB5E005BC9E9 /* Sources */ = {
isa = PBXGroup;
children = (
E32E9B651EB87D7B000FEEE9 /* LaunchAtLogin */,
E32E9B751EB87EA3000FEEE9 /* LaunchAtLoginHelper */,
);
path = Sources;
sourceTree = "<group>";
};
E32E9B591EB87D7B000FEEE9 = {
isa = PBXGroup;
children = (
E3A6EB57249009C3004D7101 /* LaunchAtLogin.entitlements */,
E32E9B651EB87D7B000FEEE9 /* LaunchAtLogin */,
E32E9B751EB87EA3000FEEE9 /* LaunchAtLoginHelper */,
D9CFA59424C0FB5E005BC9E9 /* Sources */,
E32E9B641EB87D7B000FEEE9 /* Products */,
);
sourceTree = "<group>";
@@ -79,10 +90,13 @@
E32E9B651EB87D7B000FEEE9 /* LaunchAtLogin */ = {
isa = PBXGroup;
children = (
E32E9B6E1EB87DC5000FEEE9 /* LaunchAtLogin.swift */,
E32E9B661EB87D7B000FEEE9 /* LaunchAtLogin.h */,
D9EF7E1C24BCF9E300A1E9AA /* copy-helper-swiftpm.sh */,
E32E9B921EB889AE000FEEE9 /* copy-helper.sh */,
E32E9B671EB87D7B000FEEE9 /* Info.plist */,
E3A6EB57249009C3004D7101 /* LaunchAtLogin.entitlements */,
E32E9B661EB87D7B000FEEE9 /* LaunchAtLogin.h */,
E32E9B6E1EB87DC5000FEEE9 /* LaunchAtLogin.swift */,
D92CFD2124C5D909005B91BE /* Toggle.swift */,
);
path = LaunchAtLogin;
sourceTree = "<group>";
@@ -154,7 +168,7 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0830;
LastUpgradeCheck = 1020;
LastUpgradeCheck = 1200;
ORGANIZATIONNAME = "Sindre Sorhus";
TargetAttributes = {
E32E9B621EB87D7B000FEEE9 = {
@@ -176,7 +190,7 @@
};
};
buildConfigurationList = E32E9B5D1EB87D7B000FEEE9 /* Build configuration list for PBXProject "LaunchAtLogin" */;
compatibilityVersion = "Xcode 11.0";
compatibilityVersion = "Xcode 12.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
@@ -201,6 +215,7 @@
files = (
E32E9B861EB8845E000FEEE9 /* LaunchAtLoginHelper.app in Resources */,
E32E9B931EB889AE000FEEE9 /* copy-helper.sh in Resources */,
D9EF7E1D24BCF9E300A1E9AA /* copy-helper-swiftpm.sh in Resources */,
E3A6EB58249009C3004D7101 /* LaunchAtLogin.entitlements in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -220,6 +235,7 @@
buildActionMask = 2147483647;
files = (
E32E9B6F1EB87DC5000FEEE9 /* LaunchAtLogin.swift in Sources */,
D92CFD2224C5D909005B91BE /* Toggle.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -267,6 +283,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
@@ -300,7 +317,6 @@
SWIFT_COMPILATION_MODE = singlefile;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
VALID_ARCHS = x86_64;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
@@ -331,6 +347,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
@@ -356,7 +373,6 @@
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_VERSION = 5.0;
VALID_ARCHS = x86_64;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
@@ -375,14 +391,14 @@
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_VERSION = A;
INFOPLIST_FILE = LaunchAtLogin/Info.plist;
INFOPLIST_FILE = Sources/LaunchAtLogin/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
"@loader_path/Frameworks",
);
MARKETING_VERSION = 3.0.1;
MARKETING_VERSION = 4.1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.sindresorhus.LaunchAtLogin;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -405,14 +421,14 @@
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_VERSION = A;
INFOPLIST_FILE = LaunchAtLogin/Info.plist;
INFOPLIST_FILE = Sources/LaunchAtLogin/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
"@loader_path/Frameworks",
);
MARKETING_VERSION = 3.0.1;
MARKETING_VERSION = 4.1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.sindresorhus.LaunchAtLogin;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -423,17 +439,19 @@
E32E9B811EB87EA3000FEEE9 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_ENTITLEMENTS = LaunchAtLoginHelper/LaunchAtLoginHelper.entitlements;
CODE_SIGN_ENTITLEMENTS = Sources/LaunchAtLoginHelper/LaunchAtLoginHelper.entitlements;
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 2;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = LaunchAtLoginHelper/Info.plist;
INFOPLIST_FILE = Sources/LaunchAtLoginHelper/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MARKETING_VERSION = 1.0.1;
PRODUCT_BUNDLE_IDENTIFIER = com.sindresorhus.LaunchAtLoginHelper;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -444,17 +462,19 @@
E32E9B821EB87EA3000FEEE9 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_ENTITLEMENTS = LaunchAtLoginHelper/LaunchAtLoginHelper.entitlements;
CODE_SIGN_ENTITLEMENTS = Sources/LaunchAtLoginHelper/LaunchAtLoginHelper.entitlements;
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 2;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = LaunchAtLoginHelper/Info.plist;
INFOPLIST_FILE = Sources/LaunchAtLoginHelper/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MARKETING_VERSION = 1.0.1;
PRODUCT_BUNDLE_IDENTIFIER = com.sindresorhus.LaunchAtLoginHelper;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1020"
LastUpgradeVersion = "1200"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -29,8 +29,6 @@
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
@@ -51,8 +49,6 @@
ReferencedContainer = "container:LaunchAtLogin.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1020"
LastUpgradeVersion = "1200"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -27,8 +27,6 @@
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
@@ -38,8 +36,8 @@
ReferencedContainer = "container:LaunchAtLogin.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
@@ -61,8 +59,6 @@
ReferencedContainer = "container:LaunchAtLogin.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
-21
View File
@@ -1,21 +0,0 @@
import Foundation
import ServiceManagement
public struct LaunchAtLogin {
private static let id = "\(Bundle.main.bundleIdentifier!)-LaunchAtLoginHelper"
public static var isEnabled: Bool {
get {
guard let jobs = (SMCopyAllJobDictionaries(kSMDomainUserLaunchd).takeRetainedValue() as? [[String: AnyObject]]) else {
return false
}
let job = jobs.first { $0["Label"] as! String == id }
return job?["OnDemand"] as? Bool ?? false
}
set {
SMLoginItemSetEnabled(id as CFString, newValue)
}
}
}
+30
View File
@@ -0,0 +1,30 @@
// swift-tools-version:5.3
import PackageDescription
let package = Package(
name: "LaunchAtLogin",
platforms: [
.macOS(.v10_12)
],
products: [
.library(
name: "LaunchAtLogin",
targets: ["LaunchAtLogin"]
)
],
targets: [
.target(
name: "LaunchAtLogin",
exclude: [
"Info.plist",
"copy-helper.sh"
],
resources: [
.copy("LaunchAtLogin.entitlements"),
.copy("copy-helper-swiftpm.sh"),
.copy("LaunchAtLoginHelper.zip"),
.copy("LaunchAtLoginHelper-with-runtime.zip")
]
)
]
)
+67
View File
@@ -0,0 +1,67 @@
import Foundation
import ServiceManagement
import Combine
public enum LaunchAtLogin {
public static let kvo = KVO()
@available(macOS 10.15, *)
public static let observable = Observable()
@available(macOS 10.15, *)
private static let _publisher = CurrentValueSubject<Bool, Never>(isEnabled)
@available(macOS 10.15, *)
public static let publisher = _publisher.eraseToAnyPublisher()
private static let id = "\(Bundle.main.bundleIdentifier!)-LaunchAtLoginHelper"
public static var isEnabled: Bool {
get {
guard let jobs = (SMCopyAllJobDictionaries(kSMDomainUserLaunchd)?.takeRetainedValue() as? [[String: AnyObject]]) else {
return false
}
let job = jobs.first { ($0["Label"] as? String) == id }
return job?["OnDemand"] as? Bool ?? false
}
set {
if #available(macOS 10.15, *) {
observable.objectWillChange.send()
}
kvo.willChangeValue(for: \.isEnabled)
SMLoginItemSetEnabled(id as CFString, newValue)
kvo.didChangeValue(for: \.isEnabled)
if #available(macOS 10.15, *) {
_publisher.send(newValue)
}
}
}
}
// MARK: - LaunchAtLoginObservable
extension LaunchAtLogin {
@available(macOS 10.15, *)
public final class Observable: ObservableObject {
public var isEnabled: Bool {
get { LaunchAtLogin.isEnabled }
set {
LaunchAtLogin.isEnabled = newValue
}
}
}
}
// MARK: - LaunchAtLoginKVO
extension LaunchAtLogin {
public final class KVO: NSObject {
@objc dynamic public var isEnabled: Bool {
get { LaunchAtLogin.isEnabled }
set {
LaunchAtLogin.isEnabled = newValue
}
}
}
}
Binary file not shown.
+80
View File
@@ -0,0 +1,80 @@
import SwiftUI
@available(macOS 10.15, *)
extension LaunchAtLogin {
/**
This package comes with a `LaunchAtLogin.Toggle` view which is like the built-in `Toggle` but with a predefined binding and label. Clicking the view toggles “launch at login” for your app.
```
struct ContentView: View {
var body: some View {
LaunchAtLogin.Toggle()
}
}
```
The default label is `"Launch at login"`, but it can be overridden for localization and other needs:
```
struct ContentView: View {
var body: some View {
LaunchAtLogin.Toggle {
Text("Launch at login")
}
}
}
```
*/
public struct Toggle<Label>: View where Label: View {
@ObservedObject private var launchAtLogin = LaunchAtLogin.observable
private let label: Label
/**
Creates a toggle that displays a custom label.
- Parameters:
- label: A view that describes the purpose of the toggle.
*/
public init(@ViewBuilder label: () -> Label) {
self.label = label()
}
public var body: some View {
SwiftUI.Toggle(isOn: $launchAtLogin.isEnabled) { label }
}
}
}
@available(macOS 10.15, *)
extension LaunchAtLogin.Toggle where Label == Text {
/**
Creates a toggle that generates its label from a localized string key.
This initializer creates a ``Text`` view on your behalf with provided `titleKey`
- Parameters:
- titleKey: The key for the toggle's localized title, that describes the purpose of the toggle.
*/
public init(_ titleKey: LocalizedStringKey) {
label = Text(titleKey)
}
/**
Creates a toggle that generates its label from a string.
This initializer creates a `Text` view on your behalf with the provided `title`.
- Parameters:
- title: A string that describes the purpose of the toggle.
*/
public init<S>(_ title: S) where S: StringProtocol {
label = Text(title)
}
/**
Creates a toggle with the default title of `Launch at login`.
*/
public init() {
self.init("Launch at login")
}
}
+50
View File
@@ -0,0 +1,50 @@
#!/bin/bash
HELPER_CHECKSUM="0a3d09438fb595802d554ce0a7c4ba8e1d2d91d5170362adc965da82e70d74cb"
HELPER_CHECKSUM_RUNTIME="98ef556b490e02f4084a11d8a07c33a880177a9816b355885a11f58c95876d62"
verlte() {
[ "$1" = "`echo -e "$1\n$2" | sort -V | head -n1`" ]
}
if verlte "10.14.4" "$MACOSX_DEPLOYMENT_TARGET"; then
helper_name="LaunchAtLoginHelper"
checksum="$HELPER_CHECKSUM"
else
helper_name="LaunchAtLoginHelper-with-runtime"
checksum="$HELPER_CHECKSUM_RUNTIME"
fi
package_resources_path="$BUILT_PRODUCTS_DIR/LaunchAtLogin_LaunchAtLogin.bundle/Contents/Resources"
helper_path="$package_resources_path/$helper_name.zip"
contents_path="$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH"
login_items="$contents_path/Library/LoginItems"
login_helper_path="$login_items/LaunchAtLoginHelper.app"
rm -rf "$login_helper_path"
mkdir -p "$login_items"
# Verify SHA256 checksum of LaunchAtLoginHelper.
zip_checksum="$(shasum -a 256 "$helper_path" | awk '{print $1}')"
if [[ "$zip_checksum" != "$checksum" ]]; then
echo "Wrong checksum of LaunchAtLoginHelper"
exit 1
fi
unzip "$helper_path" -d "$login_items/"
defaults write "$login_helper_path/Contents/Info" CFBundleIdentifier -string "$PRODUCT_BUNDLE_IDENTIFIER-LaunchAtLoginHelper"
if [[ -n $CODE_SIGN_ENTITLEMENTS ]]; then
codesign --force --entitlements="$package_resources_path/LaunchAtLogin.entitlements" --deep --options=runtime --sign="$EXPANDED_CODE_SIGN_IDENTITY_NAME" "$login_helper_path"
else
codesign --force --deep --options=runtime --sign="$EXPANDED_CODE_SIGN_IDENTITY_NAME" "$helper_path"
fi
# If this is being built for multiple architectures, assume it is a release build and we should clean up.
if [[ $ONLY_ACTIVE_ARCH == "NO" ]]; then
rm -rf "$contents_path/Resources/LaunchAtLogin_LaunchAtLogin.bundle"
fi
@@ -16,7 +16,8 @@ else
codesign --force --options=runtime --sign="$EXPANDED_CODE_SIGN_IDENTITY_NAME" "$helper_path"
fi
if [[ $CONFIGURATION == "Release" ]]; then
# If this is being built for multiple architectures, assume it is a release build and we should clean up.
if [[ $ONLY_ACTIVE_ARCH == "NO" ]]; then
rm -rf "$origin_helper_path"
rm "$(dirname "$origin_helper_path")/copy-helper.sh"
rm "$(dirname "$origin_helper_path")/LaunchAtLogin.entitlements"
@@ -11,17 +11,21 @@
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0.0</string>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>1</string>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string>
<key>LSBackgroundOnly</key>
<true/>
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>NSHumanReadableCopyright</key>
<string>MIT © Sindre Sorhus</string>
<key>LSBackgroundOnly</key>
<true/>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
@@ -1,13 +1,14 @@
import Cocoa
import AppKit
// TODO: When targeting macOS 11, convert this to use `App` protocol and remove `NSPrincipalClass` in Info.plist.
final class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ notification: Notification) {
let bundleId = Bundle.main.bundleIdentifier!
// TODO: Make this more strict by only replacing at the end
let mainBundleId = bundleId.replacingOccurrences(of: "-LaunchAtLoginHelper", with: "")
let bundleIdentifier = Bundle.main.bundleIdentifier!
let mainBundleIdentifier = bundleIdentifier.replacingOccurrences(of: #"-LaunchAtLoginHelper$"#, with: "", options: .regularExpression)
// Ensure the app is not already running
guard NSRunningApplication.runningApplications(withBundleIdentifier: mainBundleId).isEmpty else {
// Ensures the app is not already running.
guard NSRunningApplication.runningApplications(withBundleIdentifier: mainBundleIdentifier).isEmpty else {
NSApp.terminate(nil)
return
}
+9
View File
@@ -0,0 +1,9 @@
# Maintaining
## Rebuild helper binaries
1. If it's the non-`with-runtime` ZIP, set the target to macOS 10.14.4 first.
2. Set build configuration to “Release”.
3. Build the binary.
4. Find the binary in “Products” in the sidebar, ZIP it, and overwrite the existing ZIP file in the source directory.
5. Run `shasum -a 256 filename.zip` on the ZIP file and copy the hash into “copy-helper-swiftpm.sh”.
+157 -10
View File
@@ -6,29 +6,84 @@ It's usually quite a [convoluted and error-prone process](before-after.md) to ad
This package works with both sandboxed and non-sandboxed apps and it's App Store compatible and used in apps like [Plash](https://github.com/sindresorhus/Plash), [Dato](https://sindresorhus.com/dato), [Lungo](https://sindresorhus.com/lungo), and [Battery Indicator](https://sindresorhus.com/battery-indicator).
<br>
---
<div align="center">
<p>
<p>
<sup>
<a href="https://github.com/sponsors/sindresorhus">Sindre‘s open source work is supported by the community</a>
</sup>
</p>
<sup>Special thanks to:</sup>
<br>
<br>
<a href="https://keygen.sh">
<div>
<img src="https://sindresorhus.com/assets/thanks/keygen-logo.svg" width="210" alt="Keygen">
</div>
<b>A dead-simple software licensing and distribution API built for developers</b>
</a>
<br>
<br>
</p>
</div>
---
<br>
## Requirements
- macOS 10.12+
- Xcode 11+
- Swift 5+
- Xcode 12+
- Swift 5.3+
## Install
#### Swift Package Manager
Add `https://github.com/sindresorhus/LaunchAtLogin` in the [“Swift Package Manager” tab in Xcode](https://developer.apple.com/documentation/xcode/adding_package_dependencies_to_your_app).
#### Carthage
*Warning: Carthage is not recommended. Support for it will be removed at some point in the future.*
```
github "sindresorhus/LaunchAtLogin"
```
## Usage
Add a new ["Run Script Phase"](http://stackoverflow.com/a/39633955/64949) **below** "Embed Frameworks" in "Build Phases" with the following:
#### Swift Package Manager
Add a new [“Run Script Phase”](http://stackoverflow.com/a/39633955/64949) **below** (not into) “Copy Bundle Resources” in “Build Phases” with the following:
```sh
"${BUILT_PRODUCTS_DIR}/LaunchAtLogin_LaunchAtLogin.bundle/Contents/Resources/copy-helper-swiftpm.sh"
```
*(I would name the run script `Copy “Launch at Login Helper”`)*
#### Carthage
Add a new [“Run Script Phase”](http://stackoverflow.com/a/39633955/64949) **below** (not into) “Embed Frameworks” in “Build Phases” with the following:
```sh
"${PROJECT_DIR}/Carthage/Build/Mac/LaunchAtLogin.framework/Resources/copy-helper.sh"
```
Use it in your app:
*(I would name the run script `Copy “Launch at Login Helper”`)*
### Use it in your app
No need to store any state to UserDefaults.
*Note that the [Mac App Store guidelines](https://developer.apple.com/app-store/review/guidelines/) requires “launch at login” functionality to be enabled in response to a user action. This is usually solved by making it a preference that is disabled by default. Many apps also let the user activate it in a welcome screen.*
#### As static property
```swift
import LaunchAtLogin
@@ -42,20 +97,105 @@ print(LaunchAtLogin.isEnabled)
//=> true
```
No need to store any state to UserDefaults.
#### SwiftUI
*Note that the [Mac App Store guidelines](https://developer.apple.com/app-store/review/guidelines/) requires “launch at login” functionality to be enabled in response to a user action. This is usually solved by making it a preference that is disabled by default. Many apps also let the user activate it in a welcome screen.*
This package comes with a `LaunchAtLogin.Toggle` view which is like the built-in `Toggle` but with a predefined binding and label. Clicking the view toggles “launch at login” for your app.
```swift
struct ContentView: View {
var body: some View {
LaunchAtLogin.Toggle()
}
}
```
The default label is `"Launch at login"`, but it can be overridden for localization and other needs:
```swift
struct ContentView: View {
var body: some View {
LaunchAtLogin.Toggle {
Text("Launch at login")
}
}
}
```
Alternatively, you can use `LaunchAtLogin.observable` as a binding with `@ObservedObject`:
```swift
import SwiftUI
import LaunchAtLogin
struct ContentView: View {
@ObservedObject private var launchAtLogin = LaunchAtLogin.observable
var body: some View {
Toggle("Launch at login", isOn: $launchAtLogin.isEnabled)
}
}
```
#### Combine
Just subscribe to `LaunchAtLogin.publisher`:
```swift
import Combine
import LaunchAtLogin
final class ViewModel {
private var isLaunchAtLoginEnabled = LaunchAtLogin.isEnabled
private var cancellables = Set<AnyCancellable>()
func bind() {
LaunchAtLogin
.publisher
.assign(to: \.isLaunchAtLoginEnabled, on: self)
.store(in: &cancellables)
}
}
```
#### Storyboards
Bind the control to the `LaunchAtLogin.kvo` exposed property:
```swift
import Cocoa
import LaunchAtLogin
final class ViewController: NSViewController {
@objc dynamic var launchAtLogin = LaunchAtLogin.kvo
}
```
<img src="storyboard-binding.png" width="445">
## How does it work?
The framework bundles the helper app needed to launch your app and copies it into your app at build time.
The package bundles the helper app needed to launch your app and copies it into your app at build time.
## FAQ
#### The size of my app increased after adding `LaunchAtLogin`
#### I'm getting a “No such file or directory” error when archiving my app
Please ensure that the LaunchAtLogin run script phase is still below the “Embed Frameworks” phase. The order could have been accidentally changed.
The build error usually presents itself as:
```
cp: […]/Resources/LaunchAtLoginHelper.app: No such file or directory
rm: […]/Resources/copy-helper.sh: No such file or directory
Command PhaseScriptExecution failed with a nonzero exit code
```
#### The size of my app increased after adding `LaunchAtLogin` when using Carthage
The bundled launcher app is written in Swift and hence needs to embed the Swift runtime libraries. If your project targets macOS 10.14.4 or later, you can avoid embedding the Swift runtime libraries. First, open `./Carthage/Checkouts/LaunchAtLogin/LaunchAtLogin.xcodeproj` and set the deployment target to the same as your app, and then run `$ carthage build`. You'll have to do this each time you update `LaunchAtLogin`.
This is not a problem when using Swift Package Manager.
#### My app doesn't show up in “System Preferences › Users & Groups › Login Items”
[This is the expected behavior](https://stackoverflow.com/a/15104481/64949), unfortunately.
@@ -77,17 +217,24 @@ Some helpful Stack Overflow answers:
#### Can you support CocoaPods?
CocoaPods used to be supported, but [it did not work well](https://github.com/sindresorhus/LaunchAtLogin/issues/22) and there was no easy way to fix it, so support was dropped. Even though you mainly use CocoaPods, you can still use Carthage just for this package without any problems.
CocoaPods used to be supported, but [it did not work well](https://github.com/sindresorhus/LaunchAtLogin/issues/22) and there was no easy way to fix it, so support was dropped. Even though you mainly use CocoaPods, you can still use Swift Package Manager just for this package without any problems.
#### I'm getting a `'SMCopyAllJobDictionaries' was deprecated in OS X 10.10` warning
Apple deprecated that API without providing an alternative. Apple engineers have [stated that it's still the preferred API to use](https://github.com/alexzielenski/StartAtLoginController/issues/12#issuecomment-307525807). I plan to use it as long as it's available. There are workarounds I can implement if Apple ever removes the API, so rest assured, this module will be made to work even then. If you want to see this resolved, submit a [Feedback Assistant](https://feedbackassistant.apple.com) report with [the following text](https://github.com/feedback-assistant/reports/issues/16). There's unfortunately still [no way to suppress warnings in Swift](https://stackoverflow.com/a/32861678/64949).
#### I can't see the `LaunchAtLogin.bundle` in my debug build or I get a notarization error for developer ID distribution
As discussed [here](https://github.com/sindresorhus/LaunchAtLogin/issues/50), this package tries to determine if you're making a release or debug build and clean up its install accordingly. If your debug build is missing the bundle or, conversely, your release build has the bundle and it causes a code signing error, that means this has failed.
The script's determination is based on the “Build Active Architecture Only” flag in build settings. If this is set to `YES`, then the script will package LaunchAtLogin for a debug build. You must set this flag to `NO` if you plan on distributing the build with codesigning.
## Related
- [Defaults](https://github.com/sindresorhus/Defaults) - Swifty and modern UserDefaults
- [Preferences](https://github.com/sindresorhus/Preferences) - Add a preferences window to your macOS app in minutes
- [KeyboardShortcuts](https://github.com/sindresorhus/KeyboardShortcuts) - Add user-customizable global keyboard shortcuts to your macOS app
- [Regex](https://github.com/sindresorhus/Regex) - Swifty regular expressions
- [Preferences](https://github.com/sindresorhus/Preferences) - Add a preferences window to your macOS app in minutes
- [DockProgress](https://github.com/sindresorhus/DockProgress) - Show progress in your app's Dock icon
- [create-dmg](https://github.com/sindresorhus/create-dmg) - Create a good-looking DMG for your macOS app in seconds
- [More…](https://github.com/search?q=user%3Asindresorhus+language%3Aswift)
Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB