Implementing NFT Creation (Minting) via Mobile Application
Minting is invoking a smart contract's mint function, which records a new token on the blockchain and attaches metadata to it. The mobile minting flow is more complex than desktop: users must select a media file, upload it to decentralized storage, compose metadata in ERC-721 JSON format, then invoke the contract. All this must work with connection interruptions, slow IPFS, and limited mobile resources.
Uploading Media to IPFS
Minting begins with a media file: image, video, or audio. Users select from their photo gallery or capture new media.
On iOS — use PHPickerViewController (iOS 14+) to select from Photos. On Android — use ActivityResultContracts.PickVisualMedia (Photo Picker, Android 13+) with fallback to GetContent.
Upload the media file to IPFS via Pinata, NFT.Storage, or Web3.Storage API:
// iOS — uploading file to Pinata IPFS
func pinFileToIPFS(fileURL: URL, fileName: String) async throws -> String {
var request = URLRequest(url: URL(string: "https://api.pinata.cloud/pinning/pinFileToIPFS")!)
request.httpMethod = "POST"
request.setValue("Bearer \(pinataJWT)", forHTTPHeaderField: "Authorization")
let boundary = UUID().uuidString
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
var body = Data()
body.append("--\(boundary)\r\nContent-Disposition: form-data; name=\"file\"; filename=\"\(fileName)\"\r\n")
body.append("Content-Type: image/jpeg\r\n\r\n")
body.append(try Data(contentsOf: fileURL))
body.append("\r\n--\(boundary)--\r\n")
request.httpBody = body
let (data, _) = try await URLSession.shared.data(for: request)
let response = try JSONDecoder().decode(PinataResponse.self, from: data)
return "ipfs://\(response.ipfsHash)"
}
Uploading large video files (100+ MB) on mobile should be a background task with a progress bar. On iOS — use URLSession with background configuration; on Android — WorkManager with setExpedited.
Forming ERC-721 Metadata
After uploading media — compose metadata JSON according to OpenSea standard:
// Android — forming NFT metadata JSON
data class NftMetadata(
val name: String,
val description: String,
val image: String, // ipfs://QmXxx... (CID of uploaded file)
val externalUrl: String?,
val attributes: List<NftAttribute>
)
data class NftAttribute(
val traitType: String,
val value: String
)
// Example result
val metadata = NftMetadata(
name = "My NFT #1",
description = "Created via mobile app",
image = "ipfs://QmImageHash...",
attributes = listOf(
NftAttribute("Background", "Blue"),
NftAttribute("Rarity", "Rare")
)
)
This JSON is also uploaded to IPFS — yielding ipfs://QmMetadataHash. This URI is passed to mint(to, tokenId, metadataURI).
Minting Form UI
Minimum fields: Name, Description, media file, attributes (optional). Attributes are dynamically added key-value pairs. Show an NFT card preview in real time.
Break upload progress into three steps with an indicator:
- Uploading media to IPFS (long step)
- Uploading metadata to IPFS (fast)
- Minting transaction
Lazy Minting
If gas is expensive — offer lazy minting: the token is not recorded on blockchain immediately. Metadata is stored off-chain, and the transaction occurs on first sale (buyer pays minting gas). OpenSea supports this via vouchers — EIP-712 signed messages that mint on purchase.
// iOS — signing voucher for lazy minting (EIP-712)
struct NFTVoucher {
let tokenId: BigUInt
let minPrice: BigUInt
let uri: String
let signature: Data // EIP-712 creator signature
}
Lazy minting removes the "pay gas before selling" barrier — important for new creators.
Timeline
5 working days: selecting media, uploading to IPFS (Pinata/NFT.Storage), forming ERC-721 metadata, calling mint, progress bar across steps, basic attributes form. Lazy minting via EIP-712 vouchers — additional 2–3 days.







