How to choose Firebase vs Google Analytics

Issue #387 Google Analytics is shutting down. From Firebase Analytics console, we can choose to upgrade to Google Analytics, no code change is needed. https://support.google.com/firebase/answer/9167112?hl=en In October 2019, we will start to sunset Google Analytics mobile-apps reporting based on the Google Analytics Services SDKs for Android and iOS. https://firebase.googleblog.com/2019/07/firebase-google-analytics-upgrade.html Thanks to our continued partnership with Google Analytics, you can now upgrade your Firebase projects to the next generation of app analytics!...

September 3, 2019 · 1 min · 139 words · Khoa

How to use lessThan and greaterThan in Auto Layout in iOS

Issue #386 When it comes to right and bottom side, we should use negative values, and use lessThan, as it means less than a negative value

September 3, 2019 · 1 min · 26 words · Khoa

How to check app running on jailbreak iOS device

Issue #385 From https://github.com/OneSignal/OneSignal-iOS-SDK/blob/master/iOS_SDK/OneSignalSDK/Source/OneSignalJailbreakDetection.m + (BOOL)isJailbroken { #if !(TARGET_IPHONE_SIMULATOR) FILE *file = fopen("/Applications/Cydia.app", "r"); if (file) { fclose(file); return YES; } file = fopen("/Library/MobileSubstrate/MobileSubstrate.dylib", "r"); if (file) { fclose(file); return YES; } file = fopen("/bin/bash", "r"); if (file) { fclose(file); return YES; } file = fopen("/usr/sbin/sshd", "r"); if (file) { fclose(file); return YES; } file = fopen("/etc/apt", "r"); if (file) { fclose(file); return YES; } file = fopen("/usr/bin/ssh", "r"); if (file) { fclose(file); return YES; } NSFileManager *fileManager = [NSFileManager defaultManager]; if ([fileManager fileExistsAtPath:@"/Applications/Cydia....

September 2, 2019 · 1 min · 194 words · Khoa

How to manage OneSignal push notification in iOS

Issue #377 OneSignal is an alternative for Parse for push notifications but the sdk has many extra stuff and assumptions and lots of swizzling. We can just use Rest to make API calls. From https://github.com/onmyway133/Dust Every official push notification SDK can do many things Register device token. This is crucial for the notification to get from your backend -> APNS -> device Manage player id, user id, arn, …This is used to associate with device token Manager tag, topic, subscription, segments, …This is used to group a set of device tokens Do swizzling, update your application badge number, change your user notification settings, … without your knowing about that Some other fancy stuffs Dust does only one thing, which is push notification handling....

August 27, 2019 · 4 min · 784 words · Khoa

How to do throttle and debounce using DispatchWorkItem in Swift

Issue #376 https://github.com/onmyway133/Omnia/blob/master/Sources/Shared/Debouncer.swift import Foundation public class Debouncer { private let delay: TimeInterval private var workItem: DispatchWorkItem? public init(delay: TimeInterval) { self.delay = delay } /// Trigger the action after some delay public func run(action: @escaping () -> Void) { workItem?.cancel() workItem = DispatchWorkItem(block: action) DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute: workItem!) } } import XCTest class DebouncerTests: XCTestCase { func testDebounce() { let expectation = self.expectation(description: #function) let debouncer = Debouncer(delay: 0....

August 27, 2019 · 1 min · 117 words · Khoa

How to simplify UIApplication life cycle observation in iOS

Issue #375 final class LifecyclerHandler { private var observer: AnyObject! var action: (() -> Void)? private let debouncer = Debouncer(delay: 1.0) func setup() { observer = NotificationCenter.default.addObserver( forName: UIApplication.didBecomeActiveNotification, object: nil, queue: .main, using: { [weak self] _ in self?.debouncer.run { self?.action?() } }) } } private let lifecycleHandler = LifecyclerHandler() override func viewDidLoad() { super.viewDidLoad() lifecycleHandler.action = { Deps.userHandler.refreshToken() } lifecycleHandler.setup() }

August 27, 2019 · 1 min · 63 words · Khoa

How to do UITests with Google Maps on iOS

Issue #374 Interact with GMSMapView Add accessibilityIdentifier to the parent view of GMSMapView. Setting directly onto GMSMapView has no effect accessibilityIdentifier = "MapView" let map = app.otherElements.matching(identifier: "MapView").element(boundBy: 0) map.pinch(withScale: 2, velocity: 1) map.rotate(CGFloat.pi/3, withVelocity: 1.0) map.swipeLeft() map.swipeRight() map.swipeDown() map.swipeDown() Interact with GMSMarker (1st try) Need to enable accessibility mapView.accessibilityElementsHidden = false Can’t use pinch to zoom out with UITests, so need to mock location !!! map().pinch(withScale: 0.05, velocity: -1) Need to use gpx to mock to preferred location...

