iOS · Swift

iOS quickstart

Add iArnacon, implement three persistence methods, connect the network, then message and ring an identity. No API key. No CocoaPods.

SPM · iArnacon iOS 13+ CallKit is yours
1 Install 2 Host 3 Init 4 Message 5 Call

1. Install

In Xcode: File → Add Package Dependencies. Add the iOS SDK repository, not the store app.

Swift PackageiArnacon
https://github.com/cellact/Arnacon-iOS-SDK.git

Product: iArnacon
Platforms: iOS 13+, macOS 10.15+
Dependencies the package already pulls: web3swift, BlueECC
No CocoaPods. Do not add cellact/arnacon-swift — that is the store app.

The repository is not on the public package index. If GitHub returns 404, you do not have access yet. A private clone still works as a local package path.

2. Host contracts

The SDK does not own disk or HTTP. You implement three protocols. The store app wires UserDefaults and the shipped CloudFunctions server.

PDataSaver is three methods. The protocol extension on top of them stores the wallet, products, and tokens. Implement the three; do not reimplement the extension.

PDataSaverrequired
import iArnacon

final class AppDataSaver: PDataSaver {
    private let defaults = UserDefaults.standard

    func setPreference(key: String, value: String) {
        defaults.set(value, forKey: key)
    }

    func getPreference(key: String, defaultValue: String?) -> String? {
        defaults.string(forKey: key) ?? defaultValue
    }

    func resetPreferences() {
        guard let domain = Bundle.main.bundleIdentifier else { return }
        defaults.removePersistentDomain(forName: domain)
    }
}

PLogger is debug(_:) and error(_:). PServer is the backend surface. Use the shipped CloudFunctions unless you are replacing the entire backend. You do not write URL lists by hand — CloudFunctions loads them from on-chain config.

3. Init

Create the SDK, set a logger, then connect the network. Blockchain calls fail or return nil until setNetworkSettings finishes.

gpsCoordinates is the local encryption password for the wallet secrets. It is not a location and not a phone number. The store app currently passes "123". Use a secret your app already stores, and never log it.

SwiftArnacon : AArnacon
import iArnacon

let dataSaver = AppDataSaver()
let arnacon = Arnacon(
    dataSaveHelper: dataSaver,
    gpsCoordinates: storedEncryptionPassword
)
arnacon.setLogger(AppLogger())

let server = await CloudFunctions()
let appVersion = Bundle.main.infoDictionary?["AppStoreVersion"] as? String
    ?? "1.0.0"
await arnacon.setNetworkSettings(server, appVersion, false)

The third argument is forceFresh. The store app passes false so contract addresses are not refetched on every launch. Pass true only when you intend to refresh.

4. Identity

People talk as an email they verify, or as a new identity they install. Recipients look like alice.arnacon.global. A phone number is not the gate.

Swiftidentity
let me = arnacon.getMainCoordinates()

let owner = try await arnacon.getENSOwner(
    memberName: "alice.arnacon.global"
)

let xData = arnacon.getXData(identity: "alice.arnacon.global")
let xSign = try arnacon.getXSign(xData)
let signed = try arnacon.signMessage(message)

Never log the mnemonic, the encryption password, or the private key. installProduct adds an identity. New code must wait for an explicit user Allow. Do not auto-run it from a push payload.

5. First message

Prepare, then execute. NotificationType.message = 1. Do not treat a plaintext helper as the entry if you need to inspect the plan.

Swiftplan
let prepared = await arnacon.prepareNotification(
    sender: "alice.arnacon.global",
    target: "bob.arnacon.global",
    message: "Hello from Alice",
    notificationType: .message
)
let result = await arnacon.executeNotification(prepared)

// or one step:
let sent = await arnacon.sendNotification(
    sender: "alice.arnacon.global",
    target: "bob.arnacon.global",
    message: "Hello from Alice",
    notificationType: .message
)

Encrypted chat content is a separate path: encryptMessageForRecipient then sendDirectMessage. The store app uses that for the chat body. The notification plan is what wakes the other device.

6. First call

Ring the other identity. NotificationType.call = 0. CallKit lives in your app, not in this SDK.

Swiftcall
let ringing = try await arnacon.sendCallNotification(
    sender: "alice.arnacon.global",
    target: "bob.arnacon.global"
)

// or the plan path:
let prepared = await arnacon.prepareNotification(
    sender: "alice.arnacon.global",
    target: "bob.arnacon.global",
    message: "{\"type\":\"call\"}",
    notificationType: .call
)
let result = await arnacon.executeNotification(prepared)

SDP, ICE, cancel, reject, and end are not incoming-call invites. Use NotificationType.realtimeSignal for those so the other phone does not ring again.

7. Push tokens

Register this device so it can ring. Do not upload tokens for someone else.

Swiftthis device
try await arnacon.sendNotiTokens(fcmToken, voipToken)

VoIP push and CallKit reporting stay in the host app. The SDK only stores and routes the tokens.

What the host still owns

  • CallKit, PushKit, and the in-call UI
  • Chat storage and message list UI
  • Firebase / APNs project setup
  • The Allow dialog before installProduct
  • Payments and store checkout
Never expose from the host app:
  • Mnemonic or private keys
  • gpsCoordinates / the encryption password
  • Sending as an identity the user did not select

Next

Concepts, then the other platform.