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?...
Issue #933
By default, Nextjs route is case sensitive, so localhost:3000/About and localhost:3000/about are different routes.
To make uppercase routes become lowercase routes, we can add a middleware.tsx file to the src so it is same level as pages
import { NextResponse, NextRequest } from "next/server" const Middleware = (req: NextRequest) => { if (req.nextUrl.pathname === req.nextUrl.pathname.toLowerCase()) { return NextResponse.next() } return NextResponse.redirect( new URL(req.nextUrl.origin + req.nextUrl.pathname.toLowerCase()) ) } export default Middleware
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...
Issue #931
AppStore screenshots Screenshot specifications
iPhone 6.7" Portrait 1290 x 2796
iPhone 6.5" Portrait 1242 x 2688
In-App Purchase screenshots In-app purchase information
iOS 640 x 920 tvO 1920 x1080 pixels macOS 1280 x 800 pixels
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?...
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...
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....
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)...
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....
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....
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...
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)
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
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) } } }
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 }
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....
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?...
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?...
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....
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