How to do launch at login for macOS apps

Issue #757 Use SMLoginItemSetEnabled from Service Management framework Use a helper background app that checks and invokes our main application Copy our helper app into Library/LoginItems helper_dir="$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/LoginItems" 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: "") // Ensure the app is not already running guard NSRunningApplication....

January 20, 2021 路 1 min 路 Khoa Pham

How to use NSWindow style and NSToolbar in AppKit

Issue #755 Read more

January 20, 2021 路 1 min 路 Khoa Pham

How to use NSSplitViewController in AppKit

Issue #754 Read more

January 20, 2021 路 1 min 路 Khoa Pham

How to create and notarize dmg file

Issue #753 Archive and export app from Xcode Create dmg Use create-dmg It is said that we don鈥檛 need to notarize the app, we can just notarize the whole dmg Send dmg to notarize This takes a while xcrun altool -t osx -f PastePal.dmg --primary-bundle-id com.onmyway133.PastePal --notarize-app -u -p APP_SPECIFIC_PASSWORD --asc-provider "T78DK947F2" If wrong password, you will get error Error: code -1011 (Failed to authenticate for session: ( 鈥淓rror Domain=ITunesConnectionAuthenticationErrorDomain Code=-22938 "Sign in with the app-specific password you generated....

January 18, 2021 路 2 min 路 Khoa Pham

How to open downloaded app from browser in Big Sur

Issue #750 Recently when distributing staging releases of my app PastePal via GitHub release or Google Drive, people had hard time opening it The displayed error is You do not have permission to open the application The more verbose error when using open command in Terminal is The application cannot be opened for an unexpected reason, error=Error Domain=NSOSStatusErrorDomain Code=-10826 鈥渒LSNoLaunchPermissionErr: User doesn鈥檛 have permission to launch the app (managed networks)鈥 UserInfo={_LSFunction=_LSLaunchWithRunningboard, _LSLine=2508, NSUnderlyingError=0x7fa9c750d850 {Error Domain=RBSRequestErrorDomain Code=5 鈥淟aunch failed....

January 15, 2021 路 2 min 路 Khoa Pham

How to make popup button in SwiftUI for macOS

Issue #748 There is said to be PopUpButtonPickerStyle and MenuPickerStyle but these don鈥檛 seem to work. There鈥檚 Menu button it shows a dropdown style. We fake it by fading this and overlay with a button. allowsHitTesting does not work, but disabled seems to do the trick Menu { Button("About", action: ActionService.onAbout) Button("Quit", action: ActionService.onQuit) } label: { Text("") } .frame(width: 24) .opacity(0.01) .overlay( makeButton(action: {}, "gearshape.fill") .disabled(true) .foregroundColor(Color.secondaryLabel) ) Follow pika...

January 13, 2021 路 1 min 路 Khoa Pham

How to use Apple keyboard key symbols

Issue #743 鈥 铮 = Apple logo 鈥 鈱 = Command 鈥 鈬 = Shift 鈥 鈱 = Backspace/Delete 鈥 鈬 = Caps lock 鈥 鈱 = Option/Alt 鈥 鈱 = Control 鈥 鈳 = Escape 鈥 鈫愨啈鈫掆啌 = Arrow Keys 鈥 鈫 = Return

January 5, 2021 路 1 min 路 Khoa Pham

How to disable scrolling in NSTextView for macOS

Issue #733 NSTextView has this handy method to make scrollable NSTextView NSTextView.scrollableTextView(). The solution is to get to the responder outside enclosing NSScrollView, in my case it is the SwiftUI hosting view class DisabledScrollTextView: NSTextView { override func scrollWheel(with event: NSEvent) { // 1st nextResponder is NSClipView // 2nd nextResponder is NSScrollView // 3rd nextResponder is NSResponder SwiftUIPlatformViewHost self.nextResponder?.nextResponder?.nextResponder?.scrollWheel(with: event) } } Then we can construct with our new DisabledScrollTextView.scrollableTextView

