The following documentation aims to educate you on building a native mobile application on Flow. It first presents Monster Maker, a starter project we’ve built to represent simple Flow mobile concepts. Next it presents various developer resources related to building mobile native Flow applications.
Monster Maker is a native iOS app that allows users to connect a wallet, sign a transaction to mint an NFT (a monster) and display their collection of NFTs (their monsters) within the app. It’s meant to be a lightweight sample project to exemplify how to build a mobile native Flow project. If you’re looking to build a native mobile application for Flow, exploring the Monster Maker code base first or even building off of it is a great way to get started.
The Monster Maker Github Repo can be found here:
https://github.com/onflow/monster-maker
Before you run Monster Maker on your device, please make sure you have installed the Xcode14 from Mac App Store. Once you clone the repo, open the MonsterMaker.xcodeproj under the iOS folder.
Xcode should automatically setup the project for you. If you do see any error related to dependencies, run Xcode Menu -> File -> Packages -> Reset Package Cache
to resolve the issue.
In the meantime, you can choose a simulator or your iPhone to run. For more detail here is the official doc. For run in real device, there are a few steps to deal with signing:
-
Add your apple account to the Xcode which can be accessed from
Xcode Menu -> Settings -> Add account
. -
Change the Team to your Personal Apple account from the Signing & Capabilities under the project target menu. For more detail, please check the screenshot below.
To connect with wallets, there is native wallet discovery in the app. Once you click on connect, it will bring out the list of the wallets which support HTTP/POST
or WC/RPC
method.
To make sure, the wallet can recognise your dApp, there is a few field you will need to config before connect to a wallet. The account proof config is optional. In addition, you will need to create a project id from walletconnect cloud before you can connect to the WC/RPC
compatible wallet such as dapper self custody or lilico wallet.
1import FCL23// Config the App4let defaultProvider: FCL.Provider = .dapperPro5let defaultNetwork: Flow.ChainID = .testnet // or .mainnet67// Optinal: Config for account proof8let accountProof = FCL.Metadata.AccountProofConfig(appIdentifier: "Monster Maker")910// Config for WC/RPC compatible wallet11let walletConnect = FCL.Metadata.WalletConnectConfig(urlScheme: "monster-maker://", projectID: "12ed93a2aae83134c4c8473ca97d9399")1213// Config basic dApp info14let metadata = FCL.Metadata(appName: "Monster Maker",15appDescription: "Monster Maker Demo App for mobile",16appIcon: URL(string: "https://i.imgur.com/jscDmDe.png")!,17location: URL(string: "https://monster-maker.vercel.app/")!,18accountProof: accountProof,19walletConnectConfig: walletConnect)20fcl.config(metadata: metadata,21env: defaultNetwork,22provider: defaultProvider)2324// Import keywords replacement for cadence query and transaction25fcl.config26.put("0xFungibleToken", value: "0x631e88ae7f1d7c20")27.put("0xMonsterMaker", value: "0xfd3d8fe2c8056370")28.put("0xMetadataViews", value: "0x631e88ae7f1d7c20")29.put("0xTransactionGeneration", value: "0x44051d81c4720882")
In Monster Maker, the Connect button triggers opening of Wallet Discovery
For the wallet support HTTP/POST
, it will use webview to show the following actions.
For the wallet support WC/RPC
, it will use deep-link to the wallet for actions.
You can open the native wallet discovery to make the selection, but also you can connect to the specific wallet as well.
Here is the code snippet of it:
1import FCL23// Open discovery view4fcl.openDiscovery()56// Or manual connect to specific wallet7try fcl.changeProvider(provider: provider, env: .testnet)8try await fcl.authenticate()
In Monster Maker, Initializing the NFT collection with the Initialize button triggers a transaction.
Similar to what we have on fcl-js, native sdk also use query
and mutate
for on-chain interactions. To request a signature from user, you can simply use fcl.mutate
method. By default, the user will be the payer, proposer and authorizer, if you want to add custom authorizer please refer to the code from Server and iOS end.
1guard let user = fcl.currentUser else {2// Not signin3return4}56let txId = try await fcl.mutate(7cadence: """8transaction(test: String, testInt: Int) {9prepare(signer: AuthAccount) {10log(signer.address)11log(test)12log(testInt)13}14}15""",16args: [17.string("Hello"),18.int(10)19],20gasLimit: 999,21authorizors: [user])2223print("txId -> \(txId)")
The View page in Monster Maker exemplifies showing Monster Maker NFTs held by the connected wallet
To view the NFT from an wallet address, first and foremost, we highly recommend you use NFT-Catalog standard when you are ready. So that it will be easy to allow other platform like marketplace and wallet to recognise and display your NFT collection. However, during development, you always can query your NFT with fcl.query
. Here is an example:
-
Query cadence
1import NonFungibleToken from 0xNonFungibleToken2import MonsterMaker from 0xMonsterMaker3import MetadataViews from 0xMetadataViews45pub struct Monster {6pub let name: String7pub let description: String8pub let thumbnail: String9pub let itemID: UInt6410pub let resourceID: UInt6411pub let owner: Address12pub let component: MonsterMaker.MonsterComponent1314init(15name: String,16description: String,17thumbnail: String,18itemID: UInt64,19resourceID: UInt64,20owner: Address,21component: MonsterMaker.MonsterComponent22) {23self.name = name24self.description = description25self.thumbnail = thumbnail26self.itemID = itemID27self.resourceID = resourceID28self.owner = owner29self.component = component30}31}3233pub fun getMonsterById(address: Address, itemID: UInt64): Monster? {3435if let collection = getAccount(address).getCapability<&MonsterMaker.Collection{NonFungibleToken.CollectionPublic, MonsterMaker.MonsterMakerCollectionPublic}>(MonsterMaker.CollectionPublicPath).borrow() {3637if let item = collection.borrowMonsterMaker(id: itemID) {38if let view = item.resolveView(Type<MetadataViews.Display>()) {39let display = view as! MetadataViews.Display40let owner: Address = item.owner!.address!41let thumbnail = display.thumbnail as! MetadataViews.HTTPFile4243return Monster(44name: display.name,45description: display.description,46thumbnail: thumbnail.url,47itemID: itemID,48resourceID: item.uuid,49owner: address,50component: item.component51)52}53}54}5556return nil57}5859pub fun main(address: Address): [Monster] {60let account = getAccount(address)61let collectionRef = account.getCapability(MonsterMaker.CollectionPublicPath)!.borrow<&{NonFungibleToken.CollectionPublic}>()62?? panic("Could not borrow capability from public collection")6364let ids = collectionRef.getIDs()6566let monsters : [Monster] = []6768for id in ids {69if let monster = getMonsterById(address: address, itemID: id) {70monsters.append(monster)71}72}7374return monsters75}
1let nftList = try await fcl.query(script: cadenceScript,2args: [.address(address)])3.decode([NFTModel].self)
FCL Swift
FCL Swift is the iOS native SDK for FCL. This SDK is integrated into the Monster Maker sample.
https://github.com/Outblock/fcl-swift
FCL Android
FCL Android is the Android native SDK for FCL.
https://github.com/Outblock/fcl-android
FCL Wallet Connect 2.0
One of the easiest ways to connect to a wallet via a mobile native dApp is through Flow’s new support for Wallet Connect 2.0. This is the pattern that Monster Maker uses to connect to the Dapper Self Custody wallet and Lilico. For more information on FCL Wallet Connect 2.0, check out this page:
How to Build a Native iOS Dapp
The Agile Monkeys has written a very comprehensive guide on how to build a native mobile application on iOS and interface with fcl-swift. Found here: