Hi there, I’m Khoa aka onmyway133 👋

  • 🧑‍💻 I love crafting high quality and useful apps

  • 🔥 I love open source. My GitHub open source has 1.6k followers with packages that are integrated by 45k+ apps and over 3.4m+ downloads on CocoaPods.

  • ✍️ I write here on my blog and on Medium, which has over 2.4k+ followers with tons of articles and 90k+ monthly views.

  • 🖥 Follow me for sharings about Swift, SwiftUI, iOS and macOS development

How to use ViewBuilder in SwiftUI

Issue #877 Over the course of making several SwiftUI apps, I’ve discovered quite a few hidden magic of SwiftUI that are quite fun. Here are 6 interesting SwiftUI features in View Builder many don’t know are even possible 🤯 View protocol with enum Struct is not the only way to describe #SwiftUI views. Learn how you can achieve the same thing with just enum Conform primitive type to View protocol You can also conform any value type or primitive type to View protocol...

April 11, 2022 · 2 min · Khoa Pham

How to debounce TextField search in SwiftUI

Issue #876 Make a dedicate DebounceObject to debounce (or throttle). Then we can even observe with onChange on the debouncedText or just use it directly to sort public final class DebounceObject: ObservableObject { @Published var text: String = "" @Published var debouncedText: String = "" private var bag = Set<AnyCancellable>() public init(dueTime: TimeInterval = 0.5) { $text .removeDuplicates() .debounce(for: .seconds(dueTime), scheduler: DispatchQueue.main) .sink(receiveValue: { [weak self] value in self?.debouncedText = value }) ....

March 26, 2022 · 1 min · Khoa Pham

How to create document based macOS app

Issue #875 Read newDocument This method calls openUntitledDocumentAndDisplay(_:). Read openUntitledDocumentAndDisplay The default implementation of this method calls defaultType to determine the type of new document to create, calls makeUntitledDocument(ofType:) to create it, then calls addDocument(_:) to record its opening. Read defaultType The default implementation of this method returns the first Editor type declared by the CFBundleDocumentTypes array in the application’s Info.plist, or returns nil if no Editor type is declared....

March 19, 2022 · 1 min · Khoa Pham

How to add dot indicator to tab bar item in iOS

Issue #874 From iOS 13, use UITabBarAppearance and UITabBarItemAppearance let appearance = UITabBarAppearance() let itemAppearance = UITabBarItemAppearance(style: .stacked) itemAppearance.normal.badgeBackgroundColor = .clear itemAppearance.normal.badgeTextAttributes = [.foregroundColor: UIColor.red] profileViewController.tabBarItem.badgeValue = "●" Read more https://emptytheory.com/2019/12/31/using-uitabbarappearance-for-tab-bar-changes-in-ios-13/

March 15, 2022 · 1 min · Khoa Pham

How to use Multipeer Connectivity

Issue #873 Use assistant let assistant = MCAdvertiserAssistant(serviceType: "my-service, discoveryInfo: nil, session: mcSession) assistant.start() let browser = MCBrowserViewController(serviceType: "my-service", session: mcSession) browser.delegate = self present(browser, animated: true) Manual let advertiser = MCNearbyServiceAdvertiser(peer: localPeerID, discoveryInfo: nil, serviceType: self.serviceType) advertiser.startAdvertisingPeer() let browser = MCNearbyServiceBrowser(peer: localPeerID, serviceType: self.serviceType) browser.startBrowsingForPeers() Enable in Info.plist <key>NSLocalNetworkUsageDescription</key> <string>Enable local network discovery to use Peer Share</string> <key>NSBonjourServices</key> <array> <string>_my-service._tcp</string> <string>_my-service._udp</string> </array> Read more https://www.hackingwithswift.com/example-code/networking/how-to-create-a-peer-to-peer-network-using-the-multipeer-connectivity-framework https://www.toptal.com/ios/collusion-ios-multipeerconnectivity

March 15, 2022 · 1 min · Khoa Pham

How to generate Polygon wallet account in Swift

Issue #871 Use libraries https://github.com/argentlabs/web3.swift https://github.com/bswags/web3keystore import web3 import web3keystore import KeychainAccess private final class KeyStorage: EthereumKeyStorageProtocol { enum Key: String { case privateKey case phrase } private let keychain = Keychain(service: "com.example.myapp") func storePrivateKey(key: Data) throws { try keychain.set(key, key: Key.privateKey.rawValue) } func loadPrivateKey() throws -> Data { if let data = try keychain.getData(Key.privateKey.rawValue) { return data } else { throw EthereumKeyStorageError.failedToLoad } } var phrase: String? { get { try?...

February 24, 2022 · 2 min · Khoa Pham