December 31, 2020 路 1 min 路 Khoa Pham

How to make attributed string Text in SwiftUI for macOS

Issue #730 Use NSTextField with maximumNumberOfLines import AppKit import SwiftUI struct AttributedText: NSViewRepresentable { let attributedString: NSAttributedString init(_ attributedString: NSAttributedString) { self.attributedString = attributedString } func makeNSView(context: Context) -> NSTextField { let textField = NSTextField() textField.lineBreakMode = .byClipping textField.maximumNumberOfLines = 0 textField.isBordered = false return textField } func updateNSView(_ nsView: NSTextField, context: Context) { nsView.attributedStringValue = attributedString } } TextField has problem with wrapping, we can use TextView struct AttributedTextView: NSViewRepresentable { typealias NSViewType = NSScrollView let attributedText: NSAttributedString?...

December 31, 2020 路 1 min 路 Khoa Pham

How to do copy paste delete in Swift for macOS

Issue #729 @NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate { @IBAction func copy(_ sender: Any) { print("copy", sender) } @IBAction func paste(_ sender: Any) { print("paste", sender) } } For delete, we can listen to keyDown in NSWindow class MyWindow: NSWindow { override func keyDown(with event: NSEvent) { super.keyDown(with: event) guard let deleteScalar = UnicodeScalar(NSDeleteCharacter), event.charactersIgnoringModifiers == String(deleteScalar) else { return } .didKeyboardDeleteItem)) } }

December 30, 2020 路 1 min 路 Khoa Pham

How to make visual effect blur in SwiftUI for macOS

Issue #724 We can use .blur modifier, but with VisualEffectView gives us more options for material and blending mode. public struct VisualEffectView: NSViewRepresentable { let material: NSVisualEffectView.Material let blendingMode: NSVisualEffectView.BlendingMode public init( material: NSVisualEffectView.Material = .contentBackground, blendingMode: NSVisualEffectView.BlendingMode = .withinWindow ) { self.material = material self.blendingMode = blendingMode } public func makeNSView(context: Context) -> NSVisualEffectView { let visualEffectView = NSVisualEffectView() visualEffectView.material = material visualEffectView.blendingMode = blendingMode visualEffectView.state = return visualEffectView } public func updateNSView(_ visualEffectView: NSVisualEffectView, context: Context) { visualEffectView....

December 26, 2020 路 1 min 路 Khoa Pham

How to force set frame explicitly for NSWindow

Issue #721 For setFrame to take effect mainWindow.setFrame(rect, display: true) we can remove auto save frame flag mainWindow.setFrameAutosaveName("MyApp.MainWindow")

December 25, 2020 路 1 min 路 Khoa Pham

How to rotate NSStatusItem

Issue #720 NSStatusItem is backed by NSButton, we can animate this inner button. We need to specify position and anchorPoint for button鈥檚 layer so it rotates around its center point guard let button = statusItem.button else { return } let animation = CABasicAnimation(keyPath: "transform.rotation.z") animation.fromValue = 0 animation.toValue = CGFloat.pi * 2 animation.duration = 0.25 animation.repeatCount = 1 button.layer?.position = NSPoint(x: NSMidX(button.frame), y: NSMidY(button.frame)) button.layer?.anchorPoint = NSPoint(x: 0.5, y: 0.5) button....

December 23, 2020 路 1 min 路 Khoa Pham

How to show image and text in menu item in SwiftUI for macOS

Issue #719 From SwiftUI 2 for macOS 11.0, we have access to Menu for creating menu and submenu. Usually we use Button for interactive menu items and Text for disabled menu items. The easy way to customize menu with image is to call Menu with content and label. Pay attention to how we use Button and Label inside Content to create interactive menu items Menu( content: { ForEach(collections) { collection in Button(action: {) { Label(collection....

