How to pass ObservedObject as parameter in SwiftUI

Issue #685 Since we have custom init in ChildView to manually set a State, we need to pass ObservedObject. In the ParentView, use underscore _ to access property wrapper type. struct ChildView: View { @ObservedObject var store: Store @State private var selectedTask: AnyTask init(store: ObservedObject<Store>) { _selectedTask = State(initialValue: tasks.first!) _store = store } } struct ParentView: View { @ObservedObject var store: Store var body: some View { ChildView(store: _store) }

October 24, 2020 · 1 min · 71 words · Khoa

How to do equal width in SwiftUI

Issue #684 In SwiftUI, specifying maxWidth as .infinity means taking the whole available width of the container. If many children ask for max width, then they will be divided equally. This is similar to weight in LinearLayout in Android or css flex-grow property. The same applies in vertical direct also. struct ContentView: View { var body: some View { HStack(spacing: 0) { VStack { Spacer() } .frame(maxWidth: .infinity) .background(Color.red) VStack { Spacer() } ....

October 23, 2020 · 1 min · 135 words · Khoa

How to test push notifications in simulator and production iOS apps

Issue #682 After has recently reminded about his updating APNs provider API which makes me realised a lot has changed about push notifications, both in terms of client and provider approach. The HTTP/2-based Apple Push Notification service (APNs) provider API lets you take advantage of great features, such as authentication with a JSON Web Token, improved error messaging, and per-notification feedback. If you send push notifications with the legacy binary protocol, we strongly recommend upgrading to the APNs provider API....

October 11, 2020 · 12 min · 2474 words · Khoa

How to style multiline Text in SwiftUI for macOS

Issue #681 Only need to specify fixedSize on text to preserve ideal height. The maximum number of lines is 1 if the value is less than 1. If the value is nil, the text uses as many lines as required. The default is nil. Text(longText) .lineLimit(nil) // No need .fixedSize(horizontal: false, vertical: true) If the Text is inside a row in a List, fixedSize causes the row to be in middle of the List, workaround is to use ScrollView and vertical StackView....

October 7, 2020 · 1 min · 98 words · Khoa

How to clear List background color in SwiftUI for macOS

Issue #680 For List in SwiftUI for macOS, it has default background color because of the enclosing NSScrollView via NSTableView that List uses under the hood. Using listRowBackground also gives no effect The solution is to use a library like SwiftUI-Introspect import Introspect extension List { func removeBackground() -> some View { return introspectTableView { tableView in tableView.backgroundColor = .clear tableView.enclosingScrollView!.drawsBackground = false } } } then List { ForEach(items) { item in // view here } } ....

October 4, 2020 · 1 min · 204 words · Khoa

How to avoid reduced opacity when hiding view with animation in SwiftUI

Issue #679 While redesigning UI for my app Push Hero, I ended up with an accordion style to toggle section. It worked great so far, but after 1 collapsing, all image and text views have reduced opacity. This does not happen for other elements like dropdown button or text. extension View { func sectionBackground(_ title: String, _ shows: Binding<Bool>) -> some View { VStack(alignment: .leading) { HStack { Text(title.uppercased()) Spacer() if shows !...

October 1, 2020 · 1 min · 141 words · Khoa

How to dynamically add items to VStack from list in SwiftUI

Issue #678 Use enumerated to get index so we can assign to item in list. Here is how I show list of device tokens in my app Push Hero private var textViews: some View { let withIndex = input.deviceTokens.enumerated().map({ $0 }) let binding: (Int, Input.DeviceToken) -> Binding<String> = { index, token in Binding<String>( get: { token.token }, set: { self.input.deviceTokens[index].token = $0 } ) } return VStack { ForEach(withIndex, id: \....

October 1, 2020 · 1 min · 82 words · Khoa

How to unwrap Binding with Optional in SwiftUI

Issue #677 The quick way to add new properties without breaking current saved Codable is to declare them as optional. For example if you use EasyStash library to save and load Codable models. import SwiftUI struct Input: Codable { var bundleId: String = "" // New props var notificationId: String? This new property when using dollar syntax $input.notificationId turn into Binding with optional Binding<Strting?> which is incompatible in SwiftUI when we use Binding....