How to learn Polygon programming

Issue #870 Building https://dev.to/dabit3/building-scalable-full-stack-apps-on-ethereum-with-polygon-2cfb Sidechain https://docs.polygon.technology/docs/home/new-to-polygon https://hackernoon.com/what-are-sidechains-and-childchains-7202cc9e5994 USDC Testnet https://mumbai.polygonscan.com/token/0x566368d78dbdec50f04b588e152de3cec0d5889f Mainnet https://polygonscan.com/token/0x2791bca1f2de4661ed88a30c99a7a9449aa84174 Faucet Testnet https://faucet.polygon.technology/

February 24, 2022 · 1 min · Khoa Pham

How to make simple Plist builder with resultBuilder in Swift

Issue #869 We can use PropertyListEncoder from Swift 4 let encoder = PropertyListEncoder() encoder.outputFormat = .xml let data = try encoder.encode(model) Or we can manually do with resultBuilder style Declare @resultBuilder for PlistBuilder that can support if else check import Foundation @resultBuilder enum PlistBuilder { static func buildBlock(_ components: PlistNode...) -> [PlistNode] { components } static func buildBlock(_ components: [PlistNode]...) -> [PlistNode] { components.flatMap { $0 } } static func buildEither(first component: [PlistNode]) -> [PlistNode] { component } static func buildEither(second component: [PlistNode]) -> [PlistNode] { component } static func buildOptional(_ component: [PlistNode]?...

February 18, 2022 · 2 min · Khoa Pham

How to generate JWT token for App Store Connect API in Swift

Issue #868 Use JWTKit and code from AppStoreConnect library import JWTKit public struct Credential { let issuerId: String let privateKeyId: String let privateKey: String public init( issuerId: String, privateKeyId: String, privateKey: String ) { self.issuerId = issuerId self.privateKeyId = privateKeyId self.privateKey = privateKey } func generateJWT() throws -> String { guard let signer = try? JWTSigner.es256( key: ECDSAKey.private(pem: privateKey)) else { throw AppStoreConnectError.invalidJWT } let payload = Payload( issueID: IssuerClaim(value: issuerId), expiration: ExpirationClaim( value: Date( timeInterval: 2 * 60, since: Date() ) ), audience: AudienceClaim( value: "appstoreconnect-v1" ) ) guard let jwt = try?...

February 16, 2022 · 1 min · Khoa Pham

How to send SPL token in Swift

Issue #867 Using https://github.com/ajamaica/Solana.Swift and the sendSPLToken method. Note that the from is the token account address and amount can have decimals solana.action.sendSPLTokens( mintAddress: MINT_ADDRESS, from: SENDER_TOKEN_ACCOUNT_ADDRESS, to: RECEIVER_WALLET_ADDDRESS, amount: UInt64(Float(amount) * units), allowUnfundedRecipient: true, onComplete: { result in continuation.resume(with: result) } ) We can also cross-reference to the solana web3.js library for how to create mint and transfer token import { clusterApiUrl, Connection, Keypair, LAMPORTS_PER_SOL } from '@solana/web3.js'; import { createMint, getOrCreateAssociatedTokenAccount, mintTo, transfer } from '....

February 11, 2022 · 2 min · Khoa Pham

How to calculate Solana transaction fee

Issue #866 A simple way is to use a fixed fee of 0.000005 For example from https://solscan.io/tx/5DkApvwTYuMqCiA94MhUVKJoLn8MGma9gAWXhreRJKqAs395P5CqEK3R84m3MWjcTKMem53XcLwYErGkaJAbQC2h?cluster=testnet And call some exchange API, like Coingecko https://api.coingecko.com/api/v3/simple/price?ids=solana&vs_currencies=usd and show the price in USD { "solana": { "usd": 114.13 } }

February 10, 2022 · 1 min · Khoa Pham

How to parse large JSON Dictionary in Swift

Issue #865 We can define some typealias and build extensions on JSONDictionary to easily extract values typealias JSONDictionary = [String: Any] typealias JSONArray = [JSONDictionary] extension JSONDictionary { func dict(_ key: String) -> JSONDictionary? { self[key] as? JSONDictionary } func array(_ key: String) -> JSONArray? { self[key] as? JSONArray } func value<T>(_ key: String, as: T.Type) -> T? { self[key] as? T } } let responseJson = try JSONSerialization.jsonObject(with: data, options: []) guard let responseJson = responseJson as?...

February 7, 2022 · 1 min · Khoa Pham

How to make simple async URLSession in Swift

