#!/usr/bin/env bash set -euo pipefail repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" out_dir="${1:-/private/tmp/tele-ios-ffi-swift-smoke}" lib_path="${repo_root}/target/release/libtele_ios_ffi.a" if [[ -z "${DEVELOPER_DIR:-}" && -d /Applications/Xcode.app/Contents/Developer ]]; then export DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer fi cd "${repo_root}" case "${out_dir}" in "" | "/" | "/tmp" | "/private" | "/private/tmp") printf 'Refusing unsafe output directory: %s\n' "${out_dir}" >&2 exit 2 ;; esac cargo build \ -p tele-ios-ffi \ --no-default-features \ --features standalone-fake \ --release rm -rf "${out_dir}" mkdir -p "${out_dir}/Swift" "${out_dir}/Headers" cargo run -p uniffi-bindgen-swift -- "${lib_path}" "${out_dir}/Swift" --swift-sources cargo run -p uniffi-bindgen-swift -- "${lib_path}" "${out_dir}/Headers" --headers cargo run -p uniffi-bindgen-swift -- "${lib_path}" "${out_dir}/Headers" \ --modulemap \ --module-name tele_ios_ffiFFI \ --modulemap-filename module.modulemap cat > "${out_dir}/Smoke.swift" <<'SWIFT' import Foundation @main struct Smoke { static func main() throws { func require(_ condition: @autoclosure () -> Bool, _ message: String) { if !condition() { fatalError(message) } } let session = try createSession(config: IosSessionConfig( accountId: "fake", displayName: "Fake", databasePath: "/tmp/tele-ios-ffi-swift-smoke-db", useFakeTdlib: true )) let chats = try session.loadChats(limit: 20) require(chats.count == 1, "expected one fake chat") let chat = chats[0] let history = try session.loadHistory(chatId: chat.id, limit: 20) require(history.first?.text == "Hello from fake TDLib", "expected seeded history") let pinned = try session.pinnedMessages(chatId: chat.id) require(pinned.first?.text == "Hello from fake TDLib", "expected pinned message") let photo = try session.downloadPhoto(fileId: 100) require(photo.path == "/tmp/fake-photo.jpg", "expected downloaded photo path") let voice = try session.downloadVoice(fileId: 200) require(voice.path == "/tmp/fake-voice.ogg", "expected downloaded voice path") try session.setDraft(chatId: chat.id, text: "Draft from Swift FFI") let draftEvents = session.pollEvents() require(draftEvents.contains { if case let .draftChanged(draft) = $0 { return draft.text == "Draft from Swift FFI" } return false }, "expected draftChanged event") let sent = try session.sendMessage(chatId: chat.id, text: "Hi from Swift FFI", replyToMessageId: nil) require(sent.text == "Hi from Swift FFI", "expected sent message text") let copiedText = try session.copyPayload(chatId: chat.id, messageId: sent.id) require(copiedText == "Hi from Swift FFI", "expected copy payload") let reactions = try session.react(chatId: chat.id, messageId: sent.id, reaction: "+1") require(reactions.first?.emoji == "+1", "expected reaction") let results = try session.searchMessages(chatId: chat.id, query: "Swift FFI") require(results.count == 1, "expected search result") session.simulateIncomingMessage(chatId: chat.id, text: "Incoming from Swift smoke", senderName: "Bob") let events = session.pollEvents() require(events.contains { if case .messageAdded = $0 { return true }; return false }, "expected messageAdded event") require(events.contains { if case .incomingNotificationCandidate = $0 { return true }; return false }, "expected notification candidate") print("tele-ios-ffi Swift smoke passed") } } SWIFT swiftc \ -parse-as-library \ -module-cache-path "${out_dir}/module-cache" \ -I "${out_dir}/Headers" \ "${out_dir}/Swift/tele_ios_ffi.swift" \ "${out_dir}/Smoke.swift" \ "${lib_path}" \ -o "${out_dir}/smoke" "${out_dir}/smoke"