September 29, 2020 · 1 min · 136 words · Khoa

How to use Binding in function in Swift

Issue #675 Use wrappedValue to get the underlying value that Binding contains extension View { func addOverlay(shows: Binding<Bool>) -> some View { HStack { self Spacer() } .overlay( HStack { Spacer() SmallButton( imageName: "downArrow", tooltip: shows.wrappedValue ? "Collapse" : "Expand", action: { shows.wrappedValue.toggle() } ) .rotationEffect(.radians(shows.wrappedValue ? .pi : 0)) } ) } }

September 25, 2020 · 1 min · 54 words · Khoa

How to use HSplitView to define 3 panes view in SwiftUI for macOS

Issue #674 Specify minWidth to ensure miminum width, and use .layoutPriority(1) for the most important pane. import SwiftUI struct MainView: View { @EnvironmentObject var store: Store var body: some View { HSplitView { LeftPane() .padding() .frame(minWidth: 200, maxWidth: 500) MiddlePane(store: store) .padding() .frame(minWidth: 500) .layoutPriority(1) RightPane() .padding() .frame(minWidth: 300) } .background(R.color.background) } }

September 23, 2020 · 1 min · 53 words · Khoa

How to draw arc corner using Bezier Path

Issue #673 To draw rounded 2 corners at top left and top right, let’s start from bottom left let path = UIBezierPath() // bottom left path.move(to: CGPoint(x: 0, y: bounds.height)) // top left corner path.addArc(withCenter: CGPoint(x: radius, y: radius), radius: radius, startAngle: CGFloat.pi, endAngle: CGFloat.pi * 3 / 2, clockwise: true) // top right corner path.addArc(withCenter: CGPoint(x: bounds.width - radius, y: radius), radius: radius, startAngle: CGFloat.pi * 3 / 2, endAngle: 0, clockwise: true) // bottom right path....

September 15, 2020 · 1 min · 86 words · Khoa

How to stitch and sort array in Swift

Issue #672 Supposed we want to stitch magazines array into books array. The requirement is to sort them by publishedDate, but must keep preferredOrder of books. One way to solve this is to declare an enum to hold all possible cases, and then do a sort that check every possible combination struct Book { let preferredOrder: Int let publishedDate: Date } struct Magazine { let publishedDate: Date } enum StitchItem { case book(Book) case magazine(Magazine) } func stitch(_ books: [Book], magazines: [Magazine]) -> [StitchItem] { let items = books....

August 28, 2020 · 2 min · 304 words · Khoa

How to make dynamic font size for UIButton

Issue #671 Use adjustsFontForContentSizeCategory A Boolean that indicates whether the object automatically updates its font when the device’s content size category changes. If you set this property to YES, the element adjusts for a new content size category on a UIContentSizeCategoryDidChangeNotification. button.titleLabel?.adjustsFontForContentSizeCategory = true button.backgroundColor = UIColor.green button.titleLabel?.font = UIFont.preferredFont(forTextStyle: .title1) label.adjustsFontForContentSizeCategory = true label.backgroundColor = UIColor.yellow label.font = UIFont.preferredFont(forTextStyle: .title1) However it seems view (UIButton or UILabel) size is the same, just the inner text increases in size....

August 14, 2020 · 1 min · 95 words · Khoa

How to test for view disappear in navigation controller

Issue #670 To test for viewWillDisappear during UINavigationController popViewController in unit test, we need to simulate UIWindow so view appearance works. final class PopTests: XCTestCase { func testPop() { let window = UIWindow(frame: UIScreen.main.bounds) let navigationController = UINavigationController() window.rootViewController = navigationController let viewController = DetailViewController() navigationController.viewControllers = [ UIViewController(), viewController ] window.makeKeyAndVisible() let expectation = XCTestExpectation() navigationController.popViewController(animated: false) DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { XCTAssertTrue(viewController.wasDismissed) expectation.fulfill() } wait(for: [expectation], timeout: 1) } } class DetailViewController: UIViewController { override func viewWillDisappear(_ animated: Bool) { super....

