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’m using the new containerRelativeFrame modifier to size these park cards relative to the visible size of the horizontal scroll view. I’d like my park cards to snap into place. The new scrollTargetLayout modifier makes that easy....

June 7, 2023 · 13 min · 2576 words · Khoa

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 · 32 words · Khoa

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 · 189 words · Khoa

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 · 117 words · Khoa

How to handle status bar with custom overlay UIWindow

Issue #908 When we add another UIWindow, then its rootViewController will decide the style of the status bar, not the rootViewController of the keyWindow anymore childForStatusBarStyle The usual way to fix this is to defer the decision to the correct rootViewController, like in our HUDViewController class HUDViewController: UIViewController { override var childForStatusBarStyle: UIViewController? { let windows = view.window?.windowScene?.windows ?? [] for window in windows where window != self.view.window { return window....

November 22, 2022 · 2 min · 303 words · Khoa

How to make SwiftUI widget in iOS

Issue #906 Read more https://developer.apple.com/documentation/widgetkit/making-a-configurable-widget What is the purpose of getSnapshot method from WidgetKit

October 30, 2022 · 1 min · 14 words · Khoa

How to use actor in Swift concurrency

Issue #905 Protect mutable state with Swift actors Actor reentrancy Imagine we have two different concurrent tasks trying to fetch the same image at the same time. The first sees that there is no cache entry, proceeds to start downloading the image from the server, and then gets suspended because the download will take a while. While the first task is downloading the image, a new image might be deployed to the server under the same URL....

October 5, 2022 · 4 min · 693 words · Khoa

How Task use thread in Swift concurrency

Issue #904 Consider this code where we have an ObservableObject with fetch1 and async fetch2, and a fetch inside ContentView Here the observation in Xcode 14 ViewModel.fetch1: run on main thread ViewModel.fetch2: run on cooperative thread pool ContentView.fetch: run on main thread import SwiftUI import CoreData import Combine class ViewModel: ObservableObject { @Published var string = "" func fetch1() { let url = URL(string: "https://google.com")! let data = try! Data(contentsOf: url) self....

October 4, 2022 · 4 min · 834 words · Khoa

How to handle shortcut intents in iOS

Issue #902 iOS 13 Intents Extension & Intents UI Extension Donate Shortcuts at the Right Time Siri can predict shortcuts to actions that a user may want to perform using your app, and suggest those shortcuts to the user in places such as Spotlight search, Lock Screen, and the Siri watch face. Siri learns about the shortcuts available for your app through donations that your app makes to Siri. To handle the shortcut, implement the application(_:continue:restorationHandler:) method in your app delegate....

September 14, 2022 · 3 min · 481 words · Khoa

How to use Universal Links in iOS

Issue #901 Apple app site association https://com.example/.well-known/apple-app-site-association Supporting Associated Domains New format from iOS 13 Can also remove components section if we match all URLs { "applinks": { "details": [ { "appIDs": [ "ABCDE12345.com.example.app", "ABCDE12345.com.example.app2" ], "components": [ { "/": "/*", "comment": "Match all URLs" } } ] }, } Application Identifier Prefix appID is in form <Application Identifier Prefix>.<Bundle Identifier> For Application Identifier Prefix , you can find it in https://developer....

August 9, 2022 · 3 min · 493 words · Khoa

How to set popoverPresentationController sourceView in SwiftUI

Issue #894 Use a UIView as source view and set it in background class ViewModel { lazy var sourceView = UIView() } struct SourceView: UIViewRepresentable { let viewModel: ViewModel func makeUIView(context: Context) -> UIView { viewModel.sourceView } func updateUIView(_ uiView: UIView, context: Context) {} } Button(action: { onShowAlert(viewModel.sourceView) }) { Image(systemName: "bookmark") } .background(SourceView(viewModel: viewModel)) let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) alertController.popoverPresentationController?.sourceView = viewModel.sourceView

July 8, 2022 · 1 min · 67 words · Khoa

How to get notification userInfo at launch

Issue #884 When user taps on push notification, depending on app state SceneDelegate Checking UIScene.ConnectionOptions.notificationResponse?.notification.request.content.userInfo in scene(_:willConnectTo:options:) app terminated: sometimes nil app in background: notification info UNUserNotificationCenter Checking UNNotificationResponse.notification.request.content.userInfo in userNotificationCenter(_:didReceive:withCompletionHandler:) app terminated: notification info app in background: notification info Read more https://stackoverflow.com/questions/60007715/launchoptions-always-nil-when-launching-from-a-push-notification https://stackoverflow.com/questions/42989932/difference-between-unusernotificationcenterdelegate-and-didreceiveremotenotifica

June 1, 2022 · 1 min · 44 words · Khoa

How to use native SSL Pinning

Issue #880 From iOS 14, we can do Identity Pinning: How to configure server certificates for your app right from Info.plist <key>NSAppTransportSecurity</key> <dict> <key>NSPinnedDomains</key> <dict> <key>awesome.apps.example.com</key> <dict> <key>NSIncludesSubdomains</key> <true/> <key>NSPinnedCAIdentities</key> <array> <dict> <key>SPKI-SHA256-BASE64</key> <string>12312312312xasdas123asdasdasdasdsad</string> </dict> </array> </dict> </dict> </dict> There are a few drawbacks The subdomains works for multiple part host as well, as long as it is 1 subdomain level Need to duplicate values in each Info.plist Need to duplicate values if the host differs Can’t use User Defined Settings variables in localized Info....

