How to check app going to background in SwiftUI

Issue #746 From iOS 13, the default is to support multiple scene, so the the old UIApplicationDelegate lifecycle does not work. Double check your Info.plist for UIApplicationSceneManifest key <key>UIApplicationSceneManifest</key> <dict> <key>UIApplicationSupportsMultipleScenes</key> <true/> </dict> One way to be notified about application life cycle is to use UIApplicationDelegateAdaptor and via NotificationCenter import SwiftUI import UIKit import FontAwesomeSwiftUI final class AppDelegate: NSObject, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { FontAwesome....

January 7, 2021 路 1 min 路 Khoa Pham

How to make Auto Layout more convenient in iOS

Issue #742 Auto Layout has been around since macOS 10.7 and iOS 6.0 as a nicer way to do layouts over the old resizing masks. Besides some rare cases when we need to manually specify origins and sizes, Auto Layout is the preferred way to do declarative layouts whether we choose to do UI in code or Storyboard. The Auto Layout APIs have some improvements over the years, there are also some sugar libraries that add easy syntaxes, so more choices for developers....

January 5, 2021 路 18 min 路 Khoa Pham

How to convert from paid to free with IAP

Issue #703 What is receipt Read When to refresh a receipt vs restore purchases in iOS? From iOS 7, every app downloaded from the store has a receipt (for downloading/buying the app) at appStoreReceiptURL. When users purchases something via In App Purchase, the content at appStoreReceiptURL is updated with purchases information. Most of the cases, you just need to refresh the receipt (at appStoreReceiptURL) so that you know which transactions users have made....

November 30, 2020 路 9 min 路 Khoa Pham

How to avoid multiple match elements in UITests from iOS 13

Issue #691 Supposed we want to present a ViewController, and there exist both UIToolbar in both the presenting and presented view controllers. From iOS 13, the model style is not full screen and interactive. From UITests perspective there are 2 UIToolbar, we need to specify the correct one to avoid multiple match errors let editButton = app.toolbars["EditArticle.Toolbar"].buttons["Edit"] Updated at 2020-11-04 10:02:29

November 4, 2020 路 1 min 路 Khoa Pham

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 路 Khoa Pham

How to draw arc corner using Bezier Path

Issue #673 To draw rounded 2 corners at top left and top right, let鈥檚 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 路 Khoa Pham

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鈥檚 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 = 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 路 Khoa Pham

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 路 Khoa Pham

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 路 Khoa Pham

How to use background in iOS

Issue #631 beginBackgroundTask When your app moves to the background, the system calls your app delegate鈥檚 applicationDidEnterBackground(_:) method. That method has five seconds to perform any tasks and return. Shortly after that method returns, the system puts your app into the suspended state. For most apps, five seconds is enough to perform any crucial tasks, but if you need more time, you can ask UIKit to extend your app鈥檚 runtime....

March 25, 2020 路 2 min 路 Khoa Pham

How to mask with UILabel

Issue #603 Need to set correct frame for mask layer or UILabel, as it is relative to the coordinate of the view to be masked let aView = UIView(frame: .init(x: 100, y: 110, width: 200, height: 100)) let textLayer = CATextLayer() textLayer.foregroundColor = UIColor.white.cgColor textLayer.string = "Hello world" textLayer.font = UIFont.preferredFont(forTextStyle: .largeTitle) textLayer.frame = aView.bounds aView.layer.mask = textLayer Use sizeToFit to ensure frame for UILabel let label = UILabel() label.frame.origin = CGPoint(x: 80, y: 80) label....

February 13, 2020 路 1 min 路 Khoa Pham

How to sync multiple CAAnimation

Issue #600 Use same CACurrentMediaTime final class AnimationSyncer { static let now = CACurrentMediaTime() func makeAnimation() -> CABasicAnimation { let animation = CABasicAnimation(keyPath: "opacity") animation.fillMode = .forwards animation.fromValue = 0 animation.toValue = 1 animation.repeatCount = .infinity animation.duration = 2 animation.beginTime = animation.autoreverses = true animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut) return animation } }

