WuKongIM Docs

iOS quickstart

Install exactly WuKongEasySDK iOS 1.1.1 and use contiguous Swift code for connection, online messaging, and lifecycle cleanup.

Use one application-owned client to install, connect, listen, send a person message, and clean up. The product backend supplies connection material first; Alice and Bob then prove online messaging in two independent app processes.

The current Product Gateway supports this connection path

The current Product Gateway supports pinned v1.1.1 JSON-RPC CONNECT and online bidirectional messaging, including object and Base64 payloads; camelCase RECVACK carries both messageId and messageSeq, and device wire values are APP 0, WEB 1, and PC 2. The tutorial installs CocoaPods 1.1.1. The unified iOS/macOS example passed at source revision 40014c16c0becd390c105098d359048901f4d87c, which is now included in the v1.1.1 Release. The CocoaPods artifact then completed bidirectional messaging and disconnect on iOS Simulator.

What you will have

  • An iOS dependency pinned to 1.1.1.
  • One application-level client object that owns initialization, listeners, connection, and logout.
  • A two-client acceptance scaffold for Alice's send result and Bob's realtime message event.
  • Explicit follow-up work for tokens, WSS, offline recovery, and production acceptance.
git clone https://github.com/WuKongIM/WuKongEasySDK-iOS.git
cd WuKongEasySDK-iOS
git checkout 40014c16c0becd390c105098d359048901f4d87c
swift test
swift build -c release
cd Examples/WuKongIMExample-Unified
./build.sh macos
./build.sh ios

After booting an iOS Simulator, run ./build.sh ios --run; use ./build.sh macos --run for macOS. Run the Official Examples covers server preparation and Alice/Bob acceptance. The source example and iOS 1.1.1 artifact now have separate runtime receipts; keep those evidence classes distinct.

Before you begin

Prepare the following:

  • Set the iOS deployment target to iOS 15 or later. The package manifest declares iOS 13, but the public WuKongEasySDK class in v1.1.1 is marked @available(iOS 15.0, ...), so this tutorial uses the more conservative API limit.
  • Use an Xcode toolchain that supports Swift 5.7 packages.
  • Run a WuKongIM single-node cluster or multi-node cluster whose /readyz is healthy and whose WebSocket Gateway is reachable from the device.
  • Have the product backend return uid, a short-lived token, and websocketUrl separately for Alice and Bob.

Read Identity & Token first. Never embed a fixed token in the app, source repository, logs, or screenshots.

Step 1: install an exact version

Swift Package Manager

In Xcode, choose File → Add Package Dependencies and enter:

https://github.com/WuKongIM/WuKongEasySDK-iOS.git

Choose Exact Version and enter 1.1.1. If the project owns a Package.swift, use an exact rule:

dependencies: [
    .package(
        url: "https://github.com/WuKongIM/WuKongEasySDK-iOS.git",
        exact: "1.1.1"
    )
]

CocoaPods

target 'YourApp' do
  pod 'WuKongEasySDK', '1.1.1'
end

Run pod install, then open the .xcworkspace. Choose one installation method, not both.

Step 2: receive connection material from the product backend

Map the product-backend response into an application model. The real implementation obtains this over HTTPS and does not call Product HTTP management routes from the client:

struct IMBootstrap: Decodable {
    let uid: String
    let token: String
    let websocketUrl: String
}

websocketUrl may use ws:// during local development. In production, the product backend selects and returns a wss:// address.

Step 3: own the client lifecycle

The following object registers listeners before connecting. On exit it removes each listener with the returned EventListener, preventing duplicate handling after a view is opened again.

import Combine
import Foundation
import WuKongEasySDK

@MainActor
final class EasyChatClient: ObservableObject {
    @Published private(set) var isConnected = false
    @Published private(set) var messages: [Message] = []

    private var sdk: WuKongEasySDK?
    private var listeners: [EventListener] = []

    func start(with bootstrap: IMBootstrap) async throws {
        stop()

        let config = try WuKongConfig(
            serverUrl: bootstrap.websocketUrl,
            uid: bootstrap.uid,
            token: bootstrap.token,
            connectionTimeout: 15,
            requestTimeout: 15,
            maxReconnectAttempts: 5,
            enableDebugLogging: false,
            logLevel: .error,
            enableJsonLogging: false // Also disable JSON summaries in production.
        )
        let sdk = WuKongEasySDK(config: config)

        listeners.append(sdk.onConnect { [weak self] _ in
            Task { @MainActor in self?.isConnected = true }
        })
        listeners.append(sdk.onDisconnect { [weak self] _ in
            Task { @MainActor in
                self?.isConnected = false
                print("WuKongEasySDK disconnected")
            }
        })
        listeners.append(sdk.onMessage { [weak self] message in
            Task { @MainActor in
                guard self?.messages.contains(where: { $0.messageId == message.messageId }) == false
                else { return }
                self?.messages.append(message)
            }
        })
        listeners.append(sdk.onError { _ in
            print("WuKongEasySDK operation failed")
        })

        self.sdk = sdk
        do {
            try await sdk.connect()
        } catch {
            stop() // Release the socket and listeners after timeout, auth, or network failure.
            throw error
        }
    }

