How to add monkey test to iOS apps

Issue #484 Use SwiftMonkey which adds random UITests gestures Add to UITests target target 'MyAppUITests' do pod 'R.swift', '~> 5.0' pod 'SwiftMonkey', '~> 2.1.0' end Troubleshooting Failed to determine hittability of Button Failed to determine hittability of Button: Unable to fetch parameterized attribute XC_kAXXCParameterizedAttributeConvertHostedViewPositionFromContext, remote interface does not have this capability. This happens when using SwiftMonkey and somewhere in our code uses isHittable, so best to avoid that by having isolated monkey test only...

November 1, 2019 · 1 min · Khoa Pham

How to use array of strings in ForEach in SwiftUI

Issue #483 Every item in list must be uniquely identifiable List { ForEach(books, id: \.bookId) { book in NavigationLink(destination: BookView(book: book) .navigationBarTitle(book.name) ) { VStack { Text(book.name) } } } } In case of primitive, we can just provide id to conform to Identifiable extension String: Identifiable { public var id: String { return self } }

October 31, 2019 · 1 min · Khoa Pham

How to make multiline Text in SwiftUI in watchOS

Issue #482 lineLimit does not seem to work, use fixedSize instead Fixes this view at its ideal size. A view that fixes this view at its ideal size in the dimensions given in fixedDimensions. extension Text { func styleText() -> some View { return self .font(.footnote) .foregroundColor(.gray) .lineLimit(10) .fixedSize(horizontal: false, vertical: true) } }

October 31, 2019 · 1 min · Khoa Pham

How to show documentations for GitHub projects

Issue #481 Use jazzy to generate documentation which the generated artifacts in docs folder GitHub has a nifty feature to use docs as GitHub pages

October 31, 2019 · 1 min · Khoa Pham

How to use CommonCrypto in iOS

Issue #480 Use modulemap modulemap approach I use modulemap in my wrapper around CommonCrypto https://github.com/onmyway133/arcane, https://github.com/onmyway133/Reindeer For those getting header not found, please take a look https://github.com/onmyway133/Arcane/issues/4 or run xcode-select --install Make a folder CCommonCrypto containing module.modulemap module CCommonCrypto { header "/usr/include/CommonCrypto/CommonCrypto.h" export * } Go to Built Settings -> Import Paths ${SRCROOT}/Sources/CCommonCrypto Cocoapods with modulemap approach Here is the podspec https://github....

October 29, 2019 · 2 min · Khoa Pham

How to make ISO 8601 date in Swift

Issue #479 From ISO8601 spec, the problems are the representation and time zone ISO 8601 = year-month-day time timezone For date and time, there are basic (YYYYMMDD, hhmmss, ...) and extended format (YYYY-MM-DD, hh:mm:ss, ...) Time zone can be Zulu, offset or GMT Separator for date and time can be space, or T There are week format for date, but it is rarely used Timezone can be a lot of spaces after Second is optional Here are some valid strings...

October 29, 2019 · 3 min · Khoa Pham

How to configure test target in Xcode

Issue #478 This applies to Main targets App Framework Test targets Unit tests UI tests Examples Dependencies used Main target: Sugar Test target: Nimble, Quick Examples Cocoapods Carthage Notes Make sure test target can link to all the frameworks it needs. This includes frameworks that Test targets use, and possibly frameworks that Main target uses ! Remember to “Clean Build Folder” and “Clear Derived Data” so that you’re sure it works....

October 29, 2019 · 2 min · Khoa Pham

How to check platform versions in Swift

Issue #477 Mark APIs availability @available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) public extension View { } Check platform #if canImport(UIKit) import UIKit #elseif canImport(OSX) import AppKit #endif In watchOS app, it still can import UIKit, so for only iOS usage, we need to use os check #if canImport(UIKit) && os(iOS) Check environment #if targetEnvironment(macCatalyst) print("UIKit running on macOS") #else print("Your regular code") #endif #if targetEnvironment(simulator) // your simulator code #else // your real device code #endif

October 29, 2019 · 1 min · Khoa Pham

How to use react-native link and CocoaPods

Issue #476 React Native comes with a default React library, and most CocoaPods libraries for React Native has React as a dependency pod, which causes the conflict https://github.com/react-native-community/react-native-svg/issues/621 https://stackoverflow.com/questions/45296994/difference-between-react-native-link-and-cocoapods https://github.com/onmyway133/notes/issues/486 https://github.com/facebook/react-native/pull/23563

October 29, 2019 · 1 min · Khoa Pham

How to flick using UIKit Dynamic in iOS

Issue #475 For a snack bar or image viewing, it’s handy to be able to just flick or toss to dismiss We can use UIKit Dynamic, which was introduced in iOS 7, to make this happen. Use UIPanGestureRecognizer to drag view around, UISnapBehavior to make view snap back to center if velocity is low, and UIPushBehavior to throw view in the direction of the gesture. import UIKit final class FlickHandler { private let viewToMove: UIView private let referenceView: UIView private var panGR: UIPanGestureRecognizer!...

October 23, 2019 · 2 min · Khoa Pham

How to use Swift package manager in watchOS

