How to use SwiftGen and LocalizedStringKey in SwiftUI

Issue #798 swiftgen.yml strings: inputs: PastePal/Resources/Localizable/en.lproj outputs: - templatePath: swiftgen-swiftui-template.stencil output: PastePal/Resources/Strings.swift Template from https://github.com/SwiftGen/SwiftGen/issues/685 swiftgen-swiftui-template.stencil // Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen {% if tables.count > 0 %} import SwiftUI // MARK: - Strings {% macro parametersBlock types %}{% filter removeNewlines:"leading" %} {% for type in types %} {% if type == "String" %} _ p{{forloop.counter}}: Any {% else %} _ p{{forloop.counter}}: {{type}} {% endif %} {{ ", " if not forloop....

April 27, 2021 · 2 min · Khoa Pham

How to use ForEach with indices in SwiftUI

Issue #796 One seemingly obvious way to use ForEach on array with indices is using enumerated struct Book: Hashable, Identifiable { let id: UUID let name: String } struct BooksView: View { let books: [Book] var body: some View { List { ForEach(Array(books.enumerated()), id: \.element) { index, book in Text(book.name) .background(index % 2 == 0 ? Color.green : Color.orange) } } } } Reading the documentation for enumerated closely When you enumerate a collection, the integer part of each pair is a counter for the enumeration, but is not necessarily the index of the paired value....

April 20, 2021 · 2 min · Khoa Pham

How to convert from paid to freemium in SwiftUI with RevenueCat

Issue #794 I’ve been distributing my apps PastePal and Push Hero as a paid upfront apps on Appstore. I’ve 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 use View protocol in SwiftUI

Issue #791 SwiftUI has View protocol which represents part of your app’s user interface and provides modifiers that you use to configure views. You create custom views by declaring types that conform to the View protocol. Implement the required body computed property to provide the content for your custom view. A typical example of View is using struct enum HeroType { case melee case range } struct ContentView: View { let heroType: HeroType var body: some View { switch heroType { case ....

March 10, 2021 · 1 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’re 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’t 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 force resolve Swift Package in Xcode

Issue #784 Every time I switch git branches, SPM packages seem to be invalidated and Xcode does not fetch again, no matter how many times I reopen. Running xcodebuild -resolvePackageDependencies does fetch but Xcode does not recognize the resolved packages and still reports missing packages. The good thing is under menu File -> Swift Packages there are options to reset and resolve packages...

February 26, 2021 · 1 min · Khoa Pham

How to listen to Published outside of SwiftUI view

Issue #782 Use $ to access Publisher final class Store: ObservableObject { @Published var showsSideWindow: Bool = false } var anyCancellables = Set<AnyCancellable>() store.$showsSideWindow .removeDuplicates() .throttle(for: 0.2, scheduler: RunLoop.main, latest: true) .receive(on: RunLoop.main) .sink(receiveValue: { shows in preferenceManager.reloadPosition(shows: shows) }) .store(in: &anyCancellables) ...

February 25, 2021 · 1 min · Khoa Pham

How to build container view in SwiftUI

Issue #780 To make a container view that accepts child content, we use ViewBuilder struct ContainerView<Content: View>: View { let content: Content init(@ViewBuilder content: () -> Content) { self.content = content() } var body: some View { content } } From Swift 5.4, it can synthesize the init, so we can declare resultBuilder for stored property struct AwesomeContainerView<Content: View>: View { @ViewBuilder let content: Content var body: some View { content } } ...

February 24, 2021 · 1 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’s 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 make simple search bar in SwiftUI

Issue #776 We need to use a custom Binding to trigger onChange as onEditingChanged is only called when the user selects the textField, and onCommit is only called when return or done button on keyboard is tapped. import UIKit import SwiftUI import EasySwiftUI struct SearchBar: View { @Binding var searchText: String let onChange: () -> Void @State private var showsCancelButton: Bool = false var body: some View { return HStack { textField cancelButton } } private var searchTextBinding: Binding<String> { Binding<String>(get: { searchText }, set: { newValue in DispatchQueue....

February 17, 2021 · 1 min · Khoa Pham

How to add home screen quick action in SwiftUI