August 27, 2019 · 2 min · 242 words · Khoa

Make to make rounded background UIButton in iOS

Issue #373 UIButton.contentEdgeInsets does not play well with Auto Layout, we need to use intrinsicContentSize final class InsetButton: UIButton { required init(text: String) { super.init(frame: .zero) titleLabel?.textColor = .white setTitle(text, for: .normal) layer.cornerRadius = 15 layer.masksToBounds = true backgroundColor = .black isUserInteractionEnabled = false } required init?(coder aDecoder: NSCoder) { fatalError() } override var intrinsicContentSize: CGSize { let size = super.intrinsicContentSize return CGSize(width: size.width + 24, height: size.height) } }

August 27, 2019 · 1 min · 70 words · Khoa

How to make scrolling UIScrollView with Auto Layout in iOS

Issue #371 Scrolling UIScrollView is used in common scenarios like steps, onboarding. From iOS 11, UIScrollView has contentLayoutGuide and frameLayoutGuide Docs https://developer.apple.com/documentation/uikit/uiscrollview/2865870-contentlayoutguide Use this layout guide when you want to create Auto Layout constraints related to the content area of a scroll view. https://developer.apple.com/documentation/uikit/uiscrollview/2865772-framelayoutguide Use this layout guide when you want to create Auto Layout constraints that explicitly involve the frame rectangle of the scroll view itself, as opposed to its content rectangle....

August 26, 2019 · 2 min · 316 words · Khoa

How to fix Auto Layout issues in iOS

Issue #369 UITemporaryLayoutHeight and UITemporaryLayoutWidth Demystify warnings with https://www.wtfautolayout.com/ Reduce priority Use Auto Layout directly instead of using manual frame layout, specially for scrolling pager NSAutoresizingMaskLayoutConstraint Check that a view ACTUALLY has translatesAutoresizingMaskIntoConstraints set to false UISV-spacing, UISV-distributing Check UIStackView Set stackview.alignment = .center if you see UIStackView trying to set same trailing or leading edges for its subviews Reduce priority if there’s edge constraints break from subviews to UIStackView Intrinsic size between UIImageView and UILabel When constraint to each other, can cause UILabel to disappear Reduce compression resistance imageView....

August 23, 2019 · 1 min · 102 words · Khoa

How to simplify anchor with NSLayoutConstraint in iOS

Issue #368 See https://github.com/onmyway133/Omnia/blob/master/Sources/iOS/NSLayoutConstraint.swift extension NSLayoutConstraint { /// Disable auto resizing mask and activate constraints /// /// - Parameter constraints: constraints to activate static func on(_ constraints: [NSLayoutConstraint]) { constraints.forEach { ($0.firstItem as? UIView)?.translatesAutoresizingMaskIntoConstraints = false $0.isActive = true } } static func on(_ constraintsArray: [[NSLayoutConstraint]]) { let constraints = constraintsArray.flatMap({ $0 }) NSLayoutConstraint.on(constraints) } func priority(_ value: Float) -> NSLayoutConstraint { priority = UILayoutPriority(value) return self } } extension Array where Element == NSLayoutConstraint { func priority(_ value: Float) -> [NSLayoutConstraint] { forEach { $0....

August 23, 2019 · 2 min · 249 words · Khoa

How to handle link clicked in WKWebView in iOS

Issue #365 import WebKit import SafariServices final class WebViewHandler: NSObject, WKNavigationDelegate { var show: ((UIViewController) -> Void)? let supportedSchemes = ["http", "https"] func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { defer { decisionHandler(.allow) } guard navigationAction.navigationType == .linkActivated, let url = navigationAction.request.url, let scheme = url.scheme, supportedSchemes.contains(scheme) else { return } let controller = SFSafariViewController(url: url) show?(controller) } }

August 21, 2019 · 1 min · 64 words · Khoa

How to use AppFlowController in iOS

Issue #364 AppFlowController.swift import UIKit import GoogleMaps import Stripe final class AppFlowController: UIViewController { private lazy var window = UIWindow(frame: UIScreen.main.bounds) func configure() { GMSServices.provideAPIKey(Constant.googleMapsApiKey) STPPaymentConfiguration.shared().publishableKey = Constant.stripeKey } func start() { if Deps.onboardingHandler.hasOnboarded { startMain() } else { startOnboarding() } window.makeKeyAndVisible() } func startOnboarding() { let controller = OnboardingController() controller.delegate = self window.rootViewController = controller } func startMain() { let controller = MainFlowController() window.rootViewController = controller controller.start() } } extension AppFlowController: OnboardingControllerDelegate { func onboardingControllerDidFinish(_ controller: OnboardingController) { Deps....

