How to use AppIntents in iOS 16

Issue #930 AppIntents Declare AppShortcutsProvider, note that appShortcuts uses @AppShortcutsBuilder syntax import AppIntents struct OurShortcutsProvider: AppShortcutsProvider { static var shortcutTileColor: ShortcutTileColor = .lightBlue @AppShortcutsBuilder static var appShortcuts: [AppShortcut] { AppShortcut(intent: AskIntent(), phrases: [ "Hey Joy", "Ask \(.applicationName)" ]) } } We can create an app intent in code import AppIntents import OpenAI struct AskIntent: AppIntent { static var title: LocalizedStringResource = "Hey Joy" static var description: IntentDescription = "Ask me anything" @Parameter(title: "Prompt") var prompt: String?...

July 11, 2023 路 1 min 路 Khoa Pham

How to press and hold button in SwiftUI

Issue #929 We can use ButtonStyleConfiguration to detect isPressed state struct RecordButton: View { var body: some View { Button { } label: { Image(systemSymbol: .micFill) } .buttonStyle(RecordButtonStyle()) } } private struct RecordButtonStyle: ButtonStyle { func makeBody(configuration: Configuration) -> some View { configuration.label .background { if configuration.isPressed { Circle() .fill(Color.pink) .frame(square: 30) } } .onChange(of: configuration.isPressed) { isPressed in print("isPressed", isPressed) } } } Alternatively, you can just use a DragGesture...

July 10, 2023 路 1 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 add launch screen in SwiftUI project

Issue #927 For a plain SwiftUI project, Xcode will generate a Info.plist file once you start to edit the Info tab in target settings. There is a Launch screen (UILaunchScreen) key by default. It has a UILaunchScreen which we don鈥檛 need to so we can safely remove it. What we need is a background color and an image. Click the + button next to Launch Screen and type to add Image Name (UIImageName) and Background color (UIColorName)...

July 4, 2023 路 1 min 路 Khoa Pham

How to create UserDefaults property wrapper in Swift

Issue #926 Like AppStorage, we can make a custom UserDefaults property wrapper that conforms to DynamicProperty @propertyWrapper public struct UserDefault<Value>: DynamicProperty { @State private var value: Value let key: String let store: UserDefaults public init( wrappedValue: Value, _ key: String, store: UserDefaults = .standard ) { self.key = key self.store = store let value = (store.object(forKey: key) as? Value) ?? wrappedValue self._value = State(wrappedValue: value) } public var wrappedValue: Value { get { value } nonmutating set { store....

July 3, 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(key.data(using: .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

SwiftUI EnvironmentValues

Issue #922 EnvironmentValues Views in SwiftUI can react to configuration information that they read from the environment using an Environment property wrapper Updated for iOS 17

June 21, 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 clear TextEditor background in SwiftUI

Issue #920 For iOS 16 and macOS 13.0 TextEditor(text: $text) .scrollContentBackground(.hidden) For below, use [SwiftUI-Introspect](https://github.com/siteline/SwiftUI-Introspect) TextEditor(text: $text) .instrospectTextView { $0.drawsBackground = false }

June 19, 2023 路 1 min 路 Khoa Pham

Learning Metal for SwiftUI

Issue #919 WWDC23 introduces lots of new additions to SwiftUI, notably Metal shader support with these modifiers colorEffect: Returns a new view that applies shader to self as a filter effect on the color of each pixel. layerEffect: Returns a new view that applies shader to self as a filter on the raster layer created from self. distortionEffect: Returns a new view that applies shader to self as a geometric distortion effect on the location of each pixel....

June 16, 2023 路 3 min 路 Khoa Pham

WWDC23 SwiftUI Q&A

Issue #918 Interesting SwiftUI Q&A during WWDC23 Observable vs ObservableObject Q: With the new SwiftUI @Observable macro, are there any cases where ObservableObject would still be a better alternative? A: Use ObservableObject when you need to back deploy to before iOS 17 A: SwiftUI registers dependencies is a value is read during the evaluation of body. Indirect modifications should invalidate the dependencies. containerRelativeFrame in ScrollView Q: For the containerRelativeFrame, does that work cleanly in a List as well as a ScrollView?...

June 12, 2023 路 27 min 路 Khoa Pham

WWDC21 SwiftUI Q&A

Issue #917 Interesting SwiftUI Q&A during WWDC21 @StateObject or ObservedObject? Q: Let鈥檚 say I have a purely SwiftUI flow. I have a ListView with a @StateObject var listFetcher, that makes requests for a list of items. Tapping on an item in the list navigates to a DetailView, which has an ObservableObject property detailFetcher, that makes requests for details on the item. What鈥檚 the best way to structure DetailView and which property wrapper would we use for detailFetcher in DetailView?...

June 12, 2023 路 7 min 路 Khoa Pham

What's new in SwiftUI iOS 17 at WWDC23

Issue #916 WWDC23 brings new additions to SwiftUI Scrolling The scroll transition modifier is very similar to the visual effect modifier Curt used earlier for the welcome screen. It lets you apply effects to items in your scroll view. I鈥檓 using the new containerRelativeFrame modifier to size these park cards relative to the visible size of the horizontal scroll view. I鈥檇 like my park cards to snap into place....

June 7, 2023 路 13 min 路 Khoa Pham

Recommended iOS articles to read

Issue #915 Here are my favorite iOS articles Why Your App Looks Better in Sketch Managing objects using Locks and Keys in Swift Proof in Functions When Not to Use an Enum

June 7, 2023 路 1 min 路 Khoa Pham

How to render markdown view with React

Issue #914 Use react-markdown to parse markdown content, react-syntax-highlighter to highlight code, and rehype-raw to parse raw html import ReactMarkdown from "react-markdown" import { Prism as SyntaxHighlighter } from "react-syntax-highlighter" import { oneDark } from "react-syntax-highlighter/dist/cjs/styles/prism" import rehypeRaw from "rehype-raw" type Props = { content: string } export default (props: Props) => { return ( <div className="markdown-body"> <ReactMarkdown children={props.content} rehypePlugins={[rehypeRaw]} components={{ code({ node, inline, className, children, ...props }) { const match = /language-(\w+)/....

June 6, 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