How to use Codable to store preferences in Swift

Issue #462 Using object, we don’t need to care about nil vs false like in UserDefaults, our object is the source of truth class StoringHandler<T: Codable> { private let key: Storage.Keys private let storage = Deps.storage init(key: Storage.Keys) { self.key = key load() } var object: T? { didSet { if let object = object { try? storage.save(object: object, key: key) } else { try? storage.clear(key: key) } } } private func load() { self....

October 11, 2019 · 1 min · 123 words · Khoa

How to custom UIAlertController in iOS

Issue #461 With UIAlertController we can add buttons and textfields, and just that func addAction(UIAlertAction) func addTextField(configurationHandler: ((UITextField) -> Void)?) To add custom content to UIAlertController, there are some workarounds Add content onto UITextField Restyle UITextField and add custom content Subclass UIAlertController and handle UI in viewWillAppear By subclassing we can tweak the whole view hierarchy and listen to events like layout subviews, but this can be very fragile. Make custom UIViewController that looks like UIAlertController This is the correct way but takes too much time to imitate UIAlertController, and have to deal with UIVisualEffectView, resize view for different screen sizes and dark mode...

October 11, 2019 · 2 min · 222 words · Khoa

How to find subview recursively in Swift

Issue #460 extension UIView { func findRecursively<T: UIView>(type: T.Type, match: (T) -> Bool) -> T? { for view in subviews { if let subview = view as? T, match(subview) { return subview } else { return view.findRecursively(type: type, match: match) } } return nil } }

October 11, 2019 · 1 min · 46 words · Khoa

How to use provisional notification in iOS 12

Issue #456 From WWDC 2018 What’s New in User Notifications Instead, the notifications from your app will automatically start getting delivered. Notifications that are delivered with provisional authorization will have a prompt like this on the notification itself. And this will help the users decide after having received a few notifications whether they want to keep getting these notifications or whether they want to turn them off It’s an automatic trial of the notifications from your app to help your users make a more informed decision about whether they want these notifications....

October 10, 2019 · 2 min · 281 words · Khoa

How to check if push notification is actually enabled in iOS

Issue #455 There are times we want to log if user can receive push notification. We may be tempted to merely use isRegisteredForRemoteNotifications but that is not enough. From a user ’s point of view, they can either receive push notification or not. But behind the scene, many factors are in the game. It can be that user has disabled push notification in app settings or in iOS Settings. It can also be that user enables push notification but disables all sound or banner display mechanism....

October 10, 2019 · 2 min · 289 words · Khoa

How to use AnyHashable in Swift

Issue #453 From documentation A type-erased hashable value. DiscussionThe AnyHashable type forwards equality comparisons and hashing operations to an underlying hashable value, hiding its specific underlying type.You can store mixed-type keys in dictionaries and other collections that require Hashable conformance by wrapping mixed-type keys in AnyHashable instances let descriptions: [AnyHashable: Any] = [ AnyHashable("😄"): "emoji", AnyHashable(42): "an Int", AnyHashable(Int8(43)): "an Int8", AnyHashable(Set(["a", "b"])): "a set of strings" ] print(descriptions[AnyHashable(42)]!) // prints "an Int" print(descriptions[AnyHashable(43)]) // prints "nil" print(descriptions[AnyHashable(Int8(43))]!...

October 10, 2019 · 1 min · 110 words · Khoa

How to register for alert push notification in iOS

Issue #452 Use UserNotifications framework import FirebaseMessaging import UserNotifications final class PushHandler: NSObject { private let center = UNUserNotificationCenter.current() private let options: UNAuthorizationOptions = [.alert] func setup() { Messaging.messaging().delegate = self } func register() { center.requestAuthorization(options: options, completionHandler: { granted, error in print(granted, error) self.getSettings() }) } func getSettings() { center.getNotificationSettings(completionHandler: { settings in guard case let .authorized = settings.authorizationStatus, case let .enabled = settings.alertSetting, settings.alertStyle != .none else { return } // TODO }) } func handle(userInfo: [AnyHashable: Any]) { } } extension PushHandler: MessagingDelegate { func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) { print(fcmToken) } func messaging(_ messaging: Messaging, didReceive remoteMessage: MessagingRemoteMessage) { print(remoteMessage) } } @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { func application( _ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { completionHandler(....