August 20, 2019 · 1 min · 116 words · Khoa

How to declare UIGestureRecognizer in iOS

Issue #362 let tapGR = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:))) @objc private func handleTap(_ gr: UITapGestureRecognizer) { didTouch?() } We need to use lazy instead of let for gesture to work lazy var tapGR = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))

August 19, 2019 · 1 min · 38 words · Khoa

How to use Payment Intent and Setup Intents with Stripe in iOS

Issue #356 StripeHandler.swift From Stripe 16.0.0 https://github.com/stripe/stripe-ios/blob/master/CHANGELOG.md#1600-2019-07-18 Migrates STPPaymentCardTextField.cardParams property type from STPCardParams to STPPaymentMethodCardParams final class StripeHandler { func createPaymentMethod( textField: STPPaymentCardTextField, completion: @escaping (Result<STPPaymentMethod, Error>) -> Void) { let paymentMethodParams = STPPaymentMethodParams( card: textField.cardParams, billingDetails: nil, metadata: nil ) STPAPIClient.shared().createPaymentMethod( with: paymentMethodParams, completion: { paymentMethod, error in DispatchQueue.main.async { if let paymentMethod = paymentMethod { completion(.success(paymentMethod)) } else { completion(.failure(error ?? AppError.request)) } } }) STPAPIClient.shared().createPaymentMethod( with: paymentMethodParams, completion: { paymentMethod, error in DispatchQueue....

August 14, 2019 · 4 min · 757 words · Khoa

How to not resign first responder for UITextField in iOS

Issue #353 When using STPPaymentCardTextField from stripe-ios, the default behavior is when we touch outside to dismiss keyboard, it checks and focus on number text field is it is invalid STPPaymentCardTextField.m - (STPFormTextField *)currentFirstResponderField { for (STPFormTextField *textField in [self allFields]) { if ([textField isFirstResponder]) { return textField; } } return nil; } - (BOOL)canResignFirstResponder { return [self.currentFirstResponderField canResignFirstResponder]; } - (BOOL)resignFirstResponder { [super resignFirstResponder]; BOOL success = [self.currentFirstResponderField resignFirstResponder]; [self layoutViewsToFocusField:nil animated:YES completion:nil]; [self updateImageForFieldType:STPCardFieldTypeNumber]; return success; } Then it calls [self....

August 13, 2019 · 1 min · 149 words · Khoa

How to use Firebase PhoneAuth in iOS

Issue #350 Read Authenticate with Firebase on iOS using a Phone Number Disable swizzling Info.plist <key>FirebaseAppDelegateProxyEnabled</key> <string>NO</string> Enable remote notification Enable Capability -> Background mode -> Remote notification AppDelegate.swift import Firebase import UIKit import FirebaseAuth @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { private let appFlowController = AppFlowController() func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { UIApplication.shared.registerForRemoteNotifications() FirebaseApp.configure() return true } // MARK: - Remote Notification func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { Auth....

August 7, 2019 · 1 min · 191 words · Khoa

How to make digit passcode input in Swift

Issue #347 Add a hidden UITextField to view hierarchy, and add UITapGestureRecognizer to activate that textField. Use padding string with limit to the number of labels, and prefix to get exactly n characters. DigitView.swift import UIKit final class DigitView: UIView { lazy var stackView: UIStackView = { let view = UIStackView() view.axis = .horizontal view.distribution = .equalSpacing return view }() private(set) var boxes: [UIView] = [] private(set) var labels: [UILabel] = [] lazy var hiddenTextField: UITextField = { let textField = UITextField() textField....

August 6, 2019 · 2 min · 323 words · Khoa

How to make credit card input UI in Swift

Issue #346 We have FrontCard that contains number and expiration date, BackCard that contains CVC. CardView is used to contain front and back sides for flipping transition. We leverage STPPaymentCardTextField from Stripe for working input fields, then CardHandler is used to parse STPPaymentCardTextField content and update our UI. For masked credit card numbers, we pad string to fit 16 characters with ● symbol, then chunk into 4 parts and zip with labels to update....

August 5, 2019 · 3 min · 632 words · Khoa

How to stop implicit animation when title change on UIButton

Issue #345 UIButton with system type has implicit animation for setTitle(_:for:) Use this method to set the title for the button. The title you specify derives its formatting from the button’s associated label object. If you set both a title and an attributed title for the button, the button prefers the use of the attributed title over this one. At a minimum, you should set the value for the normal state....

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