diff --git a/example/swift/.gitignore b/example/swift/.gitignore new file mode 100644 index 00000000..4737be59 --- /dev/null +++ b/example/swift/.gitignore @@ -0,0 +1,3 @@ +xcuserdata/ +*workspace/ +td/ diff --git a/example/swift/README.md b/example/swift/README.md new file mode 100644 index 00000000..bbaa3a8c --- /dev/null +++ b/example/swift/README.md @@ -0,0 +1,12 @@ +# TDLib swift MacOS example + +TDLib should be prebuilt and installed to local subdirectory `td/`: +``` +cd +mkdir build +cd build +cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX:PATH=../example/swift/td .. +cmake --build . --target install +``` + +Then you can open and build the exampe with XCode. diff --git a/example/swift/src/main.swift b/example/swift/src/main.swift new file mode 100644 index 00000000..8e53cb74 --- /dev/null +++ b/example/swift/src/main.swift @@ -0,0 +1,174 @@ +import Foundation + +class TdClient { + typealias Client = UnsafeMutableRawPointer + var client = td_json_client_create()! + let tdlibMainLoop = DispatchQueue(label: "tdlib") + let tdlibQueryQueue = DispatchQueue(label: "tdlibQuery") + var queryF = Dictionary)->()>() + var updateF : ((Dictionary)->())? + var queryId : Int64 = 0 + var closing = false + + func queryAsync(query: [String: Any], f: ((Dictionary)->())? = nil) { + tdlibQueryQueue.async { + var newQuery = query + + if f != nil { + let nextQueryId = self.queryId + 1 + newQuery["@extra"] = nextQueryId + self.queryF[nextQueryId] = f + self.queryId = nextQueryId + } + td_json_client_send(self.client, to_json(newQuery)) + } + } + + func querySync(query: [String: Any]) -> Dictionary { + let semaphore = DispatchSemaphore(value:0) + var result = Dictionary() + queryAsync(query: query) { + result = $0 + semaphore.signal() + } + semaphore.wait() + return result + + } + + func close() { + closing = true + } + init() { + } + deinit { + td_json_client_destroy(client) + } + func run(updateHandler: @escaping (Dictionary)->()) { + updateF = updateHandler + tdlibMainLoop.async { [weak self] in + while (true) { + if let s = self { + if s.closing { + break + } + if let res = td_json_client_receive(s.client,10) { + let event = String(cString: res) + s.queryResultAsync(event) + } + } else { + break + } + } + } + } + + private func queryResultAsync(_ result: String) { + tdlibQueryQueue.async { + let json = try? JSONSerialization.jsonObject(with: result.data(using: .utf8)!, options:[]) + if let dictionary = json as? [String:Any] { + if let extra = dictionary["@extra"] as? Int64 { + let index = self.queryF.index(forKey: extra)! + self.queryF[index].value(dictionary) + self.queryF.remove(at: index) + } else { + self.updateF!(dictionary) + } + } + } + } + + +} + +// Just an example of usage +func to_json(_ obj: Any) -> String { + do { + let obj = try JSONSerialization.data(withJSONObject: obj) + return String(data: obj, encoding: .utf8)! + } catch { + return "" + } + +} +func myReadLine() -> String { + while (true) { + if let line = readLine() { + return line + } + } +} + + +td_set_log_verbosity_level(2); +var client = TdClient() +func updateAuthorizationState(authState : Dictionary) { + switch(authState["@type"] as! String) { + case "authorizationStateWaitTdlibParameters": + client.queryAsync(query:[ + "@type":"setTdlibParameters", + "parameters":[ + "use_message_database":true, + "use_secret_chats":true, + "api_id":94575, + "api_hash":"a3406de8d171bb422bb6ddf3bbd800e2", + "system_language_code":"en", + "device_model":"Desktop", + "system_version":"Unknown", + "application_version":"1.0", + "enable_storage_optimizer":true + ] + ]); + case "authorizationStateWaitEncryptionKey": + client.queryAsync(query: ["@type":"checkDatabaseEncryptionKey", "key":"cucumber"]) + + case "authorizationStateWaitPhoneNumber": + print("Enter your phone: ") + let phone = myReadLine() + client.queryAsync(query:["@type":"setAuthenticationPhoneNumber", "phone_number":phone], f:checkAuthError) + + case "authorizationStateWaitCode": + var first_name : String = "" + var last_name: String = "" + var code : String = "" + if let is_registered = authState["is_registered"] as? Bool, is_registered { + } else { + print("Enter your first name : ") + first_name = myReadLine() + print("Enter your last name: ") + last_name = myReadLine() + } + print("Enter (sms) code: ") + code = myReadLine() + client.queryAsync(query:["@type":"checkAuthenticationCode", "code":code, "first_name":first_name, "last_name":last_name], f:checkAuthError) + case "authorizationStateWaitPassword": + print("Enter password: ") + let password = myReadLine() + client.queryAsync(query:["@type":"checkAuthenticationPassword", "password":password], f:checkAuthError) + case "authorizationStateReady": + () + default: + assert(false, "TODO: Unknown auth state "); + + } +} + +func checkAuthError(error: Dictionary) { + if (error["@type"] as! String == "error") { + client.queryAsync(query:["@type":"getAuthorizationState"], f:updateAuthorizationState) + } +} + +client.run { + let update = $0 + print(update) + if update["@type"] as! String == "updateAuthorizationState" { + updateAuthorizationState(authState: update["authorization_state"] as! Dictionary) + } +} + +while true { + sleep(1) +} + + diff --git a/example/swift/src/td-Bridging-Header.h b/example/swift/src/td-Bridging-Header.h new file mode 100644 index 00000000..278d19a6 --- /dev/null +++ b/example/swift/src/td-Bridging-Header.h @@ -0,0 +1,12 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// + +#include "td/telegram/td_json_client.h" +#include "td/telegram/td_log.h" diff --git a/example/swift/td.xcodeproj/project.pbxproj b/example/swift/td.xcodeproj/project.pbxproj new file mode 100644 index 00000000..7b774c42 --- /dev/null +++ b/example/swift/td.xcodeproj/project.pbxproj @@ -0,0 +1,310 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 1F65E3A42031BF6A00F79763 /* libtdjson.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 1F65E3A32031BF6A00F79763 /* libtdjson.dylib */; }; + 1F65E3A92031C0F000F79763 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F65E3A82031C0F000F79763 /* main.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 1FCE2CEF1EC5E1B50061661A /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1F65E3A32031BF6A00F79763 /* libtdjson.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libtdjson.dylib; path = td/lib/libtdjson.dylib; sourceTree = ""; }; + 1F65E3A82031C0F000F79763 /* main.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = main.swift; path = src/main.swift; sourceTree = SOURCE_ROOT; }; + 1F65E3AA2031C14300F79763 /* td-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "td-Bridging-Header.h"; path = "src/td-Bridging-Header.h"; sourceTree = SOURCE_ROOT; }; + 1FCE2CF11EC5E1B50061661A /* td */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = td; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 1FCE2CEE1EC5E1B50061661A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 1F65E3A42031BF6A00F79763 /* libtdjson.dylib in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 1FCE2CE81EC5E1B50061661A = { + isa = PBXGroup; + children = ( + 1FCE2CF31EC5E1B50061661A /* src */, + 1FCE2CF21EC5E1B50061661A /* Products */, + 1FCE2CFB1EC5E1EE0061661A /* Frameworks */, + ); + sourceTree = ""; + }; + 1FCE2CF21EC5E1B50061661A /* Products */ = { + isa = PBXGroup; + children = ( + 1FCE2CF11EC5E1B50061661A /* td */, + ); + name = Products; + sourceTree = ""; + }; + 1FCE2CF31EC5E1B50061661A /* src */ = { + isa = PBXGroup; + children = ( + 1F65E3AA2031C14300F79763 /* td-Bridging-Header.h */, + 1F65E3A82031C0F000F79763 /* main.swift */, + ); + name = src; + path = td; + sourceTree = ""; + }; + 1FCE2CFB1EC5E1EE0061661A /* Frameworks */ = { + isa = PBXGroup; + children = ( + 1F65E3A32031BF6A00F79763 /* libtdjson.dylib */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 1FCE2CF01EC5E1B50061661A /* td */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1FCE2CF81EC5E1B50061661A /* Build configuration list for PBXNativeTarget "td" */; + buildPhases = ( + 1FCE2CED1EC5E1B50061661A /* Sources */, + 1FCE2CEE1EC5E1B50061661A /* Frameworks */, + 1FCE2CEF1EC5E1B50061661A /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = td; + productName = td; + productReference = 1FCE2CF11EC5E1B50061661A /* td */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 1FCE2CE91EC5E1B50061661A /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0830; + LastUpgradeCheck = 0920; + ORGANIZATIONNAME = "Arseny Smirnov "; + TargetAttributes = { + 1FCE2CF01EC5E1B50061661A = { + CreatedOnToolsVersion = 8.3.2; + LastSwiftMigration = 0920; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 1FCE2CEC1EC5E1B50061661A /* Build configuration list for PBXProject "td" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 1FCE2CE81EC5E1B50061661A; + productRefGroup = 1FCE2CF21EC5E1B50061661A /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 1FCE2CF01EC5E1B50061661A /* td */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 1FCE2CED1EC5E1B50061661A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1F65E3A92031C0F000F79763 /* main.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 1FCE2CF61EC5E1B50061661A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.12; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_SWIFT3_OBJC_INFERENCE = Default; + }; + name = Debug; + }; + 1FCE2CF71EC5E1B50061661A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.12; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_SWIFT3_OBJC_INFERENCE = Default; + }; + name = Release; + }; + 1FCE2CF91EC5E1B50061661A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks $(PROJECT_DIR)/lib"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/td/lib", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "src/td-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_SWIFT3_OBJC_INFERENCE = Default; + SWIFT_VERSION = 4.0; + USER_HEADER_SEARCH_PATHS = td/include/; + }; + name = Debug; + }; + 1FCE2CFA1EC5E1B50061661A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks $(PROJECT_DIR)/lib"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/td/lib", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "src/td-Bridging-Header.h"; + SWIFT_SWIFT3_OBJC_INFERENCE = Default; + SWIFT_VERSION = 4.0; + USER_HEADER_SEARCH_PATHS = td/include/; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1FCE2CEC1EC5E1B50061661A /* Build configuration list for PBXProject "td" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1FCE2CF61EC5E1B50061661A /* Debug */, + 1FCE2CF71EC5E1B50061661A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1FCE2CF81EC5E1B50061661A /* Build configuration list for PBXNativeTarget "td" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1FCE2CF91EC5E1B50061661A /* Debug */, + 1FCE2CFA1EC5E1B50061661A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 1FCE2CE91EC5E1B50061661A /* Project object */; +}