October 10, 2019 · 1 min · 169 words · Khoa

How to flat map publisher of publishers in Combine

Issue #451 For some services, we need to deal with separated APIs for getting ids and getting detail based on id. To chain requests, we can use flatMap and Sequence, then collect to wait and get all elements in a single publish FlatMap Transforms all elements from an upstream publisher into a new or existing publisher. struct FlatMap<NewPublisher, Upstream> where NewPublisher : Publisher, Upstream : Publisher, NewPublisher.Failure == Upstream.Failure Publishers.Sequence...

October 9, 2019 · 1 min · 191 words · Khoa

How to make container view in SwiftUI

Issue #450 Following the signatures of ScrollView and Group, we can create our own container public struct ScrollView<Content> : View where Content : View { /// The content of the scroll view. public var content: Content } extension Group : View where Content : View { /// The type of view representing the body of this view. /// /// When you create a custom view, Swift infers this type from your /// implementation of the required `body` property....

October 9, 2019 · 2 min · 225 words · Khoa

How to use protocol in List in SwiftUI

Issue #446 Suppose we have Service protocol, and want to use in List protocol Service { var name: String { get } } struct MainView: View { let services = [ Car() Plane() ] var body: some View { List(services) { service in HStack { Image(service.name) Text(service.name) } } } } This is not possible because item in List needs to conform to Identifiable Protocol type ‘Service’ cannot conform to ‘Identifiable’ because only concrete types can conform to protocols...

October 7, 2019 · 2 min · 231 words · Khoa

How to support Swift Package Manager for existing projects

Issue #445 How to add SPM Run swift package init Check enum for version for each platform https://developer.apple.com/documentation/swift_packages/supportedplatform/tvosversion Example https://github.com/onmyway133/Anchors/blob/master/Package.swift // swift-tools-version:5.1 import PackageDescription let package = Package( name: "Anchors", platforms: [ .macOS(.v10_11), .iOS(.v9), .tvOS(.v9) ], products: [ .library( name: "Anchors", targets: ["Anchors"]), ], targets: [ .target( name: "Anchors", dependencies: [], path: "Sources" ), .testTarget( name: "AnchorsTests", dependencies: ["Anchors"]), ], swiftLanguageVersions: [.v5] ) To test, swift test to test locally, this should validate Package....

October 7, 2019 · 1 min · 202 words · Khoa

How to fix unreadable ASCII characters in Swift

Issue #444 To avoid compiler error Unprintable ASCII character found in source file, from Swift 5, we can use isASCII. Run this from the generator app that generates Swift code. let normalized = weirdString.filter({ $0.isASCII }) For more check, see Character https://developer.apple.com/documentation/swift/character/3127011-isletter Watch out for Delete/House symbol ⌂ code 127 (0x7f) The above does not seem to work, use find to find  character (copy Sublime Text to see 0x7f character)...

October 7, 2019 · 1 min · 83 words · Khoa

How to style NSTextView and NSTextField in macOS

Issue #443 let textField = NSTextField() textField.focusRingType = .none let textView = NSTextView() textView.insertionPointColor = R.color.caret textView.isRichText = false textView.importsGraphics = false textView.isEditable = true textView.isSelectable = true textView.drawsBackground = false textView.allowsUndo = true

October 5, 2019 · 1 min · 34 words · Khoa

How to center NSWindow in screen

Issue #442 On macOS, coordinate origin is bottom left let window = NSWindow(contentRect: rect, styleMask: .borderless, backing: .buffered, defer: false) window.center() let frame = window.frame window.setFrameOrigin(CGPoint(x: frame.origin.x, y: 100))

October 4, 2019 · 1 min · 29 words · Khoa

How to remove duplicates based on property in array in Swift

