How to deinit NSWindow

Issue #848 Hold a weak reference to NSWindow, and let system window server manages its lifetime weak var window = NSWindow() window.isReleasedWhenClosed = true

September 9, 2021 路 1 min 路 Khoa Pham

How to convert NSEvent locationInWindow to window coordinate

Issue #822 Get active screen with mouse func activeScreen() -> NSScreen? { let mouseLocation = NSEvent.mouseLocation let screens = NSScreen.screens let screenWithMouse = (screens.first { NSMouseInRect(mouseLocation, $0.frame, false) }) return screenWithMouse } Reposition our NSWIndow if window.frame != screen.frame { window.setFrameOrigin(screen.frame.origin) window.setContentSize(screen.frame.size) } Flip y as AppKit has coordinate origin starting from bottom left of the screen, while our SwiftUI starts from top left var point = window.convertPoint(fromScreen: event.locationInWindow) point.y = window....

July 13, 2021 路 1 min 路 Khoa Pham

How to show modal window in AppKit

Issue #806 Use runModal Blocks main queue A loop that uses this method is similar in some ways to a modal event loop run with runModal(for:), except with this method your code can do some additional work between method invocations. When you invoke this method, events for the NSWindow object of this session are dispatched as normal. This method returns when there are no more events. You must invoke this method frequently enough in your loop that the window remains responsive to events....

June 24, 2021 路 1 min 路 Khoa Pham

How to resize NSImage with padding

Issue #795 extension NSImage { func resize(width: CGFloat, height: CGFloat, padding: CGFloat) -> NSImage { let img = NSImage(size: CGSize(width: width, height: height)) img.lockFocus() let ctx = NSGraphicsContext.current ctx?.imageInterpolation = .high self.draw( in: NSMakeRect(0, 0, width, height), from: NSMakeRect(0, -padding, size.width, size.height - padding), operation: .copy, fraction: 1 ) img.unlockFocus() return img } } So we can use like button.image = NSImage(named: NSImage.Name("pastePal"))?.resize(width: 22, height: 22, padding: -4)

April 15, 2021 路 1 min 路 Khoa Pham

How to convert from paid to freemium in SwiftUI with RevenueCat

Issue #794 I鈥檝e been distributing my apps PastePal and Push Hero as a paid upfront apps on Appstore. I鈥檝e quickly come to realize the importance of trials since many have requested a tryout before purchasing. Manual sending out trials comes with its own problem. I need to notarize and package the app as a dmg file, and not to even mention implement my own trial mechanism to check and notify of expired builds....

April 11, 2021 路 5 min 路 Khoa Pham

How to manage WindowGroup in SwiftUI for macOS

Issue #789 Using WindowGroup New in SwiftUI 2.0 for iOS 14 and macOS 11.0 is WindwGroup which is used in App protocol. WindowGroup is ideal for document based applications where you can open multiple windows for different content or files. For example if you鈥檙e developing a text editor or drawing apps, you can show multiple windows for different text file or drawing. All is handled automatically for you if you use WindowGroup...

March 7, 2021 路 6 min 路 Khoa Pham

How to use Core Data

Issue #785 Core Data Responding to changes in a managed object context Calling mergeChanges on a managed object context will automatically refresh any managed objects that have changed. This ensures that your context always contains all the latest information. Note that you don鈥檛 have to call mergeChanges on a viewContext when you set its automaticallyMergesChangesFromParent property to true. In that case, Core Data will handle the merge on your behalf....

February 26, 2021 路 5 min 路 Khoa Pham

How to tune performance with ButtonBehavior in SwiftUI

Issue #779 With Xcode 12.4, macOS 11.0 app. Every time we switch the system dark and light mode, the CPU goes up to 100%. Instruments show that there鈥檚 an increasing number of ButtonBehavior Suspect State in a row in LazyVStack Every cell has its own toggle state struct Cell: View { enum ToggleState { case general case request case response } let item: Item @State private var toggleState: ToggleState = ....

February 24, 2021 路 1 min 路 Khoa Pham

How to use GroupBox in SwiftUI

