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 ยท Khoa Pham

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 ยท Khoa Pham

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 ยท Khoa Pham

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 ยท Khoa Pham

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 ยท Khoa Pham

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 ยท Khoa Pham

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 ยท Khoa Pham

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 ยท Khoa Pham

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 ยท Khoa Pham

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 ยท Khoa Pham

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 ยท Khoa Pham

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 ยท Khoa Pham

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 ยท Khoa Pham

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 ยท Khoa Pham

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 ยท Khoa Pham

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 ยท Khoa Pham

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 ยท Khoa Pham

How to use addSubview in iOS

Issue #344 addSubview can trigger viewDidLayoutSubviews, so be careful to just do layout stuff in viewDidLayoutSubviews This method establishes a strong reference to view and sets its next responder to the receiver, which is its new superview. Views can have only one superview. If view already has a superview and that view is not the receiver, this method removes the previous superview before making the receiver its new superview....

August 1, 2019 ยท 1 min ยท Khoa Pham

How to run app on beta iOS devices

Issue #343 Xcode 10.3 with iOS 13 sudo ln -s /Applications/Xcode-beta.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/13.0 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport Xcode 10.3 with iOS 13.1 beta 2 sudo ln -s /Applications/Xcode-beta.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/13.0/ /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/13.1 Use DeviceSupport Download and unzip Device support https://github.com/iGhibli/iOS-DeviceSupport /Applications/Xcode/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport

August 1, 2019 ยท 1 min ยท Khoa Pham

How to cache URLSession response

Issue #339 For simple cases, we donโ€™t need to. Letโ€™s use urlCache The URL cache for providing cached responses to requests within the session. Accessing Cached Data The URL Loading System caches responses both in memory and on disk, improving performance and reducing network traffic. The URLCache class is used for caching responses from network resources. Your app can directly access the shared cache instance by using the shared property of URLCache....

July 3, 2019 ยท 1 min ยท Khoa Pham