May 23, 2022 · 1 min · 97 words · Khoa

How to use NSPersistentCloudKitContainer

Issue #879 Setting Up Core Data with CloudKit Enable iCloud Enable CloudKit and Push Notifications Enable Remote Notifications in the Background Creating a Core Data Model for CloudKit Initialize Your CloudKit Schema During Development let container = NSPersistentCloudKitContainer(name: "Earthquakes") // Only initialize the schema when building the app with the // Debug build configuration. #if DEBUG do { // Use the container to initialize the development schema. try container.initializeCloudKitSchema(options: []) } catch { // Handle any errors....

May 11, 2022 · 2 min · 326 words · Khoa

How to add dot indicator to tab bar item in iOS

Issue #874 From iOS 13, use UITabBarAppearance and UITabBarItemAppearance let appearance = UITabBarAppearance() let itemAppearance = UITabBarItemAppearance(style: .stacked) itemAppearance.normal.badgeBackgroundColor = .clear itemAppearance.normal.badgeTextAttributes = [.foregroundColor: UIColor.red] profileViewController.tabBarItem.badgeValue = "●" Read more https://emptytheory.com/2019/12/31/using-uitabbarappearance-for-tab-bar-changes-in-ios-13/

March 15, 2022 · 1 min · 31 words · Khoa

How to use Multipeer Connectivity

Issue #873 Use assistant let assistant = MCAdvertiserAssistant(serviceType: "my-service, discoveryInfo: nil, session: mcSession) assistant.start() let browser = MCBrowserViewController(serviceType: "my-service", session: mcSession) browser.delegate = self present(browser, animated: true) Manual let advertiser = MCNearbyServiceAdvertiser(peer: localPeerID, discoveryInfo: nil, serviceType: self.serviceType) advertiser.startAdvertisingPeer() let browser = MCNearbyServiceBrowser(peer: localPeerID, serviceType: self.serviceType) browser.startBrowsingForPeers() Enable in Info.plist <key>NSLocalNetworkUsageDescription</key> <string>Enable local network discovery to use Peer Share</string> <key>NSBonjourServices</key> <array> <string>_my-service._tcp</string> <string>_my-service._udp</string> </array> Read more https://www.hackingwithswift.com/example-code/networking/how-to-create-a-peer-to-peer-network-using-the-multipeer-connectivity-framework https://www.toptal.com/ios/collusion-ios-multipeerconnectivity

March 15, 2022 · 1 min · 67 words · Khoa

How to use Apple Pay in iOS

Issue #856 Use PKPaymentRequest and PKPaymentAuthorizationViewController @MainActor final class WalletViewModel: NSObject, ObservableObject { var canMakePayments: Bool { PKPaymentAuthorizationViewController.canMakePayments() } func showApplePay(amount: Amount, from window: UIWindow) { let request = PKPaymentRequest() request.supportedNetworks = [PKPaymentNetwork.amex, .discover, .masterCard, .visa] request.countryCode = "US" request.currencyCode = "USD" request.merchantIdentifier = "merchant.\(Bundle.main.bundleIdentifier!)" request.merchantCapabilities = .capability3DS let item = PKPaymentSummaryItem(label: "Add Cash", amount: amount.toNsDecimal) request.paymentSummaryItems = [item] guard let vc = PKPaymentAuthorizationViewController(paymentRequest: request) else { return } vc.delegate = self window....

January 17, 2022 · 1 min · 114 words · Khoa

How to scale system font size to support Dynamic Type

Issue #847 We should use Dynamic Font Type as much as possible, as per Typography guide and https://www.iosfontsizes.com/ But in case we have to use a specific font, we can scale it with UIFontMetrics import SwiftUI import UIKit extension Font { static func system( scaledSize size: CGFloat, weight: Font.Weight = .regular, design: Font.Design = .default ) -> Font { Font.system( size: UIFontMetrics.default.scaledValue(for: size), weight: weight, design: design ) } } Then instead of...

September 2, 2021 · 1 min · 130 words · Khoa

How to make multiline message text view in SwiftUI

Issue #843 This can be used in message bar input or some popover form. We use sizeThatFits to calculate the height so that it grow under certain height limit import SwiftUI import UIKit struct MessageTextField: View { let placeholder: String @Binding var text: String @Binding var isEditing: Bool @State private var height: CGFloat = 100 var body: some View { UITextViewWrapper( text: $text, isEditing: $isEditing, height: $height ) .frame(height: height) ....

August 21, 2021 · 2 min · 269 words · Khoa

How to make equal width buttons in SwiftUI

Issue #835 I usually define ButtonStyle to encapsulate common styles related to buttons. Here we specify .frame(maxWidth: .infinity) to let this button take the whole width as possible struct MyActionButtonStyle: ButtonStyle { func makeBody(configuration: Configuration) -> some View { configuration.label .font(.headline.bold()) .foregroundColor(.white) .frame(height: 44) .frame(maxWidth: .infinity) .background(Color.green) .cornerRadius(8) } } If we have 2 buttons both specifying maxWidth: .infinity then they will be divided equally with a spacing specified by our HStack...

August 5, 2021 · 1 min · 113 words · Khoa