The Pubky Core Mobile SDK provides native bindings for iOS and Android platforms to interact with Pubky. This SDK allows you to perform operations like publishing content, retrieving data and managing authentication.
./build.sh all
./build.sh ios
./build.sh android
./build.sh python
cargo test -- --test-threads=1
-
Add the XCFramework to your Xcode project:
- Drag bindings/ios/PubkyCore.xcframework into your Xcode project Ensure "Copy items if needed" is checked Add the framework to your target
-
Copy the Swift bindings:
- Add bindings/ios/pubkycore.swift to your project
import PubkyCore
class PubkyManager {
// Generate a new secret key
func generateNewAccount() throws -> String {
let result = try generateSecretKey()
guard let jsonData = result[1].data(using: .utf8),
let json = try? JSONSerialization.jsonObject(with: jsonData) as? [String: Any],
let secretKey = json["secret_key"] as? String else {
throw NSError(domain: "PubkyError", code: -1, userInfo: [NSLocalizedDescriptionKey: "Failed to parse response"])
}
return secretKey
}
// Get a signup token
func getSignupToken(homeserverPubky: String, adminPassword: String) async throws -> String {
let result = try getSignupToken(homeserverPubky: homeserverPubky, adminPassword: adminPassword)
if result[0] == "error" {
throw NSError(domain: "PubkyError", code: -1, userInfo: [NSLocalizedDescriptionKey: result[1]])
}
return result[1]
}
// Sign up with a homeserver (with optional signup token)
func signUp(secretKey: String, homeserver: String, signupToken: String? = nil) async throws -> String {
let result = try signUp(secretKey: secretKey, homeserver: homeserver, signupToken: signupToken)
if result[0] == "error" {
throw NSError(domain: "PubkyError", code: -1, userInfo: [NSLocalizedDescriptionKey: result[1]])
}
return result[1]
}
// Publish content
func publishContent(recordName: String, content: String, secretKey: String) async throws -> String {
let result = try publish(recordName: recordName, recordContent: content, secretKey: secretKey)
if result[0] == "error" {
throw NSError(domain: "PubkyError", code: -1, userInfo: [NSLocalizedDescriptionKey: result[1]])
}
return result[1]
}
// Retrieve content
func getContent(url: String) async throws -> String {
let result = try get(url: url)
if result[0] == "error" {
throw NSError(domain: "PubkyError", code: -1, userInfo: [NSLocalizedDescriptionKey: result[1]])
}
return result[1]
}
}
class ViewController: UIViewController {
let pubkyManager = PubkyManager()
func setupAccount() async {
do {
// Generate new account
let secretKey = try pubkyManager.generateNewAccount()
// Sign up with homeserver
let homeserver = "pubky://8pinxxgqs41n4aididenw5apqp1urfmzdztr8jt4abrkdn435ewo"
// For servers requiring signup tokens
// let adminPassword = "your-admin-password"
// let signupToken = try await pubkyManager.getSignupToken(homeserverPubky: homeserver, adminPassword: adminPassword)
// let publicKey = try await pubkyManager.signUp(secretKey: secretKey, homeserver: homeserver, signupToken: signupToken)
// For servers without token requirements
let publicKey = try await pubkyManager.signUp(secretKey: secretKey, homeserver: homeserver)
// Publish content
let content = "Hello, Pubky!"
let recordName = "example.com"
let publishResult = try await pubkyManager.publishContent(
recordName: recordName,
content: content,
secretKey: secretKey
)
print("Published with public key: \(publishResult)")
} catch {
print("Error: \(error.localizedDescription)")
}
}
}
-
Add the JNI libraries to your project:
- Copy the contents of bindings/android/jniLibs to your project's app/src/main/jniLibs directory
-
Add the Kotlin bindings:
- Copy bindings/android/pubkycore.kt to your project's source directory
class PubkyManager {
init {
// Initialize the library
System.loadLibrary("pubkycore")
}
fun generateNewAccount(): String {
val result = generateSecretKey()
if (result[0] == "error") {
throw Exception(result[1])
}
val json = JSONObject(result[1])
return json.getString("secret_key")
}
suspend fun getSignupToken(homeserverPubky: String, adminPassword: String): String {
val result = getSignupToken(homeserverPubky, adminPassword)
if (result[0] == "error") {
throw Exception(result[1])
}
return result[1]
}
suspend fun signUp(secretKey: String, homeserver: String, signupToken: String? = null): String {
val result = signUp(secretKey, homeserver, signupToken)
if (result[0] == "error") {
throw Exception(result[1])
}
return result[1]
}
suspend fun publishContent(recordName: String, content: String, secretKey: String): String {
val result = publish(recordName, content, secretKey)
if (result[0] == "error") {
throw Exception(result[1])
}
return result[1]
}
suspend fun getContent(url: String): String {
val result = get(url)
if (result[0] == "error") {
throw Exception(result[1])
}
return result[1]
}
}
class MainActivity : AppCompatActivity() {
private val pubkyManager = PubkyManager()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
lifecycleScope.launch {
try {
// Generate new account
val secretKey = pubkyManager.generateNewAccount()
// Sign up with homeserver
val homeserver = "pubky://8pinxxgqs41n4aididenw5apqp1urfmzdztr8jt4abrkdn435ewo"
// For servers requiring signup tokens
// val adminPassword = "your-admin-password"
// val signupToken = pubkyManager.getSignupToken(homeserver, adminPassword)
// val publicKey = pubkyManager.signUp(secretKey, homeserver, signupToken)
// For servers without token requirements
val publicKey = pubkyManager.signUp(secretKey, homeserver)
// Publish content
val content = "Hello, Pubky!"
val recordName = "example.com"
val publishResult = pubkyManager.publishContent(
recordName = recordName,
content = content,
secretKey = secretKey
)
Log.d("Pubky", "Published with public key: $publishResult")
} catch (e: Exception) {
Log.e("Pubky", "Error: ${e.message}")
}
}
}
}
// iOS
func publishHttps(recordName: String, target: String, secretKey: String) async throws -> String {
let result = try publishHttps(recordName: recordName, target: target, secretKey: secretKey)
if result[0] == "error" {
throw NSError(domain: "PubkyError", code: -1, userInfo: [NSLocalizedDescriptionKey: result[1]])
}
return result[1]
}
// Android
suspend fun publishHttps(recordName: String, target: String, secretKey: String): String {
val result = publishHttps(recordName, target, secretKey)
if (result[0] == "error") {
throw Exception(result[1])
}
return result[1]
}
For servers that require authentication control:
// iOS
// 1. Admin generates a signup token
func getServerSignupToken(homeserverPubky: String, adminPassword: String) async throws -> String {
let result = try getSignupToken(homeserverPubky: homeserverPubky, adminPassword: adminPassword)
if result[0] == "error" {
throw NSError(domain: "PubkyError", code: -1, userInfo: [NSLocalizedDescriptionKey: result[1]])
}
return result[1]
}
// 2. User signs up with the token
func signUpWithToken(secretKey: String, homeserver: String, token: String) async throws -> String {
let result = try signUp(secretKey: secretKey, homeserver: homeserver, signupToken: token)
if result[0] == "error" {
throw NSError(domain: "PubkyError", code: -1, userInfo: [NSLocalizedDescriptionKey: result[1]])
}
return result[1]
}
// Android
// 1. Admin generates a signup token
suspend fun getServerSignupToken(homeserverPubky: String, adminPassword: String): String {
val result = getSignupToken(homeserverPubky, adminPassword)
if (result[0] == "error") {
throw Exception(result[1])
}
return result[1]
}
// 2. User signs up with the token
suspend fun signUpWithToken(secretKey: String, homeserver: String, token: String): String {
val result = signUp(secretKey, homeserver, token)
if (result[0] == "error") {
throw Exception(result[1])
}
return result[1]
}
// iOS
func createRecoveryFile(secretKey: String, passphrase: String) throws -> String {
let result = try createRecoveryFile(secretKey: secretKey, passphrase: passphrase)
if result[0] == "error" {
throw NSError(domain: "PubkyError", code: -1, userInfo: [NSLocalizedDescriptionKey: result[1]])
}
return result[1]
}
// Android
fun createRecoveryFile(secretKey: String, passphrase: String): String {
val result = createRecoveryFile(secretKey, passphrase)
if (result[0] == "error") {
throw Exception(result[1])
}
return result[1]
}
All methods return a Vec<String>
where:
- The first element ([0]) is either "success" or "error"
- The second element ([1]) contains either the result data or error message
It's recommended to wrap all calls in try-catch blocks and handle errors appropriately in your application.
You can switch between default and testnet:
// iOS
try switchNetwork(useTestnet: true) // For testnet
try switchNetwork(useTestnet: false) // For default
// Android
switchNetwork(true) // For testnet
switchNetwork(false) // For default