How to use custom font as resource in Android

Issue #380 Downloadable fonts https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts Android 8.0 (API level 26) and Android Support Library 26 introduce support for APIs to request fonts from a provider application instead of bundling files into the APK or letting the APK download fonts. The feature is available on devices running Android API versions 14 and higher through the Support Library 26 Before Select File -> New -> Folder -> Assets Folder to create src/main/assets/fonts al myTypeface = Typeface....

August 28, 2019 路 1 min 路 Khoa Pham

How to get Hacker News top stories using parallel coroutine and Retrofit

Issue #379 interface Api { @GET("topstories.json?print=pretty") suspend fun getTopStories(): List<Int> @GET("item/{id}.json?print=pretty") suspend fun getStory(@Path("id") id: Int): Item } class Repo { fun api(): Api { return Retrofit.Builder() .baseUrl("https://hacker-news.firebaseio.com/v0/") .addConverterFactory(MoshiConverterFactory.create()) .build() .create(Api::class.java) } } class ViewModel(val repo: Repo): ViewModel() { val items = MutableLiveData<ArrayList<Item>>() suspend fun load() { try { val ids = repo.api() .getTopStories() .take(20) val items = ids.map { repo.api().getStory(it) } this.items.value = items.toCollection(ArrayList()) } catch (e: Exception) { this....

August 28, 2019 路 2 min 路 Khoa Pham

How to show generic list in Fragment in Android

Issue #378 After having a generic RecyclerView, if we want to show multiple kinds of data in Fragment, we can use generic. We may be tempted to use interface or protocol, but should prefer generic. class FeedFragment() : Fragment() { override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) val mainViewModel: MainViewModel = ViewModelProviders.of(activity!!).get(MainViewModel::class.java) mainViewModel.resId.observe(viewLifecycleOwner, Observer { when (it) { R.id.gitHub -> { handleGitHub() } R.id.hackerNews -> { handleHackerNews() } R.id.reddit -> { handleReddit() } R....

August 28, 2019 路 2 min 路 Khoa Pham

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, 鈥his is used to associate with device token Manager tag, topic, subscription, segments, 鈥his 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鈥檛 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 use Product Hunt GraphQL API with Retrofit

Issue #370 Define response model import com.squareup.moshi.Json data class Response( @field:Json(name="data") val data: ResponseData ) data class ResponseData( @field:Json(name="posts") val posts: Posts ) data class Posts( @field:Json(name="edges") val edges: List<Edge> ) data class Edge( @field:Json(name="node") val node: Item ) data class Item( @field:Json(name="id") val id: String, @field:Json(name="name") val name: String, @field:Json(name="url") val url: String, @field:Json(name="tagline") val tagline: String, @field:Json(name="featuredAt") val featuredAt: String, @field:Json(name="votesCount") val votesCount: Int, @field:Json(name="commentsCount") val commentsCount: Int, @field:Json(name="thumbnail") val thumbnail: Thumbnail ) data class Thumbnail( @field:Json(name="url") val ur: String ) Here is the query...

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鈥檚 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 get trending repos on GitHub using Retrofit

Issue #367 https://api.github.com/search/repositories?sort=stars&order=desc&q=language:javascript,java,swift,kotlin&q=created:>2019-08-21 interface Api { @GET("https://api.github.com/search/repositories") suspend fun getTrendingRepos( @Query("sort") sort: String, @Query("order") order: String, @Query("q") qs: List<String> ): Response } class Repo { fun api(): Api { return Retrofit.Builder() .baseUrl("https://api.github.com/") .addConverterFactory(MoshiConverterFactory.create()) .build() .create(Api::class.java) } } class ViewModel(val repo: Repo, val dateProvider: DateProvider): ViewModel() { val items = MutableLiveData<ArrayList<Item>>() suspend fun load() { try { val order = "desc" val sort = "star" val formatter = SimpleDateFormat("YYYY-MM-dd") val qs = listOf( "language:javascript,java,swift,kotlin", "q=created:>${formatter....

August 22, 2019 路 1 min 路 Khoa Pham

How to use Retrofit in Android

Issue #366 Code uses Retrofit 2.6.0 which has Coroutine support app/build.gradle implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0-alpha01" implementation "com.squareup.moshi:moshi:$Version.moshi" implementation "com.squareup.retrofit2:retrofit:$Version.retrofit" implementation "com.squareup.retrofit2:converter-moshi:$Version.retrofit" Api.kt import retrofit2.http.GET interface Api { @GET("api/articles") suspend fun getArticles(): List<Article> } Repo.kt import retrofit2.Retrofit import retrofit2.converter.moshi.MoshiConverterFactory class Repo { fun get(): Api { return Retrofit.Builder() .baseUrl("https://dev.to") .addConverterFactory(MoshiConverterFactory.create()) .build() .create(Api::class.java) } } ViewModel.kt import androidx.lifecycle.ViewModel import androidx.lifecycle.liveData import kotlinx.coroutines.Dispatchers class ViewModel(val repo: Repo): ViewModel() { val articles = liveData(Dispatchers.Main) { emit(repo.get().getArticles().toCollection(ArrayList())) } } Article....

August 22, 2019 路 1 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 use ViewModel and ViewModelsProviders in Android

Issue #363 ViewModels a simple example https://medium.com/androiddevelopers/viewmodels-a-simple-example-ed5ac416317e Rotating a device is one of a few configuration changes that an app can go through during its lifetime, including keyboard availability and changing the device鈥檚 language. All of these configuration changes cause the Activity to be torn down and recreated The ViewModel class is designed to hold and manage UI-related data in a life-cycle conscious way. This allows data to survive configuration changes such as screen rotations....

August 20, 2019 路 3 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 function builder in Swift 5.1

Issue #361 protocol Task {} struct Build: Task {} struct Test: Task {} @_functionBuilder public struct TaskBuilder { public static func buildBlock(_ tasks: Task...) -> [Task] { tasks } } public func run(@TaskBuilder builder: () -> [Task]) { MyManager.run(tasks: builder()) } public func run(@TaskBuilder builder: () -> Task) { MyManager.run(tasks: [builder()]) } run { Build() Test() } Read more The Swift 5.1 features that power SwiftUI鈥檚 API ...

August 18, 2019 路 1 min 路 Khoa Pham

How to simplify get GRPC streaming in Swift

Issue #360 Given a streaming service service Server { rpc GetUsers(GetUsersRequest) returns (stream GetUsersResponse); } To get a response list in Swift, we need to do observe stream, which is a subclass of ClientCallServerStreaming func getUsers(roomId: String, completion: @escaping (Result<[User], Error>) -> Void) { let request = withValue(Server_GetUsersRequest()) { $0.roomId = roomId } DispatchQueue.global().async { var users = [User]() do { var streaming = true let stream = try self.client.getUsers(request, completion: { _ in streaming = false }) while streaming { if let response = try stream....

August 16, 2019 路 2 min 路 Khoa Pham