How to show anchor bottom view in SwiftUI

Issue #954 From iOS 15, there鈥檚 a handy safeAreaInset that allows us to place additional content extending the safe area. Shows the specified content beside the modified view. safeAreaInset allows us to customize which edge and alignment we can place our views. This works for both ScrollView, List and Form and you can apply it multiple times. The content view is anchored to the specified horizontal edge in the parent view, aligning its vertical axis to the specified alignment guide....

November 22, 2023 路 2 min 路 Khoa Pham

How to store Codable in AppStorage

Issue #949 AppStorage and SceneStorage accepts RawRepresentable where value is Int or String. Creates a property that can read and write to a string user default, transforming that to RawRepresentable data type. init(wrappedValue:_:store:) init( wrappedValue: Value, _ key: String, store: UserDefaults? = nil ) where Value : RawRepresentable, Value.RawValue == String One clever thing (that does not work) is to use a custom Codable type that conforms to RawRepresentable, like below...

October 3, 2023 路 2 min 路 Khoa Pham

How to update widget for iOS 17

Issue #948 iOS 17 has a new Stand by mode so SwiftUI introduces containerBackground for the system to decide when to draw background. It also automatically applies margin to widget so we may need to disable that To update existing widgets, we can write some useful extension extension View { @ViewBuilder func safeContainerBackground(@ViewBuilder content: () -> some View) -> some View { if #available(iOS 17.0, *) { self.containerBackground(for: .widget, content: content) } else { self....

October 2, 2023 路 1 min 路 Khoa Pham

How to force localized language with SwiftGen

Issue #945 The default SwiftGen generate generated strings L10n file like this extension L10n { private static func tr(_ table: String, _ key: String, _ args: CVarArg..., fallback value: String) -> String { let format = BundleToken.bundle.localizedString(forKey: key, value: value, table: table) return String(format: format, locale: Locale.current, arguments: args) } } // swiftlint:disable convenience_type private final class BundleToken { static let bundle: Bundle = { #if SWIFT_PACKAGE return Bundle.module #else return Bundle(for: BundleToken....

August 26, 2023 路 1 min 路 Khoa Pham

How to make tag flow layout using Layout protocol in SwiftUI

Issue #944 SwiftUI in iOS 16 supports Layout protocol to arrange subviews We need to implement 2 methods sizeThatFits(proposal:subviews:cache:) reports the size of the composite layout view. placeSubviews(in:proposal:subviews:cache:) assigns positions to the container鈥檚 subviews. We will make a custom layout that arranges elements from top leading and span element to new rows if it can鈥檛 fit the current row. Start by defining a FlowLayout struct that conforms to Layout protocol....

August 22, 2023 路 3 min 路 Khoa Pham

How to use hover annotation in Swift Charts

Issue #943 In this tutorial, we鈥檒l learn how to use Swift Charts to visualize ranking data. We use default AxisMarks and AxisMarks to let Swift Charts interpolate x and y grid lines. For y axis, I want to have finer grain control over the grid, so I specify an arrayLiteral Note that for rank, the lower value the better ranking, so we make use of chartYScale with reversed automatic domain to flip from 0 -> 100 to 100 -> 0...

August 18, 2023 路 2 min 路 Khoa Pham

How to scale image fill without affect layout in SwiftUI

Issue #939 Instead of letting the Image decide the size, we can put it as background or overlay. I use clipped and contentShape to avoid the fill image obscuring touch event Color.clear .frame(height: 100) .overlay { AsyncImage(url: item.imageUrl) { image in image .resizable() .scaledToFill() } placeholder: { ProgressView() } } .clipped() .contentShape(Rectangle())

July 30, 2023 路 1 min 路 Khoa Pham

How to move Core Data database to AppGroup folder

Issue #938 To let app and extension to talk to the same database, we need to use AppGroup. Here is how to use replacePersistentStore Replaces one persistent store with another actor DatabaseMigrator { @AppStorage("DatabaseMigrator.hasMigrated") var hasMigrated = false func migrateIfNeeded() { guard !hasMigrated else { return } migrate() hasMigrated = true } private func migrate() { let oldContainer = NSPersistentCloudKitContainer(name: "Bookmarks") guard let oldStoreUrl = oldContainer.persistentStoreDescriptions.first?.url, let newStoreUrl = Constants....

July 30, 2023 路 1 min 路 Khoa Pham

How to read write files to iCloud Drive

Issue #937 First, you need to enable iCloud Documents capability. Go to target settings -> Signing & Capabilities -> iCloud ` Then inside your Info.plist, add this with your iCloud identifier and app name <key>NSUbiquitousContainers</key> <dict> <key></key> <dict> <key>NSUbiquitousContainerIsDocumentScopePublic</key> <true/> <key>NSUbiquitousContainerName</key> <string>PastePal</string> <key>NSUbiquitousContainerSupportedFolderLevels</key> <string>Any</string> </dict> </dict> To access to your iCloud Drive folder, we use FileManager to retrieve the folder. Returns the URL for the iCloud container associated with the specified identifier and establishes access to that container....

July 28, 2023 路 2 min 路 Khoa Pham

How to use keychain in Swift

Issue #934 There are a few keychain wrappers around but for simple needs, you can write it yourself Here is a basic implementation. I use actor to go with async/await, and a struct KeychainError to contain status code in case we want to deal with error cases. accessGroup is to define kSecAttrAccessGroup to share keychain across your apps public actor Keychain { public struct KeychainError: Error { let status: OSStatus } let service: String let accessGroup: String?...

July 20, 2023 路 2 min 路 Khoa Pham

How to make share and action extension in iOS