Issue #778 For now using GroupBox has these issues in macOS Prevents dragging scroll indicator to scroll Switch from light to dark mode may cause 100% CPU usage

February 23, 2021 路 1 min 路 Khoa Pham

How to show close button in NSTextField in AppKit

Issue #771 Use NSSearchField instead

February 6, 2021 路 1 min 路 Khoa Pham

How to show modal window in SwiftUI for macOS

Issue #768 Use custom NSWindow, set level in becomeKey and call NSApp.runModal to show modal final class ModalWindow: NSWindow { override func becomeKey() { super.becomeKey() level = .statusBar } override func close() { super.close() NSApp.stopModal() } } let window = ModalWindow( contentRect: .zero, styleMask: [.titled, .closable], backing: .buffered, defer: false ) window.titlebarAppearsTransparent = true window.title = "Manage collections" window.isReleasedWhenClosed = false self.window = window let view = CollectionSettingsView(store: Store.shared) ....

February 3, 2021 路 1 min 路 Khoa Pham

How to handle keyDown in SwiftUI for macOS

Issue #764 Use a custom KeyAwareView that uses an NSView that checks for keyDown method. In case we can鈥檛 handle certain keys, call super.keyDown(with: event) import SwiftUI import KeyboardShortcuts struct KeyAwareView: NSViewRepresentable { let onEvent: (Event) -> Void func makeNSView(context: Context) -> NSView { let view = KeyView() view.onEvent = onEvent DispatchQueue.main.async { view.window?.makeFirstResponder(view) } return view } func updateNSView(_ nsView: NSView, context: Context) {} } extension KeyAwareView { enum Event { case upArrow case downArrow case leftArrow case rightArrow case space case delete case cmdC } } private class KeyView: NSView { var onEvent: (KeyAwareView....

January 29, 2021 路 1 min 路 Khoa Pham

How to use Sparkle for macOS app

Issue #762 Install Sparkle For now, the latest stable version is 1.24.0 which supports CocoaPods OK, but still, have issues with SPM. Support non sandboxed apps Version 2.0.0 is in beta and supports sandboxed apps To install, use CocoaPods platform :osx, '11.0' target 'MyApp' do # Comment the next line if you don't want to use dynamic frameworks use_frameworks! pod 'Sparkle' end Sign In your target, choose Signing & Capability tab, change Signing Certificate from Locally to Development for code sign to work for embedded frameworks...

January 26, 2021 路 2 min 路 Khoa Pham

How to handle keyDown in NSResponder

Issue #760 import AppKit import Omnia class MyWindow: NSWindow { override func keyDown(with event: NSEvent) { super.keyDown(with: event) if isKey(NSDeleteCharacter, event: event) { .didKeyboardDeleteItem)) } else if isKey(NSUpArrowFunctionKey, event: event) { print("up") } else if isKey(NSDownArrowFunctionKey, event: event) { print("down") } else if isKey(NSLeftArrowFunctionKey, event: event) { print("left") } else if isKey(NSRightArrowFunctionKey, event: event) { print("right") } } private func isKey(_ key: Int, event: NSEvent) -> Bool { if let scalar = UnicodeScalar(key) { return event....

January 21, 2021 路 2 min 路 Khoa Pham

How to use built in NSImage in macos

Issue #759 Read more

January 21, 2021 路 1 min 路 Khoa Pham

How to handle NSSearchToolbarItem in macOS 11

Issue #758 extension NSToolbarItem.Identifier { static let searchItem: NSToolbarItem.Identifier = NSToolbarItem.Identifier("SearchItem") } let searchItem = NSSearchToolbarItem(itemIdentifier: .searchItem) extension AppDelegate: NSToolbarDelegate { func toolbar( _ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, willBeInsertedIntoToolbar flag: Bool ) -> NSToolbarItem? { switch itemIdentifier { case .searchItem: searchItem.searchField.delegate = self return searchItem } } extension AppDelegate: NSSearchFieldDelegate { func control( _ control: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector ) -> Bool { if (commandSelector == #selector(NSResponder.insertNewline(_:))) { print("enter") return true } return false } }

January 21, 2021 路 1 min 路 Khoa Pham

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