Issue #441 Make object conform to Equatable and Hashable and use Set to eliminate duplications. Set loses order so we need to sort after uniquing struct App: Equatable, Hashable { static func == (lhs: App, rhs: App) -> Bool { return lhs.name == rhs.name && lhs.bundleId == rhs.bundleId } func hash(into hasher: inout Hasher) { hasher.combine(name) hasher.combine(bundleId) } } let uniqueApps = Array(Set(unsortedApps)) let apps = uniqueApps.sorted(by: { $0.name.lowercased() < $1....

October 3, 2019 · 1 min · 72 words · Khoa

How to log Error in Swift

Issue #439 Use localizedDescription We need to provide NSLocalizedDescriptionKey inside user info dictionary, otherwise the outputt string may not be what we want. NSError https://developer.apple.com/documentation/foundation/nserror/1414418-localizeddescription A string containing the localized description of the error. The object in the user info dictionary for the key NSLocalizedDescriptionKey. If the user info dictionary doesn’t contain a value for NSLocalizedDescriptionKey, a default string is constructed from the domain and code. let error = NSError(domain: "com....

October 3, 2019 · 1 min · 164 words · Khoa

How to handle NSTextField change in macOS

Issue #438 Storyboard In Storyboard, NSTextField has an Action option that specify whether Send on Send on Enter only` should be the default behaviour. Code In code, NSTextFieldDelegate notifies whenever text field value changes, and target action notifies when Enter key is pressed import Cocoa class ViewController: NSViewController, NSTextFieldDelegate { @IBOutlet weak var textField: NSTextField! override func viewDidLoad() { super.viewDidLoad() textField.delegate = self } func controlTextDidChange(_ obj: Notification) { let textField = obj....

October 2, 2019 · 1 min · 116 words · Khoa

How to add section header to NSCollectionView in macOS

Issue #437 Normal Use Omnia for itemId extension HeaderCell.swift final class HeaderCell: NSView, NSCollectionViewSectionHeaderView { let label: NSTextField = withObject(NSTextField(labelWithString: "")) { $0.textColor = R.color.header $0.font = R.font.header $0.alignment = .left $0.lineBreakMode = .byTruncatingTail } override init(frame frameRect: NSRect) { super.init(frame: frameRect) addSubviews([label]) activate( label.anchor.centerY, label.anchor.left.constant(8) ) } required init?(coder: NSCoder) { fatalError() } } ViewController.swift collectionView.register( HeaderCell.self, forSupplementaryViewOfKind: NSCollectionView.elementKindSectionHeader, withIdentifier: HeaderCell.itemId ) func collectionView(_ collectionView: NSCollectionView, viewForSupplementaryElementOfKind kind: NSCollectionView.SupplementaryElementKind, at indexPath: IndexPath) -> NSView { if kind == NSCollectionView....

October 2, 2019 · 2 min · 284 words · Khoa

How to show context menu from NSButton in macOS

Issue #435 Use NSMenu and popUp func showQuitMenu() { let menu = NSMenu() let aboutItem = NSMenuItem( title: "About", action: #selector(onAboutTouched(_:)), keyEquivalent: "" ) let quitItem = NSMenuItem( title: "Quit Hacker Pad", action: #selector(onQuitTouched(_:)), keyEquivalent: "" ) aboutItem.target = self quitItem.target = self menu.addItem(aboutItem) menu.addItem(quitItem) menu.popUp( positioning: aboutItem, at: bottomView.quitButton.frame.origin, in: bottomView ) } Use Omnia let menuHandler = MenuHandler() menuHandler.add(title: "About", action: { NSWorkspace.shared.open(URL(string: "https://onmyway133.github.io/")!) }) menuHandler.add(title: "Quit Hacker Pad", action: { NSApp....

October 1, 2019 · 1 min · 79 words · Khoa

How to handle UICollectionView reloadData with selected index path

Issue #434 When calling collectionView.reloadData(), selected indexpath stays the same, but be aware that order of data may have changed let selectedData = ... let indexPathForSelectedData = ... collectionView.scrollToItem( at: indexPathForSelectedData, at: .centeredHorizontally, animated: false )

September 30, 2019 · 1 min · 36 words · Khoa