How to use function builder in Swift 5.1
Issue #361
1 | protocol Task {} |
1 | run { |
Issue #361
1 | protocol Task {} |
1 | run { |
Issue #360
Given a streaming service
1 | service Server { |
To get a response list in Swift, we need to do observe stream, which is a subclass of ClientCallServerStreaming
1 | func getUsers(roomId: String, completion: @escaping (Result<[User], Error>) -> Void) { |
This can get repetitive very fast. To avoid the duplication, we can make a generic function
1 | import SwiftGRPC |
Since swift-grpc generates very concrete structs, we need to use generic. The difference is the Streaming
class and Response
struct
1 | func getUsers(roomId: String, completion: @escaping (Result<[User], Error>) -> Void) { |
1 | import SwiftGRPC |
Issue #359
app/build.gradle
1 | implementation "org.koin:koin-core:$Version.koin" |
MyApplication.kt
1 | import android.app.Application |
MyFragment.kt
1 | import org.koin.androidx.viewmodel.ext.android.viewModel |
Issue #358
app/build.gradle
1 | implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0-alpha01" |
1 | import androidx.lifecycle.ViewModel |
https://developer.android.com/topic/libraries/architecture/coroutines
The liveData building block serves as a structured concurrency primitive between coroutines and LiveData. The code block starts executing when LiveData becomes active and is automatically canceled after a configurable timeout when the LiveData becomes inactive.
https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/lifecycle/lifecycle-livedata-ktx/src/main/java/androidx/lifecycle/CoroutineLiveData.kt
https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/lifecycle/lifecycle-livedata-ktx/src/main/java/androidx/lifecycle/FlowLiveData.kt
CoroutineLiveData.kt
1 |
|
a LiveData that tries to load the
User
from local cache first and then tries from the server and also yields the updated value
1 | val user = liveData { |
Issue #357
generic/Adapter.kt
1 | package com.onmyway133.generic |
hero/HeroAdapter.kt
1 | package com.onmyway133.hero |
May run into https://stackoverflow.com/questions/49512629/default-interface-methods-are-only-supported-starting-with-android-n
app/build.gradle
1 | android { |
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
1 | final class StripeHandler { |
https://stripe.com/docs/payments/payment-intents/creating-payment-intents
When using automatic confirmation, create the PaymentIntent at the beginning of the checkout process. When using manual confirmation, create the PaymentIntent after collecting payment information from the customer using Elements or our iOS and Android SDKs. For a detailed comparison on the automatic and manual confirmation flows, see accepting one-time payments.
Pass the confirmed Payment Intent client secret from the previous step to STPPaymentHandler handleNextActionForPayment. If the customer must perform 3D Secure authentication to complete the payment, STPPaymentHandler presents view controllers using the STPAuthenticationContext passed in and walks them through that process. See Supporting 3D Secure Authentication on iOS to learn more.
1 | MyAPIClient.createAndConfirmPaymentIntent(paymentMethodId: paymentMethodId) { result in |
There is Setup intents https://stripe.com/docs/payments/cards/reusing-cards#saving-cards-without-payment for saving cards
Use the Setup Intents API to authenticate a customer’s card without making an initial payment. This flow works best for businesses that want to onboard customers without charging them right away:
Pass the STPSetupIntentParams object to the confirmSetupIntent method on a STPPaymentHandler sharedManager. If the customer must perform additional steps to complete the payment, such as authentication, STPPaymentHandler presents view controllers using the STPAuthenticationContext passed in and walks them through that process. See Supporting 3D Secure Authentication on iOS to learn more.
1 | let setupIntentParams = STPSetupIntentParams(clientSecret: clientSecret) |
In STPPaymentHandler.m
1 | - (BOOL)_canPresentWithAuthenticationContext:(id<STPAuthenticationContext>)authenticationContext { |
STPSetupIntentConfirmParams.useStripeSDK
A boolean number to indicate whether you intend to use the Stripe SDK’s functionality to handle any SetupIntent next actions.
If set to false, STPSetupIntent.nextAction will only ever contain a redirect url that can be opened in a webview or mobile browser.
When set to true, the nextAction may contain information that the Stripe SDK can use to perform native authentication within your app.
1 | let setupIntentParams = STPSetupIntentConfirmParams(clientSecret: clientSecret) |
Issue #355
1 | final class CurrencyFormatter { |
1 | class CurrencyFormatterTests: XCTestCase { |
Issue #354
In Construction, we have a build
method to apply closure to inout
struct.
We can explicitly define that with withValue
1 | func withValue<T>(_ value: T, closure: (inout T) -> Void) -> T { |
So we can modify Protobuf structs easily
1 | user.book = withValue(Book()) { |
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
1 | - (STPFormTextField *)currentFirstResponderField { |
Then it calls [self.numberField becomeFirstResponder];
is validation on number fails
1 | typedef void (^STPLayoutAnimationCompletionBlock)(BOOL completed); |
Be aware to use isUserInteractionEnabled
on STPPaymentCardTextField
as that can resign first responder when set to true
and become first responder when set to false
Issue #352
For a normal electron app created with npm init
, we can use all features of ES6, but not the JSX syntax for React. We can use just Babel to transpile JSX, as used in IconGenerator
.babelrc
1 | { |
And in package.json
, call babel to transpile src
to dist
1 | "main": "dist/main.js", |
Remember to use dist/main.js
as our starting point, and in index.html, specify ./dist/renderer.js
1 | <body> |
Issue #351
From Gradle tips and recipes, Configure project-wide properties
For projects that include multiple modules, it might be useful to define properties at the project level and share them across all modules. You can do this by adding extra properties to the ext block in the top-level build.gradle file.
1 | ext { |
Versions are used mostly in dependencies
block so having them defined in global ext
is not quite right. We can use def
to define variables
1 | dependencies { |
For better namespacing, we can use a class
1 | class Version { |
Issue #350
Read Authenticate with Firebase on iOS using a Phone Number
Info.plist
1 | <key>FirebaseAppDelegateProxyEnabled</key> |
Enable Capability -> Background mode -> Remote notification
AppDelegate.swift
1 | import Firebase |
Firebase push message looks like
1 | ▿ 1 element |
To disable captcha during testing
1 | Auth.auth().settings?.isAppVerificationDisabledForTesting = true |
Issue #349
build.gradle
1 | dependencies { |
app/build.gradle
1 | apply plugin: 'androidx.navigation.safeargs' |
main_activity.xml
CoordinatorLayout
and ToolBar
layout_gravity
for NavigationView
1 | <?xml version="1.0" encoding="utf-8"?> |
navigation/navigation_graph.xml
1 |
|
menu/drawer_menu.xml
1 |
|
MainActivity.kotlin
AppBarConfiguration
to define multiple top level destinations1 | package com.onmyway133.whatsupintech |
NavigationUI also provides helpers for tying destinations to menu-driven UI components. NavigationUI contains a helper method, onNavDestinationSelected(), which takes a MenuItem along with the NavController that hosts the associated destination. If the id of the MenuItem matches the id of the destination, the NavController can then navigate to that destination.
The drawer icon is displayed on all top-level destinations that use a DrawerLayout. Top-level destinations are the root-level destinations of your app. They do not display an Up button in the app bar.
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
1 | import UIKit |
DigitHandler.swift
1 | final class DigitHandler: NSObject { |
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.
For flipping animation, we use UIView.transition
with showHideTransitionViews
BackCard.swift
1 | import UIKit |
FrontCard.swift
1 | import UIKit |
CardView.swift
1 | import UIKit |
CardHandler.swift
1 | import Foundation |
String+Extension.swift
1 | extension String { |
Updated at 2020-07-12 08:43:21
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. If a title is not specified for a state, the default behavior is to use the title associated with the normal state. If the value for normal is not set, then the property defaults to a system value.
1 | UIView.performWithoutAnimation { |
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.
When the bounds change for a view controller’s view, the view adjusts the positions of its subviews and then the system calls this method. However, this method being called does not indicate that the individual layouts of the view’s subviews have been adjusted. Each subview is responsible for adjusting its own layout.
Your view controller can override this method to make changes after the view lays out its subviews. The default implementation of this method does nothing.
Issue #343
Xcode 10.3 with iOS 13
1 | 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
1 | 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 |
1 | /Applications/Xcode/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport |
Issue #342
Install electron as dev npm install electron --save-dev
Update electron-packager npm install electron-packager@latest --save-dev
Use no space in app name
1 | npx electron-packager . "MyApp" --app-bundle-id=com.onmyway133.MyApp --helper-bundle-id=com.onmyway133.MyApp.helper --app-version=1.4.0 --build-version=1.0.100 --platform=mas --arch=x64 --icon=Icon/Icon.icns --overwrite |
electron-osx-sign searches your keychain for the first signing certificates that it can locate. If you have multiple certificates then it may not know which cert you want to use for signing and you need to explicitly provide the name:
1 | electron-osx-sign "My App-mas-x64/My App.app" --identity="3rd Party Mac Developer Application: My Company, Inc (ABCDEFG1234)" --verbose |
Read README https://github.com/electron/electron-osx-sign
For distribution in the Mac App Store: Have the provisioning profile for distribution placed in the current working directory and the signing identity installed in the default keychain.
On developer.apple.com, create Mac App Distribution certificate. Make sure when we download in Keychain Access, it has associated private key
1 | /Applications/Xcode.app/Contents/Applications/Application\ Loader.app/Contents/itms/bin/iTMSTransporter -m upload -assetFile MyApp/MyApp.pkg -u onmyway133@gmail.com -p mypassword |
Use Using app-specific passwords
ERROR ITMS-90261: “Bad CFBundleExecutable. Cannot find executable file that matches the value of CFBundleExecutable in the nested bundle MyApp [com.onmyway133.MyApp.pkg/Payload/MyApp.app/Contents/Frameworks/MyApp (GPU).app] property list file.”
https://github.com/electron/electron-packager/issues?utf8=%E2%9C%93&q=helper
Try electron 5.0.0 npm install electron@5.0.0 --save-dev
https://stackoverflow.com/questions/35008347/electron-close-w-x-vs-right-click-dock-and-quit
1 | function createMenu() { |
1 | npm install -g electron-osx-sign@latest |
1 | ran xattr -cr * |
Perhaps you accidentally packaged the previous generated app bundle into your newly packaged app?
Remove dist
folder generated by electron-builder
Issue #340
https://nshipster.com/formatter/#datecomponentsformatter
Results in no padding 0
1 | func format(second: TimeInterval) -> String? { |
1 | func format(minute: Int) -> String { |
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.
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. Or, you can create your own caches for different purposes, setting distinct caches on your URLSessionConfiguration objects.
Issue #338
Gradle uses Groovy and it has ext
, also known as ExtraPropertiesExtension
Additional, ad-hoc, properties for Gradle domain objects.
Extra properties extensions allow new properties to be added to existing domain objects. They act like maps, allowing the storage of arbitrary key/value pairs. All ExtensionAware Gradle domain objects intrinsically have an extension named “ext” of this type.
1 | project.ext { |
In root build.gradle
, ext
adds extra property to rootProject
object. There we can access rootProject.ext
or just ext
1 | ext { |
In module app/build.gradle
, ext
adds extra property to project
object. There we can access project.ext
or just ext
1 | ext { |
Issue #337
Normally we just present from any UIViewController
in any UINavigationController
in UITabBarController
and it will present over tabbar
1 | present(detailViewController, animated: true, completion: nil) |
If we have animation with UIViewPropertyAnimator
, then we can implement UIViewControllerAnimatedTransitioning and interruptibleAnimator(using:)
The methods in this protocol let you define an animator object, which creates the animations for transitioning a view controller on or off screen in a fixed amount of time. The animations you create using this protocol must not be interactive. To create interactive transitions, you must combine your animator object with another object that controls the timing of your animations.
Implement this method when you want to perform your transitions using an interruptible animator object, such as a UIViewPropertyAnimator object. You must return the same animator object for the duration of the transition.
For more fine-grained control, we can have UIPresentationController
From the time a view controller is presented until the time it is dismissed, UIKit uses a presentation controller to manage various aspects of the presentation process for that view controller. The presentation controller can add its own animations on top of those provided by animator objects, it can respond to size changes, and it can manage other aspects of how the view controller is presented onscreen.
A lazy approach is to present without animation and do animation after
1 | present(detailViewController, animated: false, completion: { |
If we don’t want to involve UIViewController
then we can work on UIView
level. This way we can animate hiding tab bar. Any UIViewController
within UITabBarController
has tabBarController
1 | let animator = UIViewPropertyAnimator() |
Issue #336
Use NSPopUpButton
var pullsDown: Bool
A Boolean value indicating whether the button displays a pull-down or pop-up menu.
func addItem(withTitle: String)
Adds an item with the specified title to the end of the menu.
Should disable pullsDown
if we want to set title automatically and not scale button for title
Issue #335
This is useful when we want to get the first meaningful line in a big paragraph
1 | let scanner = Scanner(string: text) |
Issue #334
NSSecureCoding has been around since iOS 6 and has had some API changes in iOS 12
A protocol that enables encoding and decoding in a manner that is robust against object substitution attacks.
https://developer.apple.com/documentation/foundation/nscoder/2292924-decodeobject
If the coder responds true to requiresSecureCoding, then the coder calls failWithError(_:) in either of the following cases:
The class indicated by cls doesn’t implement NSSecureCoding.
The unarchived class doesn’t match cls, nor do any of its superclasses.
If the coder doesn’t require secure coding, it ignores the cls parameter and does not check the decoded object.
The class must subclass from NSObject
and conform to NSSecureCoding
1 | class Note: NSObject, NSSecureCoding { |
First, we need to serialize to Data, then use EasyStash for easy persistency
1 | do { |
Then we can use unarchiveTopLevelObjectWithData
to unarchive array
1 | do { |
Note that for UUID, NSCoding seems to convert to UUID instead of String
1 | let id = aDecoder.decodeObject( |
Issue #333
In a traditional pager with many pages of content, and a bottom navigation with previous and next button. Each page may have different content, and depending on each state, may block the next button.
The state of next button should state in real time depending on state in each page content, and when user moves back and forth between pages, the state of next button should be reflected as well.
We might have
1 | extension ViewController: BottomNavigationDelegate { |
The indirect communications between each page, bottom navigation and ViewController get complicated and out of hands very quickly.
This is a perfect problem for Rx to solve. If we look closely, the state of next button is a derivative of current index, how many items selected in preferences, valid form and agreement status.
1 | class BottomNavigation { |
Issue #332
From moveItem(at:to:)
Moves an item from one location to another in the collection view.
After rearranging items in your data source object, use this method to synchronize those changes with the collection view. Calling this method lets the collection view know that it must update its internal data structures and possibly update its visual appearance. You can move the item to a different section or to a new location in the same section. The collection view updates the layout as needed to account for the move, animating cells into position in response.
When inserting or deleting multiple sections and items, you can animate all of your changes at once using the performBatchUpdates(_:completionHandler:) method.
1 | notes.swapAt(index, 0) |
There may be unknown reasons or bug that make other cells stay in incorrect state. The fix is to reload the rest cells
1 | let set = Set((1..<notes.count).map({ $0.toIndexPath() })) |
Issue #331
From NSSegmentedControl
The features of a segmented control include the following:
A segment can have an image, text (label), menu, tooltip, and tag.
A segmented control can contain images or text, but not both.
1 | let languageMenu = NSMenu(title: "") |
Issue #330
When adding NSTextView
in xib, we see it is embedded under NSClipView
. But if we try to use NSClipView
to replicate what’s in the xib, it does not scroll.
To make it work, we can follow Putting an NSTextView Object in an NSScrollView and How to make scrollable vertical NSStackView to make our ScrollableInput
For easy Auto Layout, we use Anchors for UIScrollView
.
Things worth mentioned for vertical scrolling
1 | textContainer.heightTracksTextView = false |
1 | class ScrollableInput: NSView { |
NSTextView.scrollableTextView()
Updated at 2020-12-31 05:43:41