How to test UserDefaults in iOS

Issue #518 let userDefaults = UserDefaults(suiteName: suiteName) userDefaults.removePersistentDomain(forName: suiteName) https://developer.apple.com/documentation/foundation/userdefaults/1417339-removepersistentdomain Calling this method is equivalent to initializing a user defaults object with init(suiteName:) passing domainName, and calling the removeObject(forKey:) method on each of its keys. Read more https://www.swiftbysundell.com/articles/the-power-of-userdefaults-in-swift/ http://dscoder.com/defaults.html https://medium.com/swift-india/userdefaults-under-the-hood-457461c8d262

November 25, 2019 · 1 min · 40 words · Khoa

How to group array by property in Swift

Issue #510 Use Dictionary(grouping:by:) func groups(countries: [Country]) -> [Group] { let dictionary = Dictionary(grouping: countries, by: { String($0.name.prefix(1)) }) let groups = dictionary .map({ (key: String, value: [Country]) -> Group in return Group(initial: key, countries: value) }) .sorted(by: { $0.initial < $1.initial }) return groups }

November 16, 2019 · 1 min · 46 words · Khoa

How to map error in Combine

Issue #506 When a function expects AnyPublisher<[Book], Error> but in mock, we have Just func getBooks() -> AnyPublisher<[Book], Error> { return Just([ Book(id: "1", name: "Book 1"), Book(id: "2", name: "Book 2"), ]) .eraseToAnyPublisher() } There will be a mismatch, hence compile error Cannot convert return expression of type ‘AnyPublisher<[Book], Just.Failure>’ (aka ‘AnyPublisher<Array, Never>’) to return type ‘AnyPublisher<[Book], Error>’ The reason is because Just produces Never, not Error. The workaround is to introduce Error...

November 14, 2019 · 1 min · 107 words · Khoa

How to make Swift Package Manager package for multiple platforms

Issue #504 https://twitter.com/NeoNacho/status/1181245484867801088?s=20 There’s no way to have platform specific sources or targets today, so you’ll have to take a different approach. I would recommend wrapping all OS specific files in #if os and just having one target. For tests, you could do something similar, one test target, but conditional tests Every files are in Sources folder, so we can use platform and version checks. For example Omnia is a Swift Package Manager that supports iOS, tvOS, watchOS, macOS and Catalyst....

November 13, 2019 · 1 min · 119 words · Khoa

How to use Firebase in macOS

Issue #501 Use Catalyst Add to CocoaPods platform :ios, '13.0' target 'MyApp' do use_frameworks! pod 'FirebaseCore' pod 'Firebase/Firestore' end Troubleshooting Select a team for gRPC-C++-gRPCCertificates-Cpp FIRAnalyticsConnector: building for Mac Catalyst, but linking in object file built for iOS Simulator https://stackoverflow.com/questions/57666155/firanalyticsconnector-building-for-mac-catalyst-but-linking-in-object-file-bui The problem was related to the difference between Firebase/Core and FirebaseCore. The first is a subspec of the Firebase pod that depends on FirebaseAnalytics. The second is only the FirebaseCore pod....

November 12, 2019 · 1 min · 79 words · Khoa

How to use Firebase RemoteConfig

Issue #493 Declare in Podfile pod 'Firebase/Core' pod 'Firebase/RemoteConfig' Use RemoteConfigHandler to encapsulate logic. We introduce Key with CaseIterable and defaultValue of type NSNumber to manage default values. import Firebase import FirebaseRemoteConfig final class RemoteConfigHandler { let remoteConfig: RemoteConfig enum Key: String, CaseIterable { case interval = "fetch_interval" var defaultValue: NSNumber { switch self { case .periodicGetSalons: return NSNumber(value: 300) } } } init() { self.remoteConfig = RemoteConfig.remoteConfig() let settings = RemoteConfigSettings() settings....

November 5, 2019 · 1 min · 147 words · Khoa

How to show image picker in SwiftUI

Issue #485 The easiest way to show image picker in iOS is to use UIImagePickerController, and we can bridge that to SwiftUI via UIViewControllerRepresentable First attempt, use Environment We conform to UIViewControllerRepresentable and make a Coordinator, which is the recommended way to manage the bridging with UIViewController. There’s some built in environment property we can use, one of those is presentationMode where we can call dismiss to dismiss the modal....

November 2, 2019 · 4 min · 667 words · Khoa

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

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

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

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

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

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

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

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

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

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

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

How to declare escaping in completion in callback in Swift

Issue #464 typealias Completion = (Result<User, Error>) -> Void func validate(completion: @escaping Completion, then: (String, String, @escaping Completion) -> Void) {}

October 14, 2019 · 1 min · 21 words · Khoa

How to zoom to fit many coordinates in Google Maps in iOS

Issue #463 func zoom(location1: CLLocation, location2: CLLocation) { let bounds = GMSCoordinateBounds(coordinate: location1.coordinate, coordinate: location2.coordinate) let update = GMSCameraUpdate.fit(bounds, withPadding: 16) mapView.animate(with: update) }

October 14, 2019 · 1 min · 24 words · Khoa