December 23, 2020 路 1 min 路 Khoa Pham

How to make stepper with plus and minus buttons in SwiftUI for macOS

Issue #717 Try to use predefined system colors in Human Interface Guidelines for macOS Here we use this color unemphasizedSelectedTextBackgroundColor for button background HStack(spacing: 1) { makeUnderListButton(action: {}, icon: .plus) makeUnderListButton(action: {}, icon: .minus) } .background(Color(NSColor.unemphasizedSelectedTextBackgroundColor)) .cornerRadius(4) func makeUnderListButton(action: @escaping () -> Void, icon: AwesomeIcon) -> some View { Button(action: action) { Text(icon.rawValue) .font(.awesome(style: .solid, size: 14)) } .buttonStyle(HighlightButtonStyle(h: 8, v: 6, cornerRadius: 4)) } Another thing is List, where we have selected and alternative background colors....

December 21, 2020 路 1 min 路 Khoa Pham

How to fix Picker not showing selection in SwiftUI

Issue #716 I have an enum that conforms to CaseIterable that I want to show in Picker enum Position: String, Codable, CaseIterable, Identifiable { var id: String { rawValue } case left case right case bottom case top } Picker(selection: $preference.position, label: Text("Position")) { ForEach(Preference.Position.allCases) { position in Text(position.rawValue) } } It compiles and runs just fine, but Picker does not show current selection regardless of any Picker style I choose....

December 20, 2020 路 2 min 路 Khoa Pham

How to add toolbar programatically in macOS

Issue #713 To setup toolbar, we need to implement NSToolbarDelegate that provides toolbar items. This delegate is responsible for many things Set visible and allowed items with toolbarDefaultItemIdentifiers Provide item with itemForItemIdentifier Being notified with toolbarWillAddItem and toolbarDidRemoveItem window.toolbarStyle = .unifiedCompact let toolbar = NSToolbar(identifier: "Toolbar") toolbar.displayMode = .iconAndLabel toolbar.delegate = (NSApp.delegate as! AppDelegate) toolbar.insertItem(withItemIdentifier: .add, at: 0) toolbar.insertItem(withItemIdentifier: .settings, at: 1) window.toolbar = toolbar extension NSToolbarItem.Identifier { static let add = NSToolbarItem....

December 15, 2020 路 1 min 路 Khoa Pham

How to show sidebar in SwiftUI for macOS

Issue #710 Starting from macOS 11, we can use List with SidebarListStyle inside NavigationView to declare master detail view. The SidebarListStyle makes list translucent. It automatically handles selection and marks selected row in list with accent color. struct MainView: some View { var body: some View { NavigationView { sidebar ContentView() } } private var sidebar: some View { List { Group { Text("Categories") .foregroundColor(.gray) ForEach(categories) { category in NavigationLink(destination: ContentView(category: category)) { Label(category....

December 13, 2020 路 1 min 路 Khoa Pham

How to support right click menu to NSStatusItem

Issue #707 The trick is to set the button oinside of statusItem to send actions on both leftMouseUp and rightMouseUp. Another thing to note is we use popUpMenu on NSStatusItem, although it is marked as deprecated on macOS 10.14. We can set menu but that overrides left click. import Omnia private let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength) private let statusItemMenuHandler = MenuHandler() func setupStatusMenu() { if let button = statusItem.button { button....

December 7, 2020 路 1 min 路 Khoa Pham

How to convert from paid to free with IAP

Issue #703 What is receipt Read When to refresh a receipt vs restore purchases in iOS? From iOS 7, every app downloaded from the store has a receipt (for downloading/buying the app) at appStoreReceiptURL. When users purchases something via In App Purchase, the content at appStoreReceiptURL is updated with purchases information. Most of the cases, you just need to refresh the receipt (at appStoreReceiptURL) so that you know which transactions users have made....

November 30, 2020 路 9 min 路 Khoa Pham