Issue #864 Since async URLSession.shared.data is available in iOS 15+, we can build a custom one with withCheckedThrowingContinuation import UIKit enum HTTPMethod: String { case get = "GET" case post = "POST" } extension URLSession { func asyncData( with url: URL, method: HTTPMethod = .get, headers: [String: String] = [:], body: Data? = nil ) async throws -> Data { var request = URLRequest(url: url) request.httpMethod = method.rawValue request.allHTTPHeaderFields = [ "Content-Type": "application/json" ] request....

February 7, 2022 · 1 min · Khoa Pham

How to check SPL token balance on Solana

Issue #863 We will check USDC token balance on Solana testnet. Firstly, we will use https://usdcfaucet.com/ to airdrop some USDC tokens into our wallet. Secondly, we check USDC token mint address on testnet cluster using Solana Explorer https://explorer.solana.com/address/CpMah17kQEL2wqyMKt3mZBdTnZbkbfx4nqmQMFDP5vwp?cluster=testnet Then we make an RPC call to POST https://api.testnet.solana.comhttps://api.testnet.solana.com using method getTokenAccountsByOwner, passing our wallet address and the token mint address { "jsonrpc": "2.0", "id": 1, "method": "getTokenAccountsByOwner", "params": [ "53THxwqa9qF3cn46wHVKbGMM8hUpZDJE5jS3T1qVL5bc", { "mint": "CpMah17kQEL2wqyMKt3mZBdTnZbkbfx4nqmQMFDP5vwp" }, { "encoding": "jsonParsed" } ] } The response looks like below....

February 7, 2022 · 2 min · Khoa Pham

How to learn Solana programming

Issue #862 General https://solanacookbook.com/#contributing https://learn.figment.io/protocols/solana https://dev.to/dabit3/the-complete-guide-to-full-stack-solana-development-with-react-anchor-rust-and-phantom-3291 https://dev.to/kelvinkirima014/a-gentle-introduction-to-solana-2h3k https://buildspace.so/learn-solana Transaction https://medium.com/@asmiller1989/solana-transactions-in-depth-1f7f7fe06ac2 Token program https://spl.solana.com/token https://www.brianfriel.xyz/how-to-create-a-token-on-solana/ https://pencilflip.medium.com/solanas-token-program-explained-de0ddce29714

February 7, 2022 · 1 min · Khoa Pham

How to use subscript in Swift

Issue #861 Make it easy to access common cases, for example UserDefaults extension UserDefaults { enum Key: String { case hasBackup } subscript(key: Key) -> Bool { get { bool(forKey: key.rawValue) } set { set(newValue, forKey: key.rawValue) } } } UserDefaults[.hasBackup] = true

February 5, 2022 · 1 min · Khoa Pham

How to encode JSON dictionary into JSONEncoder

Issue #860 JSONEncoder deals with type-safe, so we need to declare an enum JSONValue for all possible types. We also need a custom initializer to init JSONValue from a JSON Dictionary import Foundation enum JSONValue { case string(String) case int(Int) case double(Double) case bool(Bool) case object([String: JSONValue]) case array([JSONValue]) } extension JSONValue: Encodable { public func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() switch self { case .string(let string): try container....

February 4, 2022 · 2 min · Khoa Pham

How to parse Apple Pay PKPayment in Swift

Issue #859 To parse PKPayment and used with Wyre CreateAppleOrder API, we can declare some Encodable structs import PassKit import Foundation struct PaymentObject: Encodable { var billingContact: Contact? var shippingContact: Contact? var token: JSONValue } extension PaymentObject { struct Contact: Encodable { var addressLines: [String]? var country: String? var countryCode: String? var familyName: String? var givenname: String? var locality: String? var postalCode: String? var administrativeArea: String? var subAdministrativeArea: String? var subLocality: String?...

February 4, 2022 · 1 min · Khoa Pham

How to pop multiple level with NavigationView and NavigationLink in SwiftUI

Issue #858 Use isActive and isDetailLink(false) Use Introspect .introspectNavigationController { nav in self.nav = nav } Read more https://www.cuvenx.com/post/swiftui-pop-to-root-view

January 27, 2022 · 1 min · Khoa Pham

How to generate Solana wallet acount in Swift

Issue #857 Use Solana.swift and Mnemonic seed phrase. For production, change endpoint to mainnet import UIKit import Solana import KeychainAccess enum SolanaError: Swift.Error { case accountFailed case unauthorized } final class SolanaClient { static let shared = SolanaClient() final class SolanaClient { static let shared = SolanaClient() private let solana: Solana private let accountStorage = KeychainAccountStorage() private let seedPharser = SeedPhraser() private let endpoint: RPCEndpoint = .devnetSolana private let network: NetworkingRouter init() { self....

January 26, 2022 · 2 min · Khoa Pham