Issue #474 SPM Go to Project -> Swift Packages, add package. For example https://github.com/onmyway133/EasyStash Select your WatchKit Extension target, under Frameworks, Libraries and Embedded Content add the library CocoaPods If we use CocoaPods, then it needs to be in WatchKit Extension target 'MyApp WatchKit Extension' do use_frameworks! pod 'EasyStash', :git => 'https://github.com/onmyway133/EasyStash' end

October 23, 2019 · 1 min · Khoa Pham

How to use external display in iOS

Issue #473 Before iOS 13 Use UIScreen.didConnectNotification NotificationCenter.default.addObserver(forName: UIScreen.didConnectNotification, object: nil, queue: nil) { (notification) in // Get the new screen information. let newScreen = notification.object as! UIScreen let screenDimensions = newScreen.bounds // Configure a window for the screen. let newWindow = UIWindow(frame: screenDimensions) newWindow.screen = newScreen // Install a custom root view controller in the window. self.configureAuxilliaryInterface(with: newWindow) // You must show the window explicitly. newWindow.isHidden = false // Save a reference to the window in a local array....

October 22, 2019 · 2 min · Khoa Pham

How to show error message like Snack Bar in iOS

Issue #472 Build error view Use convenient code from Omnia To make view height dynamic, pin UILabel to edges and center import UIKit final class ErrorMessageView: UIView { let box: UIView = { let view = UIView() view.backgroundColor = R.color.primary view.layer.cornerRadius = 6 return view }() let label: UILabel = { let label = UILabel() label.styleAsText() label.textColor = R.color.darkText label.numberOfLines = 0 return label }() override init(frame: CGRect) { super.init(frame: frame) setup() } required init?...

October 21, 2019 · 3 min · Khoa Pham

How to hide tab bar when push in iOS

Issue #471 let navigationController = UINavigationController(rootViewController: viewControllerA) navigationController.pushViewController(viewControllerB, animated: true) In view controller B, need to set hidesBottomBarWhenPushed in init final class ViewControllerB: UIViewController { let mainView = EditPaymentMethodView() var scenario: PaymentMethodScenario! init() { super.init(nibName: nil, bundle: nil) hidesBottomBarWhenPushed = true } required init?(coder: NSCoder) { fatalError() } }

October 18, 2019 · 1 min · Khoa Pham

How to add trailing image to UILabel in iOS

Issue #470 Use NSTextAttachment inside NSAttributedString extension UILabel { func addTrailing(image: UIImage) { let attachment = NSTextAttachment() attachment.image = image let attachmentString = NSAttributedString(attachment: attachment) let string = NSMutableAttributedString(string: self.text!, attributes: [:]) string.append(attachmentString) self.attributedText = string } }

October 17, 2019 · 1 min · Khoa Pham

How to handle different states in a screen in iOS

Issue #469 If there are lots of logics and states inside a screen, it is best to introduce parent and child container, and switch child depends on state. Each child acts as a State handler. In less logic case, we can introduce a Scenario class that holds the state. So the ViewController can be very slim. The thing with State is that all possible scenarios are clear and required to be handled...

October 17, 2019 · 1 min · Khoa Pham

How to reload data without using onAppear in SwiftUI in watchOS

Issue #468 From onAppeear Adds an action to perform when the view appears. In theory, this should be triggered every time this view appears. But in practice, it is only called when it is pushed on navigation stack, not when we return to it. So if user goes to a bookmark in a bookmark list, unbookmark an item and go back to the bookmark list, onAppear is not called again and the list is not updated....

October 17, 2019 · 2 min · Khoa Pham

How to use EnvironmentObject in SwiftUI for watchOS

Issue #467 Declare top dependencies in ExtensionDelegate class ExtensionDelegate: NSObject, WKExtensionDelegate { let storeContainer = StoreContainer() func applicationDidEnterBackground() { storeContainer.save() } } Reference that in HostingController. Note that we need to change from generic MainView to WKHostingController<AnyView> as environmentObject returns View protocol class HostingController: WKHostingController<AnyView> { var storeContainer: StoreContainer! override func awake(withContext context: Any?) { super.awake(withContext: context) self.storeContainer = (WKExtension.shared().delegate as! ExtensionDelegate).storeContainer } override var body: AnyView { return AnyView(MainView() ....

October 17, 2019 · 1 min · Khoa Pham

How to structure classes

Issue #466 iOS View Controller -> View Model | Logic Handler -> Data Handler -> Repo Android Activity -> Fragment -> View Model | Logic Handler -> Data Handler -> Repo

October 16, 2019 · 1 min · Khoa Pham

How to make generic store for Codable in Swift

Issue #465 Use EasyStash import EasyStash final class Store<T: Codable & ItemProtocol>: Codable { var items = [T]() func bookmark(item: T) { items.append(item) } func unbookmark(item: T) { guard let index = items.firstIndex(where: { $0.itemId == item.itemId }) else { return } items.remove(at: index) } func isBookmark(item: T) -> Bool { return items.contains(where: { $0.itemId == item.itemId }) } } import EasyStash final class StoreContainer { var food: Store<Food> static var shared = StoreContainer() let storage = try!...

October 14, 2019 · 1 min · Khoa Pham