    func sendText(to uid: String, text: String) async throws -> SendResult {
        guard let sdk, isConnected else { throw WuKongError.notConnected }

        let payload: MessagePayload = [
            "type": 1,
            "version": 1,
            "content": text
        ]
        return try await sdk.send(
            channelId: uid,
            channelType: .person,
            payload: payload
        )
    }

    func stop() {
        if let sdk {
            listeners.forEach { sdk.removeListener($0) }
            sdk.disconnect()
        }
        listeners.removeAll()
        sdk = nil
        isConnected = false
    }

    deinit {
        // The view or application lifecycle should still call stop() explicitly.
    }
}

Do not discard the anonymous closure passed to onMessage: removeListener needs the listener object returned during registration. The sample bounds connection and request handling at 15 seconds and calls stop() after failure; automatic reconnect is capped at 5 attempts, while the UI remains not ready. In SwiftUI, let a higher-level @StateObject own EasyChatClient and call stop() on logout or when the application-level owner exits.

Step 4: send the first message

The device flag and payload shape are compatible with the current server. After product login, obtain Alice's IMBootstrap and start the client:

let aliceBootstrap = try await productAPI.fetchIMBootstrap()
try await chatClient.start(with: aliceBootstrap)

_ = try await chatClient.sendText(
    to: "bob",
    text: "Hello from iOS EasySDK"
)
print("SEND completed")

The returned SendResult means Alice received a server result for the send. Bob must independently observe the same product payload through onMessage; a local list insertion is not proof of peer receipt.

Step 5: accept with Alice and Bob

  1. Sign in as Alice and Bob on two devices or in two independent app processes.
  2. Enable send only after both sides observe onConnect.
  3. Have Alice send to the person Channel bob, retaining messageId and messageSeq.
  4. On Bob, verify fromUid == "alice", the Channel, and the payload in onMessage.
  5. Send from Bob to Alice and prove the reverse direction.
  6. Leave the view or sign out, call stop(), and confirm that no duplicate listener or background connection remains.

That exact source ran against the same WuKongIM revision on an iPhone 16 / iOS 18.3 Simulator, completed bidirectional messaging, and rendered content and timestamps correctly. The CocoaPods 1.1.1 artifact then completed bidirectional messaging and disconnect on both local and hosted iOS Simulators. Retain the server and SDK revisions, Podfile.lock, device, and network evidence. Include physical-device execution, network recovery, offline synchronization, and the UI-ready state in your Release Checks.

Troubleshooting

  • The compiler says the API requires iOS 15: raise the deployment target to iOS 15; do not rely only on the lower platform declaration in Package.swift.
  • Connection or authentication fails: reach the returned address from the device network, inspect WSS, certificates, and proxy Upgrade, then refresh the short-lived token. Never return a container-only address to the phone.
  • SEND or RECV cannot decode the payload: confirm the server includes the EasySDK JSON-RPC compatibility implementation and the proxy does not rewrite messages. The pinned release sends objects; the server accepts object and Base64 input and emits object RECV.
  • The device category is unexpected: ensure product code has not overridden .app, then compare APP 0, WEB 1, and PC 2; do not carry forward an older literal.
  • Messages appear more than once: register listeners only once and merge realtime and later synchronized results by messageId.
  • Alice gets a send result but Bob sees nothing: inspect Alice's send result, Bob's realtime connection, and the product's later offline-sync path separately. Do not automatically resend a message that may already be committed.

Before production

  • Use wss:// and verify certificates, proxy Upgrade, timeouts, and network recovery from a physical device.
  • Prove that invalid, expired, and revoked tokens are rejected; a successful /user/token call is not a CONNECT-validation receipt.
  • Diagnostics are disabled by default in v1.1.1. Keep both enableDebugLogging and enableJsonLogging set to false in production, then use non-production canaries to confirm that device logs and crash reports omit tokens, payloads, and complete JSON-RPC data.
  • Retain the Release build, iOS version, device, network, and server revision from the run, then use Integration Acceptance for offline behavior, push, capacity, and rollback.

Next

Return to the WuKongEasySDK overview, or continue with Messaging, Custom Messages, and the Release Checks.

On this page