February 11, 2020 路 1 min 路 Khoa Pham

How to build SwiftUI style UICollectionView data source in Swift

Issue #598 It鈥檚 hard to see any iOS app which don鈥檛 use UITableView or UICollectionView, as they are the basic and important foundation to represent data. UICollectionView is very basic to use, yet a bit tedious for common use cases, but if we abstract over it, then it becomes super hard to customize. Every app is unique, and any attempt to wrap around UICollectionView will fail horribly. A sensable approach for a good abstraction is to make it super easy for normal cases, and easy to customize for advanced scenarios....

February 9, 2020 路 4 min 路 Khoa Pham

How to weak link Combine in macOS 10.14 and iOS 12

Issue #593 #if canImport(Combine) is not enough, need to specify in Other Linker Flags OTHER_LDFLAGS = -weak_framework Combine Read more

February 2, 2020 路 1 min 路 Khoa Pham

How to test drag and drop in UITests

Issue #583 In UITests, we can use press from XCUIElement to test drag and drop let fromCat = app.buttons["cat1"].firstMatch let toCat = app.buttons["cat2"] let fromCoordinate = fromCat.coordinate(withNormalizedOffset: CGVector(dx: 0, dy: 0)) let toCoordinate = toCat.coordinate(withNormalizedOffset: CGVector(dx: 0, dy: -0.5)) 1, thenDragTo: toCoordinate) and then take screenshot let screenshot = XCUIScreen.main.screenshot() let attachment = XCTAttachment(screenshot: screenshot) attachment.lifetime = .keepAlways = name add(attachment) Screenshot capturing happens after the action, so it may be too late....

January 23, 2020 路 1 min 路 Khoa Pham

How to set corner radius in iOS

Issue #582 Use View Debugging Run on device, Xcode -> Debug -> View debugging -> Rendering -> Color blended layer On Simulator -> Debug -> Color Blended Layer Corner radius Okay. Talked to a Core Animation engineer again: cornerRadius was deliberately improved in Metal so it could be used everywhere. Using a bitmap is WAY heavier in terms of memory and performance. CALayer maskLayer is still heavy....

January 22, 2020 路 4 min 路 Khoa Pham

How to work with SceneDelegate in iOS 12

Issue #580 Events open url Implement scene(_:openURLContexts:) in your scene delegate. If the URL launches your app, you will get scene(_:willConnectTo:options:) instead and it鈥檚 in the options. life cycle Here鈥檚 how it works: If you have an 鈥淎pplication Scene Manifest鈥 in your Info.plist and your app delegate has a configurationForConnectingSceneSession method, the UIApplication won鈥檛 send background and foreground lifecycle messages to your app delegate....

January 20, 2020 路 2 min 路 Khoa Pham

How to make rotation in same direction in iOS

Issue #571 From CGFloat.pi / 2 to -CGFloat.pi / 2 + epsilon

January 6, 2020 路 1 min 路 Khoa Pham

How to get updated safeAreaInsets in iOS

Issue #570 Use viewSafeArea @available(iOS 11.0, *) override func viewSafeAreaInsetsDidChange() { super.viewSafeAreaInsetsDidChange() self.collectionView.reloadData() } Use view.safeAreaLayoutGuide.layoutFrame For the view controller鈥檚 root view, the insets account for the status bar, other visible bars, and any additional insets that you specified using the additionalSafeAreaInsets property of your view controller. For other views in the view hierarchy, the insets reflect only the portion of the view that is covered. For example, if a view is entirely within the safe area of its superview, the edge insets in this property are 0....

January 6, 2020 路 2 min 路 Khoa Pham

How to disable implicit decoration view animation in UICollectionView

Issue #569 From documentation This method is called after the prepare(forCollectionViewUpdates:) method and before the finalizeCollectionViewUpdates() method for any decoration views that are about to be inserted. Your implementation should return the layout information that describes the initial position and state of the view. The collection view uses this information as the starting point for any animations. (The end point of the animation is the view鈥檚 new location in the collection view....

January 6, 2020 路 1 min 路 Khoa Pham