Issue #932 Add Share extension and Action extension respectively in Xcode. We can use the same code to both extension SwiftUI I usually make a ShareView in SwiftUI with ShareViewModel to control the logic struct ShareView: View { @ObservedObject var vm: ShareViewModel var body: some View { NavigationStack(path: $vm.routes) { List {} } } } In ShareViewController, we can just conform to UIViewController and add our SwiftUI view as child view controller...

July 12, 2023 路 2 min 路 Khoa Pham

How to copy text to the clipboard in Swift

Issue #928 For iOS, use string Setting this property replaces all current items in the pasteboard with the new item. If the first item has no value of the indicated type, nil is returned. let pasteboard = UIPasteboard.general pasteboard.string = "hello world" For Mac, use clearContents first Clears the existing contents of the pasteboard, preparing it for new contents. This is the first step in providing data on the pasteboard....

July 4, 2023 路 1 min 路 Khoa Pham

How to encrypt using CryptoKit in Swift

Issue #925 Use AES.GCM method with 128 bits key import CryptoKit public extension Optional { func tryUnwrap() throws -> Wrapped { if let value = self { return value } else { throw NSError(domain: "", code: 0) } } } public struct Crypto { static func encrypt(input: Data, key: String) -> Data { do { let keyData = Data( .utf8)!.prefix(32)) let key = SymmetricKey(data: keyData) let sealed = try AES.GCM.seal(input, using: key) return try sealed....

July 3, 2023 路 1 min 路 Khoa Pham

How to use NavigationSplitView and NavigationStack in SwiftUI

Issue #924 Note Navigation state needs to be in the container of NavigationSplitView for changes to propagate Need to use WindowGroup for navigation bar to work NavigationSplitView the navigation split view coordinates with the List in its first column, so that when people make a selection, the detail view updates accordingly. Programmatic changes that you make to the selection property also affect both the list appearance and the presented detail view...

June 30, 2023 路 1 min 路 Khoa Pham

How to style NavigationLink in macOS

Issue #923 NavigationLink on Mac applies the default button style. We can style it using ButtonStyle, here to use plain style we can just NavigationLink(value: DetailRoute.books) { BooksView() } .buttonStyle(.plain)

June 28, 2023 路 1 min 路 Khoa Pham

How to make TextField Stepper in SwiftUI

Issue #921 Use HStack with TextField and a little extension extension Binding where Value == Int { var toString: Binding<String> { Binding<String>( get: { "\(wrappedValue)" }, set: { wrappedValue = Int($0) ?? 0 } ) } } struct TextFieldStepper: View { @Binding var value: Int var body: some View { HStack(spacing: 0) { TextField("", text: $value.toString) .textFieldStyle(.roundedBorder) .frame(width: 50) Stepper("", value: $value) } } }

June 20, 2023 路 1 min 路 Khoa Pham

How to create Quick look thumbnail for files

Issue #913 Use QuickLookThumbnailing framework import AppKit import QuickLookThumbnailing actor QuicklookService { static let shared = QuicklookService() private let generator = QLThumbnailGenerator.shared func image( fileUrl: URL, size: CGSize ) async -> NSImage { let scale = NSScreen.main?.backingScaleFactor ?? 2 let request = QLThumbnailGenerator.Request( fileAt: fileUrl, size: size, scale: scale, representationTypes: .thumbnail ) do { let representation = try await generator.generateBestRepresentation(for: request) return representation.nsImage } catch { return NSWorkspace.shared.icon(forFile: fileUrl.path) } } } Read more Creating Quick Look Thumbnails to Preview Files in Your App

May 13, 2023 路 1 min 路 Khoa Pham

How to deal with actor reentrancy in Swift

Issue #912 Perform check before and after suspension point actor Worker { var isDoing = false var toBeDone = Set<String>() func work(string: String) async { if isDoing { toBeDone.insert(string) return } isDoing = true await performHeavyWork(string: string) isDoing = false if let first = toBeDone.popFirst() { await work(string: first) } } private func performHeavyWork(string: String) async { try? await Task.sleep(nanoseconds: 5_000_000_000) print(string) } } func main() { let worker = Worker() Array(0 ....

May 5, 2023 路 1 min 路 Khoa Pham

How to run parallel Task with Swift concurrency

Issue #911 Make an parallelTask function that wraps TaskGroup public func parallelTask(@ParallelTaskBuilder builder: () -> [ParallelTaskBuilder.Work]) async { await withTaskGroup(of: Void.self) { group in for work in builder() { group.addTask { await work.value } } } } @resultBuilder public struct ParallelTaskBuilder { public typealias Work = Task<Void, Never> public static func buildExpression(_ expression: Work?) -> [Work] { if let expression = expression { return [expression] } return [] } public static func buildExpression(_ expression: Work) -> [Work] { [expression] } public static func buildExpression(_ expression: [Work]) -> [Work] { expression } public static func buildBlock(_ components: Work....

April 27, 2023 路 1 min 路 Khoa Pham

How to use Range and NSRange in Swift

Issue #910 Use one-sided range operator let string = "Hello world" string[string.startIndex...] // Hello world string[..<string.endIndex] // Hello world Substring let string = "Hello world" let range = string.startIndex ..< string.index(string.startIndex, offsetBy: 5) string[range] // Hello Convert to and from NSRange let string = "Hello world" let range = string.startIndex... let nsRange = NSRange(range, in: string) let regex = NSRegularExpression(pattern: pattern) let matches = regex.matches(in: string, range: nsRange) for match in matches { let range = Range(match....

March 12, 2023 路 1 min 路 Khoa Pham