The Flow Client Library (FCL) is used to interact with user wallets and the Flow blockchain. When using FCL for authentication, dApps are able to support all FCL-compatible wallets on Flow and their users without any custom integrations or changes needed to the dApp code.
For more description, please refer to fcl.js
This repo is inspired by fcl-js and fcl-swift
- Swift version >= 5.6
- iOS version >= 13
FCL-SDK is available through CocoaPods. You can include specific subspec to install, simply add the following line to your Podfile:
pod 'FCL-SDK', '~> 0.4.1'
.package(url: "https://github.com/portto/fcl-swift.git", .upToNextMinor(from: "0.4.1"))
Here's an example PackageDescription:
// swift-tools-version: 5.6
import PackageDescription
let package = Package(
name: "MyPackage",
products: [
.library(
name: "MyPackage",
targets: ["MyPackage"]
),
],
dependencies: [
.package(url: "https://github.com/portto/fcl-swift.git", .upToNextMinor(from: "0.4.1"))
],
targets: [
.target(
name: "MyPackage",
dependencies: [
.product(name: "FCL_SDK", package: "fcl-swift"),
]
)
]
)
We only support iOS platform now. Please switch your XCode build target to iOS device.
import FCL_SDK
Initialize WalletProvider
instance e.g. BloctoWalletProvider
, DapperWalletProvider
. And simply specify network
and put those wallet providers into config option supportedWalletProviders
then you are good to go.
import FCL_SDK
do {
let bloctoWalletProvider = try BloctoWalletProvider(
bloctoAppIdentifier: bloctoSDKAppId,
window: nil,
network: .testnet
)
fcl.config
.put(.network(.testnet))
.put(.supportedWalletProviders(
[
bloctoWalletProvider,
]
))
} catch {
// handle error
}
Task {
try await fcl.login()
}
Note: bloctoSDKAppId can be found in Blocto Developer Dashboard, for detail instruction please refer to Blocto Docs
Cryptographic signatures are a key part of the blockchain. They are used to prove ownership of an address without exposing its private key. While primarily used for signing transactions, cryptographic signatures can also be used to sign arbitrary messages.
FCL has a feature that let you send arbitrary data to a configured wallet/service where the user may approve signing it with their private keys.
We can retrieve user signatures only after user had logged in, otherwise error will be thrown.
Task {
do {
let signatures: [FCLCompositeSignature] = try await fcl.signUserMessage(message: "message you want user to sign.")
} catch {
// handle error
}
}
The message could be signed by several private key of the same wallet address. Those signatures will be valid all together as long as their corresponding key weight sum up at least 1000. For more info about multiple signatures, please refer to Flow docs
- Query the chain: Send arbitrary Cadence scripts to the chain and receive back decoded values
import FCL_SDK
let script = """
import ValueDapp from \(valueDappContract)
pub fun main(): UFix64 {
return ValueDapp.value
}
"""
Task {
let argument = try await fcl.query(script: script)
label.text = argument.value.description
}
- Mutate the chain: Send arbitrary transactions with specify authorizer to perform state changes on chain.
import FCL_SDK
Task { @MainActor in
guard let userWalletAddress = fcl.currentUser?.address else {
// handle error
return
}
let scriptString = """
import ValueDapp from 0x5a8143da8058740c
transaction(value: UFix64) {
prepare(authorizer: AuthAccount) {
ValueDapp.setValue(value)
}
}
"""
let argument = Cadence.Argument(.ufix64(10))
let txHsh = try await fcl.mutate(
cadence: scriptString,
arguments: [argument],
limit: 100,
authorizers: [userWalletAddress]
)
}
Learn more about on-chain interactions >
To prove ownership of a wallet address, there are two approaches.
- Account proof: in the beginning of authentication, there are
accountProofData
you can provide for user to sign and return generated signatures along with account address.
fcl.authanticate
is also called behide fcl.login()
with accountProofData set to nil.
let accountProofData = FCLAccountProofData(
appId: "Here you can specify your app name.",
nonce: "75f8587e5bd5f9dcc9909d0dae1f0ac5814458b2ae129620502cb936fde7120a" // minimum 32-byte random nonce as a hex string.
)
let address = try await fcl.authanticate(accountProofData: accountProofData)
- User signature: provide specific message for user to sign and generate one or more signatures.
What makes message signatures more interesting is that we can use Flow blockchain to verify the signatures. Cadence has a built-in function called verify that will verify a signature against a Flow account given the account address.
FCL includes a utility function, verifyUserSignatures, for verifying one or more signatures against an account's public key on the Flow blockchain.
You can use both in tandem to prove a user is in control of a private key or keys. This enables cryptographically-secure login flow using a message-signing-based authentication mechanism with a user’s public address as their identifier.
To verify above ownership, there are two utility functions define accordingly in AppUtilities.
- Get account details from any Flow address
let account: Account? = try await fcl.flowAPIClient.getAccountAtLatestBlock(address: address)
- Get the latest block
let block: Block? = try await fcl.flowAPIClient.getLatestBlock(isSealed: true)
- Transaction status polling
let result = try await fcl.getTransactionStatus(transactionId: txHash)
Wallet providers on Flow have the flexibility to build their user interactions and UI through a variety of ways:
- Native app intercommunication via Universal links or custom schemes.
- Back channel communication via HTTP polling with webpage button approving.
FCL is agnostic to the communication channel and be configured to create both custodial and non-custodial wallets. This enables users to interact with wallet providers both native app install or not.
Native app should be considered first to provide better user experience if installed, otherwise fallback to back channel communication.
The communication channels involve responding to a set of pre-defined FCL messages to deliver the requested information to the dApp. Implementing a FCL compatible wallet on Flow is as simple as filling in the responses with the appropriate data when FCL requests them.
- Blocto (fully supported) Docs
- Dapper Wallet (support only authn for now)
- dApps can display and support all FCL compatible wallets who conform to
WalletProvider
. - Users don't need to sign up for new wallets - they can carry over their existing one to any dApps that use FCL for authentication and authorization.
- Wallet selection panel will be shown automatically when
login()
is being called only if there are more than one wallet provider insupportedWalletProviders
.
import FCL_SDK
do {
let bloctoWalletProvider = try BloctoWalletProvider(
bloctoAppIdentifier: bloctoSDKAppId,
window: nil,
network: .testnet
)
let dapperWalletProvider = DapperWalletProvider.default
fcl.config
.put(.network(.testnet))
.put(.supportedWalletProviders(
[
bloctoWalletProvider,
dapperWalletProvider,
]
))
} catch {
// handle error
}
Task {
try await fcl.login()
}
- Declare a wallet provider type and conform the protocol WalletProvider.
- If building a wallet involve back channel communication, read the wallet guide first to build the concept of the implementation and use method from
WalletProvider
to fulfill your business logic.
Every walllet provider can use below property from WalletProvider
to customize icon, title and description. Those info will be shown here.
var providerInfo: ProviderInfo { get }
Learn Flow's smart contract language to build any script or transactions: Cadence.
Explore all of Flow docs and tools.
Notice a problem or want to request a feature? Add an issue or Make a pull request.