This package aims to create a simple, uniform way of using the Phantom Deeplinking for native iOS projects.
- iOS 13.0+ / macOS 10.15+
- Swift 5.3+
From Xcode 12, you can use Swift Package Manager to add PhantomConnect to your project.
- Select your Project from the project browser, then Package Dependencies and click the "+" icon to add a new package.
- Add https://github.com/Tokr-Labs/phantom-connect
- Select "branch" with "main"
- Select PhantomConnect
If you encounter any problem or have a question on adding the package to an Xcode project, I suggest reading the Adding Package Dependencies to Your App guide article from Apple.
PhantomConnect.swift
- Entrypoint into the framework that contains configuration methods.
PhantomUrlHandler.swift
- Helper class to determine whether a URL is from phantom and how to parse its contents to be used within the recieving application.
PhantomConnectError.swift
- Custom errors to this framework that help with UX when something goes wrong.
PhantomDeeplink.swift
- Deeplink enumeration that aligns with the
PhantomConnectService.swift
methods.
PhantomConnectViewModel.swift
- This helper class is an ObservableObject that contains business logic used to create, encrypt and open universal links in the phantom wallet. If you can't or don't want to use the view model in a manner suitable to the SwiftUI MVVM pattern, you can use the
PhantomUtils.swift
andPhantomConnectService.swift
files directly.
You'll need to configure the framework to make sure you're pulling in the right metadata, connecting to the correct cluster and redirecting to the right application. Storing these properties in .xcconfig
files allows for different values to be used in CI/CD workflows.
PhantomConnect.configure(
appUrl: <YOUR_APP_URL>, // used for metadata (e.g. image shown in phantom dialog during deeplinking)
cluster: <SOLANA_CLUSTER>, // `devnet`|`mainnet-beta`
redirectUrl: <YOUR_APP_URL_SCHEME> // reverse app domain url ensures uniqueness, but this can be what ever you define
)
This framework was built with SwiftUI in mind, but that does not mean it cannot be used elsewhere. The following are code snippets that assume SwiftUI.
The example application in this repository stores all wallet information, including the dapp secret key used in creating the shared secret for encryption and decryption, in memory. For a real-world implementation you would want to store this information in a local keychain or on a remote server. NOTE that ideally the dapp encryption secret key would be saved behind some form of authentication within the app keychain and never leave the device.
import PhantomConnect
...
@StateObject var phantomConnectViewModel = PhantomConnectViewModel()
...
Button {
try? phantomConnectViewModel.connectWallet()
} label: {
Text("Connect with Phantom")
}
.onWalletConnect(viewModel: phantomConnectViewModel) { publicKey, phantomEncryptionPublicKey, session, error in
// wallet connected
}
...
import PhantomConnect
...
@StateObject var phantomConnectViewModel = PhantomConnectViewModel()
...
Button {
try? phantomConnectViewModel.disconnectWallet(
dappEncryptionKey: <DAPP_ENCRYPTION_PUBLIC_KEY>,
phantomEncryptionKey: <PHANTOM_ENCRYPTION_PUBLIC_KEY>,
session: <CONNECTED_PHANTOM_SESSION>,
dappSecretKey: <DAPP_ENCRYPTION_SECRET_KEY>
)
} label: {
Text("Disconnect Wallet")
}
.onWalletDisconnect { error in
// wallet disconnected
}
...
import PhantomConnect
...
@StateObject var phantomConnectViewModel = PhantomConnectViewModel()
...
Button {
try? phantomConnectViewModel.sendAndSignTransaction(
serializedTransaction: <SERIALIZED_TRANSACTION>,
dappEncryptionKey: <DAPP_ENCRYPTION_PUBLIC_KEY>,
phantomEncryptionKey: <PHANTOM_ENCRYPTION_PUBLIC_KEY>,
session: <CONNECTED_PHANTOM_SESSION>,
dappSecretKey: <DAPP_ENCRYPTION_SECRET_KEY>
)
} label: {
Text("Disconnect Wallet")
}
.onWalletTransaction(
phantomEncryptionPublicKey: <PHANTOM_ENCRYPTION_PUBLIC_KEY>,
dappEncryptionSecretKey: <DAPP_ENCRYPTION_SECRET_KEY>
) { signature, error in
// handle transaction response
}
...