Issue #774 Start by defining your quick actions. You can use UIApplicationShortcutIcon(type:) for predefined icons, or use UIApplicationShortcutIcon(systemImageName:) for SFSymbol enum QuickAction: String { case readPasteboard case clear var shortcutItem: UIApplicationShortcutItem { switch self { case .readPasteboard: return UIApplicationShortcutItem( type: rawValue, localizedTitle: "Read Pasteboard", localizedSubtitle: "", icon: UIApplicationShortcutIcon(type: .add), userInfo: nil ) case .clear: return UIApplicationShortcutItem( type: rawValue, localizedTitle: "Clear Pasteboard", localizedSubtitle: "", icon: UIApplicationShortcutIcon(systemImageName: SFSymbol.wind.rawValue), userInfo: nil ) } } } Add a service to store selected quick action....

February 10, 2021 · 2 min · Khoa Pham

How to use EquatableView in SwiftUI

Issue #773 From John Harper ’s tweet SwiftUI assumes any Equatable.== is a true equality check, so for POD views it compares each field directly instead (via reflection). For non-POD views it prefers the view’s == but falls back to its own field compare if no ==. EqView is a way to force the use of ==. When it does the per-field comparison the same rules are applied recursively to each field (to choose direct comparison or == if defined)....

February 8, 2021 · 1 min · Khoa Pham

How to add new property in Codable struct in SwiftUI

Issue #772 I use Codable structs in my apps for preferences, and bind them to SwiftUI views. If we add new properties to existing Codable, it can’t decode with old saved json as we require new properties. We can either do cutom decoding with container, but this can result in lots more code and mistakes if we have many properties inside our struct. The quick workaround is to declare new properties as optional, and use a computed property to wrap that....

February 8, 2021 · 1 min · Khoa Pham

How to handle escape in NSTextField in SwiftUI

Issue #770 Handle cancelOperation somewhere up in responder chain class MyWindow: NSWindow { let keyHandler = KeyHandler() override func cancelOperation(_ sender: Any?) { super.cancelOperation(sender) keyHandler.onEvent(.esc) } } ...

February 6, 2021 · 1 min · Khoa Pham

How to fit ScrollView to content in SwiftUI

Issue #769 If we place ScrollView inside HStack or VStack, it takes all remaining space. To fit ScrollView to its content, we need to get its content size and constrain ScrollView size. Use a GeometryReader as Scrollview content background, and get the local frame import SwiftUI struct HSearchBar: View { @State private var scrollViewContentSize: CGSize = .zero var body: some View { HStack { searchButton ScrollView(.horizontal, showsIndicators: false) { HStack(spacing: 12) { ForEach(store....

February 6, 2021 · 1 min · Khoa Pham

How to use ViewBuilder in SwiftUI

Issue #767 SwiftUI ’s ViewBuilder is a custom parameter attribute that constructs views from closures. It is available in body and most SwiftUI modifiers public protocol View { associatedtype Body : View @ViewBuilder var body: Self.Body { get } } public func contextMenu<MenuItems>(@ViewBuilder menuItems: () -> MenuItems) -> some View where MenuItems : View In these ViewBuilder enabled places we can perform conditional logic to construct views. For example here in our SampleView, we have switch statement in body...

February 2, 2021 · 3 min · Khoa Pham

How to show multiple popover in SwiftUI

Issue #765 In SwiftUI currently, it’s not possible to attach multiple .popover to the same View. But we can use condition to show different content struct ClipboardCell: View { enum PopoverStyle { case raw case preview } @Binding var showsPopover: Bool @Binding var popoverStyle: PopoverStyle var body: some View { VStack { header content .popover(isPresented: $showsPopover) { switch popoverStyle { case .raw: ViewRawView(item: item) case .preview: PreviewView(item: item) } } footer } } } ...

January 29, 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’t 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 extend custom View in SwiftUI

Issue #763 I usually break down a big struct into smaller views and extensions. For example I have a ClipboardCell that has a lot of onReceive so I want to move these to another component. One way to do that is to extend ClipboardCell struct ClipboardCell: View { let isSelected: Bool @State var showsPreview: Bool @State var showsViewRaw: Bool let onCopy: () -> Void let onDelete: () -> Void } extension ClipboardCell { func onReceiveKeyboard() -> some View { self....

January 28, 2021 · 3 min · Khoa Pham