August 6, 2020 · 1 min · 96 words · Khoa

How to set text color for UIDatePicker

Issue #660 Apply tintColor does not seem to have effect. datePicker.setValue(UIColor.label, forKeyPath: "textColor") datePicker.setValue(false, forKey: "highlightsToday")

June 10, 2020 · 1 min · 16 words · Khoa

How to make switch statement in SwiftUI

Issue #656 Lately I’ve been challenging myself to declare switch statement in SwiftUI, or in a more generalized way, execute any anonymous function that can return a View Use Switch and Case views Note that this approach does not work yet, as TupeView should support variadic number of contents, and also T.RawValue needs to conform to Equatable in order to check the cases. Also in Switch statement, Content can’t be inferred...

May 22, 2020 · 2 min · 280 words · Khoa

How to test DispatchQueue in Swift

Issue #646 Sync the DispatchQueue Pass DispatchQueue and call queue.sync to sync all async works before asserting Use mock Use DispatchQueueType and in mock, call the work immediately import Foundation public protocol DispatchQueueType { func async(execute work: @escaping @convention(block) () -> Void) } extension DispatchQueue: DispatchQueueType { public func async(execute work: @escaping @convention(block) () -> Void) { async(group: nil, qos: .unspecified, flags: [], execute: work) } } final class MockDispatchQueue: DispatchQueueType { func async(execute work: @escaping @convention(block) () -> Void) { work() } }

April 29, 2020 · 1 min · 84 words · Khoa

How to assert asynchronously in XCTest

Issue #644 import XCTest extension XCTestCase { /// Asynchronously assertion func XCTAssertWait( timeout: TimeInterval = 1, _ expression: @escaping () -> Void, _: String = "", file _: StaticString = #file, line _: UInt = #line ) { let expectation = self.expectation(description: #function) DispatchQueue.main.asyncAfter(deadline: .now() + timeout) { expression() expectation.fulfill() } let waiter = XCTWaiter() XCTAssertTrue(waiter.wait(for: [expectation], timeout: timeout + 1) == .completed) } } Updated at 2020-04-28 09:23:59

April 28, 2020 · 1 min · 69 words · Khoa

How to format percent in Swift

Issue #639 Never use String(format: "%.2f %%", 1.2 because each region can have different separator and placement of percent sign. Use NumberFormatter instead let formatter = NumberFormatter() formatter.numberStyle = .percent formatter.minimumIntegerDigits = 1 formatter.maximumIntegerDigits = 3 formatter.maximumFractionDigits = 2 formatter.locale = Locale(identifier: "en_US") formatter.string(from: NSDecimalNumber(decimal: 1.2 / 100)) // 0.12% formatter.locale = Locale(identifier: "nb_NO") formatter.string(from: NSDecimalNumber(decimal: 1.2 / 100)) // 0,12 % Note that the space created by NumberFormatter is a non breakable space \u{00a0}, which can be created by Alt Space....

April 16, 2020 · 1 min · 99 words · Khoa

How to declare commands in Xcode extensions

Issue #638 Use commandDefinitions in XCSourceEditorExtension. import Foundation import XcodeKit class SourceEditorExtension: NSObject, XCSourceEditorExtension { func extensionDidFinishLaunching() { } var commandDefinitions: [[XCSourceEditorCommandDefinitionKey: Any]] { func makeDef( _ className: String, _ commandName: String ) -> [XCSourceEditorCommandDefinitionKey: Any] { guard let bundleId = Bundle(for: type(of: self)).bundleIdentifier else { return [:] } return [ XCSourceEditorCommandDefinitionKey.identifierKey: bundleId + className, XCSourceEditorCommandDefinitionKey.classNameKey: className, XCSourceEditorCommandDefinitionKey.nameKey: commandName ] } return [ makeDef(TypeCommand.className(), "Type"), makeDef(ReloadCommand.className(), "Reload"), ] } } There is a weird crash that we can’t seem to declare functions or use commandDefinitions, the workaround is to declare in plist...

April 13, 2020 · 1 min · 95 words · Khoa