From ab9d8eb52b76b7fd77b1fc3f03ab9ae36c4b4fc4 Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Tue, 12 Apr 2022 16:36:31 +0200 Subject: [PATCH] feat: Initial apple platforms support Co-authored-by: Stefan Ceriu Co-authored-by: Benjamin Kampmann --- .gitignore | 4 + .../MatrixRustSDK.xcodeproj/project.pbxproj | 632 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcschemes/MatrixRustSDK.xcscheme | 98 +++ .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 98 +++ .../Assets.xcassets/Contents.json | 6 + .../MatrixRustSDK/ClientError+Error.swift | 11 + .../MatrixRustSDK/ContentView.swift | 21 + apple/MatrixRustSDK/MatrixRustSDK/Info.plist | 5 + .../MatrixRustSDK-Bridging-Header.h | 5 + .../MatrixRustSDK/MatrixRustSDK.entitlements | 10 + .../MatrixRustSDK/MatrixRustSDKApp.swift | 17 + .../MatrixRustSDKTests.swift | 21 + .../MatrixRustSDKUITests.swift | 42 ++ apple/MatrixRustSDK/README.md | 39 ++ apple/MatrixRustSDK/build_xcframework.sh | 94 +++ .../MatrixRustSDK/debug_build_xcframework.sh | 58 ++ crates/matrix-sdk-ffi/Cargo.toml | 38 ++ crates/matrix-sdk-ffi/README.md | 13 + crates/matrix-sdk-ffi/build.rs | 3 + crates/matrix-sdk-ffi/src/api.udl | 137 ++++ crates/matrix-sdk-ffi/src/backward_stream.rs | 42 ++ crates/matrix-sdk-ffi/src/client.rs | 163 +++++ crates/matrix-sdk-ffi/src/lib.rs | 110 +++ crates/matrix-sdk-ffi/src/messages.rs | 242 +++++++ crates/matrix-sdk-ffi/src/room.rs | 143 ++++ 28 files changed, 2078 insertions(+) create mode 100644 apple/MatrixRustSDK/MatrixRustSDK.xcodeproj/project.pbxproj create mode 100644 apple/MatrixRustSDK/MatrixRustSDK.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 apple/MatrixRustSDK/MatrixRustSDK.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 apple/MatrixRustSDK/MatrixRustSDK.xcodeproj/xcshareddata/xcschemes/MatrixRustSDK.xcscheme create mode 100644 apple/MatrixRustSDK/MatrixRustSDK/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 apple/MatrixRustSDK/MatrixRustSDK/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 apple/MatrixRustSDK/MatrixRustSDK/Assets.xcassets/Contents.json create mode 100644 apple/MatrixRustSDK/MatrixRustSDK/ClientError+Error.swift create mode 100644 apple/MatrixRustSDK/MatrixRustSDK/ContentView.swift create mode 100644 apple/MatrixRustSDK/MatrixRustSDK/Info.plist create mode 100644 apple/MatrixRustSDK/MatrixRustSDK/MatrixRustSDK-Bridging-Header.h create mode 100644 apple/MatrixRustSDK/MatrixRustSDK/MatrixRustSDK.entitlements create mode 100644 apple/MatrixRustSDK/MatrixRustSDK/MatrixRustSDKApp.swift create mode 100644 apple/MatrixRustSDK/MatrixRustSDKTests/MatrixRustSDKTests.swift create mode 100644 apple/MatrixRustSDK/MatrixRustSDKUITests/MatrixRustSDKUITests.swift create mode 100644 apple/MatrixRustSDK/README.md create mode 100755 apple/MatrixRustSDK/build_xcframework.sh create mode 100755 apple/MatrixRustSDK/debug_build_xcframework.sh create mode 100644 crates/matrix-sdk-ffi/Cargo.toml create mode 100644 crates/matrix-sdk-ffi/README.md create mode 100644 crates/matrix-sdk-ffi/build.rs create mode 100644 crates/matrix-sdk-ffi/src/api.udl create mode 100644 crates/matrix-sdk-ffi/src/backward_stream.rs create mode 100644 crates/matrix-sdk-ffi/src/client.rs create mode 100644 crates/matrix-sdk-ffi/src/lib.rs create mode 100644 crates/matrix-sdk-ffi/src/messages.rs create mode 100644 crates/matrix-sdk-ffi/src/room.rs diff --git a/.gitignore b/.gitignore index f12193193..a6abb2fb4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,10 @@ Cargo.lock target +generated master.zip emsdk-* .idea/ + +## User settings +xcuserdata/ .vscode/ diff --git a/apple/MatrixRustSDK/MatrixRustSDK.xcodeproj/project.pbxproj b/apple/MatrixRustSDK/MatrixRustSDK.xcodeproj/project.pbxproj new file mode 100644 index 000000000..04b66cc16 --- /dev/null +++ b/apple/MatrixRustSDK/MatrixRustSDK.xcodeproj/project.pbxproj @@ -0,0 +1,632 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 55; + objects = { + +/* Begin PBXBuildFile section */ + 181AA19B27B52AB40005F102 /* MatrixSDKFFI.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 181AA19A27B52AB40005F102 /* MatrixSDKFFI.xcframework */; }; + 181AA19C27B52AB40005F102 /* MatrixSDKFFI.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 181AA19A27B52AB40005F102 /* MatrixSDKFFI.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 189A89BA27B40BBF0048B0A5 /* sdk.swift in Sources */ = {isa = PBXBuildFile; fileRef = 189A89B927B40BBF0048B0A5 /* sdk.swift */; }; + 189A89C827B4201C0048B0A5 /* ClientError+Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = 189A89C727B4201C0048B0A5 /* ClientError+Error.swift */; }; + 18CE89D827B2939900CA89E1 /* MatrixRustSDKApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18CE89D727B2939900CA89E1 /* MatrixRustSDKApp.swift */; }; + 18CE89DA27B2939900CA89E1 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18CE89D927B2939900CA89E1 /* ContentView.swift */; }; + 18CE89DC27B2939A00CA89E1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 18CE89DB27B2939A00CA89E1 /* Assets.xcassets */; }; + 18CE89E927B2939A00CA89E1 /* MatrixRustSDKTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18CE89E827B2939A00CA89E1 /* MatrixRustSDKTests.swift */; }; + 18CE89F327B2939A00CA89E1 /* MatrixRustSDKUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18CE89F227B2939A00CA89E1 /* MatrixRustSDKUITests.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 18CE89E527B2939A00CA89E1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 18CE89CC27B2939900CA89E1 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 18CE89D327B2939900CA89E1; + remoteInfo = MatrixRustSDK; + }; + 18CE89EF27B2939A00CA89E1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 18CE89CC27B2939900CA89E1 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 18CE89D327B2939900CA89E1; + remoteInfo = MatrixRustSDK; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 18CE8A1F27B2941600CA89E1 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 181AA19C27B52AB40005F102 /* MatrixSDKFFI.xcframework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 181AA19927B52AA60005F102 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; + 181AA19A27B52AB40005F102 /* MatrixSDKFFI.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = MatrixSDKFFI.xcframework; path = ../../generated/MatrixSDKFFI.xcframework; sourceTree = ""; }; + 189A899E27B2BADA0048B0A5 /* build_xcframework.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = build_xcframework.sh; sourceTree = SOURCE_ROOT; }; + 189A89B927B40BBF0048B0A5 /* sdk.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = sdk.swift; path = ../../../generated/swift/sdk.swift; sourceTree = ""; }; + 189A89C327B417CA0048B0A5 /* MatrixRustSDK-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "MatrixRustSDK-Bridging-Header.h"; sourceTree = ""; }; + 189A89C727B4201C0048B0A5 /* ClientError+Error.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ClientError+Error.swift"; sourceTree = ""; }; + 18CE89D427B2939900CA89E1 /* MatrixRustSDK.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MatrixRustSDK.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 18CE89D727B2939900CA89E1 /* MatrixRustSDKApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatrixRustSDKApp.swift; sourceTree = ""; }; + 18CE89D927B2939900CA89E1 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 18CE89DB27B2939A00CA89E1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 18CE89E427B2939A00CA89E1 /* MatrixRustSDKTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MatrixRustSDKTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 18CE89E827B2939A00CA89E1 /* MatrixRustSDKTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatrixRustSDKTests.swift; sourceTree = ""; }; + 18CE89EE27B2939A00CA89E1 /* MatrixRustSDKUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MatrixRustSDKUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 18CE89F227B2939A00CA89E1 /* MatrixRustSDKUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatrixRustSDKUITests.swift; sourceTree = ""; }; + 18CE8A0127B293A900CA89E1 /* MatrixRustSDK.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = MatrixRustSDK.entitlements; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 18CE89D127B2939900CA89E1 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 181AA19B27B52AB40005F102 /* MatrixSDKFFI.xcframework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 18CE89E127B2939A00CA89E1 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 18CE89EB27B2939A00CA89E1 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 189A89AB27B2E16B0048B0A5 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 181AA19A27B52AB40005F102 /* MatrixSDKFFI.xcframework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 189A89B827B40BB10048B0A5 /* Generated */ = { + isa = PBXGroup; + children = ( + 189A89B927B40BBF0048B0A5 /* sdk.swift */, + ); + name = Generated; + sourceTree = ""; + }; + 18CE89CB27B2939900CA89E1 = { + isa = PBXGroup; + children = ( + 18CE89D627B2939900CA89E1 /* MatrixRustSDK */, + 18CE89E727B2939A00CA89E1 /* MatrixRustSDKTests */, + 18CE89F127B2939A00CA89E1 /* MatrixRustSDKUITests */, + 18CE89D527B2939900CA89E1 /* Products */, + 189A89AB27B2E16B0048B0A5 /* Frameworks */, + ); + sourceTree = ""; + }; + 18CE89D527B2939900CA89E1 /* Products */ = { + isa = PBXGroup; + children = ( + 18CE89D427B2939900CA89E1 /* MatrixRustSDK.app */, + 18CE89E427B2939A00CA89E1 /* MatrixRustSDKTests.xctest */, + 18CE89EE27B2939A00CA89E1 /* MatrixRustSDKUITests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 18CE89D627B2939900CA89E1 /* MatrixRustSDK */ = { + isa = PBXGroup; + children = ( + 181AA19927B52AA60005F102 /* Info.plist */, + 189A899E27B2BADA0048B0A5 /* build_xcframework.sh */, + 189A89B827B40BB10048B0A5 /* Generated */, + 18CE89D727B2939900CA89E1 /* MatrixRustSDKApp.swift */, + 18CE89D927B2939900CA89E1 /* ContentView.swift */, + 189A89C727B4201C0048B0A5 /* ClientError+Error.swift */, + 18CE8A0127B293A900CA89E1 /* MatrixRustSDK.entitlements */, + 18CE89DB27B2939A00CA89E1 /* Assets.xcassets */, + 189A89C327B417CA0048B0A5 /* MatrixRustSDK-Bridging-Header.h */, + ); + path = MatrixRustSDK; + sourceTree = ""; + }; + 18CE89E727B2939A00CA89E1 /* MatrixRustSDKTests */ = { + isa = PBXGroup; + children = ( + 18CE89E827B2939A00CA89E1 /* MatrixRustSDKTests.swift */, + ); + path = MatrixRustSDKTests; + sourceTree = ""; + }; + 18CE89F127B2939A00CA89E1 /* MatrixRustSDKUITests */ = { + isa = PBXGroup; + children = ( + 18CE89F227B2939A00CA89E1 /* MatrixRustSDKUITests.swift */, + ); + path = MatrixRustSDKUITests; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 18CE89D327B2939900CA89E1 /* MatrixRustSDK */ = { + isa = PBXNativeTarget; + buildConfigurationList = 18CE89F827B2939A00CA89E1 /* Build configuration list for PBXNativeTarget "MatrixRustSDK" */; + buildPhases = ( + 18CE89D027B2939900CA89E1 /* Sources */, + 18CE89D127B2939900CA89E1 /* Frameworks */, + 18CE89D227B2939900CA89E1 /* Resources */, + 18CE8A1F27B2941600CA89E1 /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = MatrixRustSDK; + productName = MatrixRustSDK; + productReference = 18CE89D427B2939900CA89E1 /* MatrixRustSDK.app */; + productType = "com.apple.product-type.application"; + }; + 18CE89E327B2939A00CA89E1 /* MatrixRustSDKTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 18CE89FB27B2939A00CA89E1 /* Build configuration list for PBXNativeTarget "MatrixRustSDKTests" */; + buildPhases = ( + 18CE89E027B2939A00CA89E1 /* Sources */, + 18CE89E127B2939A00CA89E1 /* Frameworks */, + 18CE89E227B2939A00CA89E1 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 18CE89E627B2939A00CA89E1 /* PBXTargetDependency */, + ); + name = MatrixRustSDKTests; + productName = MatrixRustSDKTests; + productReference = 18CE89E427B2939A00CA89E1 /* MatrixRustSDKTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 18CE89ED27B2939A00CA89E1 /* MatrixRustSDKUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 18CE89FE27B2939A00CA89E1 /* Build configuration list for PBXNativeTarget "MatrixRustSDKUITests" */; + buildPhases = ( + 18CE89EA27B2939A00CA89E1 /* Sources */, + 18CE89EB27B2939A00CA89E1 /* Frameworks */, + 18CE89EC27B2939A00CA89E1 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 18CE89F027B2939A00CA89E1 /* PBXTargetDependency */, + ); + name = MatrixRustSDKUITests; + productName = MatrixRustSDKUITests; + productReference = 18CE89EE27B2939A00CA89E1 /* MatrixRustSDKUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 18CE89CC27B2939900CA89E1 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1320; + LastUpgradeCheck = 1320; + TargetAttributes = { + 18CE89D327B2939900CA89E1 = { + CreatedOnToolsVersion = 13.2.1; + LastSwiftMigration = 1320; + }; + 18CE89E327B2939A00CA89E1 = { + CreatedOnToolsVersion = 13.2.1; + TestTargetID = 18CE89D327B2939900CA89E1; + }; + 18CE89ED27B2939A00CA89E1 = { + CreatedOnToolsVersion = 13.2.1; + TestTargetID = 18CE89D327B2939900CA89E1; + }; + }; + }; + buildConfigurationList = 18CE89CF27B2939900CA89E1 /* Build configuration list for PBXProject "MatrixRustSDK" */; + compatibilityVersion = "Xcode 13.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 18CE89CB27B2939900CA89E1; + productRefGroup = 18CE89D527B2939900CA89E1 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 18CE89D327B2939900CA89E1 /* MatrixRustSDK */, + 18CE89E327B2939A00CA89E1 /* MatrixRustSDKTests */, + 18CE89ED27B2939A00CA89E1 /* MatrixRustSDKUITests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 18CE89D227B2939900CA89E1 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 18CE89DC27B2939A00CA89E1 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 18CE89E227B2939A00CA89E1 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 18CE89EC27B2939A00CA89E1 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 18CE89D027B2939900CA89E1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 189A89C827B4201C0048B0A5 /* ClientError+Error.swift in Sources */, + 18CE89DA27B2939900CA89E1 /* ContentView.swift in Sources */, + 189A89BA27B40BBF0048B0A5 /* sdk.swift in Sources */, + 18CE89D827B2939900CA89E1 /* MatrixRustSDKApp.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 18CE89E027B2939A00CA89E1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 18CE89E927B2939A00CA89E1 /* MatrixRustSDKTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 18CE89EA27B2939A00CA89E1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 18CE89F327B2939A00CA89E1 /* MatrixRustSDKUITests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 18CE89E627B2939A00CA89E1 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 18CE89D327B2939900CA89E1 /* MatrixRustSDK */; + targetProxy = 18CE89E527B2939A00CA89E1 /* PBXContainerItemProxy */; + }; + 18CE89F027B2939A00CA89E1 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 18CE89D327B2939900CA89E1 /* MatrixRustSDK */; + targetProxy = 18CE89EF27B2939A00CA89E1 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 18CE89F627B2939A00CA89E1 /* 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++17"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = 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_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + 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; + IPHONEOS_DEPLOYMENT_TARGET = 15.2; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 18CE89F727B2939A00CA89E1 /* 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++17"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = 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_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + 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; + IPHONEOS_DEPLOYMENT_TARGET = 15.2; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 18CE89F927B2939A00CA89E1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = MatrixRustSDK/MatrixRustSDK.entitlements; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = MatrixRustSDK/Info.plist; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = org.matrix.MatrixRustSDK; + PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTS_MACCATALYST = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OBJC_BRIDGING_HEADER = "MatrixRustSDK/MatrixRustSDK-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 18CE89FA27B2939A00CA89E1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = MatrixRustSDK/MatrixRustSDK.entitlements; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = MatrixRustSDK/Info.plist; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = org.matrix.MatrixRustSDK; + PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTS_MACCATALYST = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OBJC_BRIDGING_HEADER = "MatrixRustSDK/MatrixRustSDK-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 18CE89FC27B2939A00CA89E1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.2; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = org.matrix.MatrixRustSDKTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/MatrixRustSDK.app/MatrixRustSDK"; + }; + name = Debug; + }; + 18CE89FD27B2939A00CA89E1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.2; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = org.matrix.MatrixRustSDKTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/MatrixRustSDK.app/MatrixRustSDK"; + }; + name = Release; + }; + 18CE89FF27B2939A00CA89E1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = org.matrix.MatrixRustSDKUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = MatrixRustSDK; + }; + name = Debug; + }; + 18CE8A0027B2939A00CA89E1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = org.matrix.MatrixRustSDKUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = MatrixRustSDK; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 18CE89CF27B2939900CA89E1 /* Build configuration list for PBXProject "MatrixRustSDK" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 18CE89F627B2939A00CA89E1 /* Debug */, + 18CE89F727B2939A00CA89E1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 18CE89F827B2939A00CA89E1 /* Build configuration list for PBXNativeTarget "MatrixRustSDK" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 18CE89F927B2939A00CA89E1 /* Debug */, + 18CE89FA27B2939A00CA89E1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 18CE89FB27B2939A00CA89E1 /* Build configuration list for PBXNativeTarget "MatrixRustSDKTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 18CE89FC27B2939A00CA89E1 /* Debug */, + 18CE89FD27B2939A00CA89E1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 18CE89FE27B2939A00CA89E1 /* Build configuration list for PBXNativeTarget "MatrixRustSDKUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 18CE89FF27B2939A00CA89E1 /* Debug */, + 18CE8A0027B2939A00CA89E1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 18CE89CC27B2939900CA89E1 /* Project object */; +} diff --git a/apple/MatrixRustSDK/MatrixRustSDK.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/apple/MatrixRustSDK/MatrixRustSDK.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/apple/MatrixRustSDK/MatrixRustSDK.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/apple/MatrixRustSDK/MatrixRustSDK.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/apple/MatrixRustSDK/MatrixRustSDK.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/apple/MatrixRustSDK/MatrixRustSDK.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/apple/MatrixRustSDK/MatrixRustSDK.xcodeproj/xcshareddata/xcschemes/MatrixRustSDK.xcscheme b/apple/MatrixRustSDK/MatrixRustSDK.xcodeproj/xcshareddata/xcschemes/MatrixRustSDK.xcscheme new file mode 100644 index 000000000..6644a0efe --- /dev/null +++ b/apple/MatrixRustSDK/MatrixRustSDK.xcodeproj/xcshareddata/xcschemes/MatrixRustSDK.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apple/MatrixRustSDK/MatrixRustSDK/Assets.xcassets/AccentColor.colorset/Contents.json b/apple/MatrixRustSDK/MatrixRustSDK/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 000000000..eb8789700 --- /dev/null +++ b/apple/MatrixRustSDK/MatrixRustSDK/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/apple/MatrixRustSDK/MatrixRustSDK/Assets.xcassets/AppIcon.appiconset/Contents.json b/apple/MatrixRustSDK/MatrixRustSDK/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..9221b9bb1 --- /dev/null +++ b/apple/MatrixRustSDK/MatrixRustSDK/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/apple/MatrixRustSDK/MatrixRustSDK/Assets.xcassets/Contents.json b/apple/MatrixRustSDK/MatrixRustSDK/Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/apple/MatrixRustSDK/MatrixRustSDK/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/apple/MatrixRustSDK/MatrixRustSDK/ClientError+Error.swift b/apple/MatrixRustSDK/MatrixRustSDK/ClientError+Error.swift new file mode 100644 index 000000000..354eccef3 --- /dev/null +++ b/apple/MatrixRustSDK/MatrixRustSDK/ClientError+Error.swift @@ -0,0 +1,11 @@ +// +// ClientError+Error.swift +// MatrixRustSDK +// +// Created by Stefan Ceriu on 09.02.2022. +// + +import Foundation + +// Fixes code generation problem from uniffi +extension ClientError: Error { } diff --git a/apple/MatrixRustSDK/MatrixRustSDK/ContentView.swift b/apple/MatrixRustSDK/MatrixRustSDK/ContentView.swift new file mode 100644 index 000000000..67f95eb10 --- /dev/null +++ b/apple/MatrixRustSDK/MatrixRustSDK/ContentView.swift @@ -0,0 +1,21 @@ +// +// ContentView.swift +// MatrixRustSDK +// +// Created by Stefan Ceriu on 08.02.2022. +// + +import SwiftUI + +struct ContentView: View { + var body: some View { + Text("Hello, Rust!") + .padding() + } +} + +struct ContentView_Previews: PreviewProvider { + static var previews: some View { + ContentView() + } +} diff --git a/apple/MatrixRustSDK/MatrixRustSDK/Info.plist b/apple/MatrixRustSDK/MatrixRustSDK/Info.plist new file mode 100644 index 000000000..0c67376eb --- /dev/null +++ b/apple/MatrixRustSDK/MatrixRustSDK/Info.plist @@ -0,0 +1,5 @@ + + + + + diff --git a/apple/MatrixRustSDK/MatrixRustSDK/MatrixRustSDK-Bridging-Header.h b/apple/MatrixRustSDK/MatrixRustSDK/MatrixRustSDK-Bridging-Header.h new file mode 100644 index 000000000..7cd6f6ea3 --- /dev/null +++ b/apple/MatrixRustSDK/MatrixRustSDK/MatrixRustSDK-Bridging-Header.h @@ -0,0 +1,5 @@ +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// + +#import "sdkFFI.h" diff --git a/apple/MatrixRustSDK/MatrixRustSDK/MatrixRustSDK.entitlements b/apple/MatrixRustSDK/MatrixRustSDK/MatrixRustSDK.entitlements new file mode 100644 index 000000000..ee95ab7e5 --- /dev/null +++ b/apple/MatrixRustSDK/MatrixRustSDK/MatrixRustSDK.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.network.client + + + diff --git a/apple/MatrixRustSDK/MatrixRustSDK/MatrixRustSDKApp.swift b/apple/MatrixRustSDK/MatrixRustSDK/MatrixRustSDKApp.swift new file mode 100644 index 000000000..69d888555 --- /dev/null +++ b/apple/MatrixRustSDK/MatrixRustSDK/MatrixRustSDKApp.swift @@ -0,0 +1,17 @@ +// +// MatrixRustSDKApp.swift +// MatrixRustSDK +// +// Created by Stefan Ceriu on 08.02.2022. +// + +import SwiftUI + +@main +struct MatrixRustSDKApp: App { + var body: some Scene { + WindowGroup { + ContentView() + } + } +} diff --git a/apple/MatrixRustSDK/MatrixRustSDKTests/MatrixRustSDKTests.swift b/apple/MatrixRustSDK/MatrixRustSDKTests/MatrixRustSDKTests.swift new file mode 100644 index 000000000..32ca8b63a --- /dev/null +++ b/apple/MatrixRustSDK/MatrixRustSDKTests/MatrixRustSDKTests.swift @@ -0,0 +1,21 @@ +// +// MatrixRustSDKTests.swift +// MatrixRustSDKTests +// +// Created by Stefan Ceriu on 08.02.2022. +// + +import XCTest +@testable import MatrixRustSDK + +class MatrixRustSDKTests: XCTestCase { + func testExample() throws { + do { + let _ = try loginNewClient(basePath: "", username: "test", password: "test") + } catch ClientError.generic(let message) { + XCTAssertNotNil(message.range(of: "Read-only file system")) + } catch { + XCTFail("Not expecting any other kind of exception") + } + } +} diff --git a/apple/MatrixRustSDK/MatrixRustSDKUITests/MatrixRustSDKUITests.swift b/apple/MatrixRustSDK/MatrixRustSDKUITests/MatrixRustSDKUITests.swift new file mode 100644 index 000000000..4e6558448 --- /dev/null +++ b/apple/MatrixRustSDK/MatrixRustSDKUITests/MatrixRustSDKUITests.swift @@ -0,0 +1,42 @@ +// +// MatrixRustSDKUITests.swift +// MatrixRustSDKUITests +// +// Created by Stefan Ceriu on 08.02.2022. +// + +import XCTest + +class MatrixRustSDKUITests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + + // In UI tests it is usually best to stop immediately when a failure occurs. + continueAfterFailure = false + + // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // UI tests must launch the application that they test. + let app = XCUIApplication() + app.launch() + + // Use recording to get started writing UI tests. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testLaunchPerformance() throws { + if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) { + // This measures how long it takes to launch your application. + measure(metrics: [XCTApplicationLaunchMetric()]) { + XCUIApplication().launch() + } + } + } +} diff --git a/apple/MatrixRustSDK/README.md b/apple/MatrixRustSDK/README.md new file mode 100644 index 000000000..bb49cae34 --- /dev/null +++ b/apple/MatrixRustSDK/README.md @@ -0,0 +1,39 @@ +# Apple platforms support + +This project and build script demonstrate how to create an XCFramework that can be imported into an Xcode project and run on Apple platforms. + +## Building the universal framework + +``` +sh build_xcframework.sh +``` + +**Prerequisites** + +* the Rust toolchain +* UniFFI - `cargo install uniffi_bindge` +* Apple targets (e.g. `rustup target add aarch64-apple-ios`) +* `xcodebuild` command line tool from [Apple](https://developer.apple.com/library/archive/technotes/tn2339/_index.html) +* `lipo` for creating the fat static libs + + +The `build_xcframework.sh` script will go through all the steps required to generate a fully usable `.xcframework`: + +1. compile `matrix-sdk-ffi` libraries for iOS, the iOS simulator, MacOS, and Mac Catalyst under `/target`. Some targets are not part of the standard library and they will be built using the nightly toolchain. +* `lipo` together the libraries for the same platform under `/generated` +* run `uniffi` and generate the C header, module map and swift files +* `xcodebuild` an `xcframework` from the fat static libs and the original iOS one, and add the header and module map to it under `generated/MatrixSDKFFI.xcframework` +* cleanup and delete the generated files except the .xcframework and the swift sources (that aren't part of the framework) + +## Running the Xcode project + +The Xcode project is meant to provide a simple example on how to integrate everything together but also a place to run unit and integration tests from. + +It's pre-configured to link to the generated .xcframework and .swift files so succesfully running the script first is necessary for it to compile. + +It makes the compiled code available to swift by importing the C header through its bridging header. + +Once all the generated components are available running it should be as easy as chosing a platform and clicking run. + +## Distribution +The generated framework and Swift code can be distributed and integrated directly but in order to make things simpler we bundle them together as a Swift package available [TBD](here) \ No newline at end of file diff --git a/apple/MatrixRustSDK/build_xcframework.sh b/apple/MatrixRustSDK/build_xcframework.sh new file mode 100755 index 000000000..9a2474918 --- /dev/null +++ b/apple/MatrixRustSDK/build_xcframework.sh @@ -0,0 +1,94 @@ +#!/usr/bin/env bash +set -eEu + +cd "$(dirname "$0")" + +# Path to the repo root +SRC_ROOT=../.. + +TARGET_DIR="${SRC_ROOT}/target" + +GENERATED_DIR="${SRC_ROOT}/generated" +mkdir -p ${GENERATED_DIR} + +REL_FLAG="--release" +REL_TYPE_DIR="release" + +# Build static libs for all the different architectures + +# iOS +cargo build --locked -p matrix-sdk-ffi ${REL_FLAG} --target "aarch64-apple-ios" + +# MacOS +cargo build --locked -p matrix-sdk-ffi ${REL_FLAG} --target "aarch64-apple-darwin" +cargo build --locked -p matrix-sdk-ffi ${REL_FLAG} --target "x86_64-apple-darwin" + +# iOS Simulator +cargo +nightly build --locked -p matrix-sdk-ffi ${REL_FLAG} --target "aarch64-apple-ios-sim" +cargo build --locked -p matrix-sdk-ffi ${REL_FLAG} --target "x86_64-apple-ios" + +# Mac Catalyst +cargo +nightly build --locked -Z build-std -p matrix-sdk-ffi ${REL_FLAG} --target "aarch64-apple-ios-macabi" +cargo +nightly build --locked -Z build-std -p matrix-sdk-ffi ${REL_FLAG} --target "x86_64-apple-ios-macabi" + +# Lipo together the libraries for the same platform + +# MacOS +lipo -create \ + "${TARGET_DIR}/x86_64-apple-darwin/${REL_TYPE_DIR}/libmatrix_sdk_ffi.a" \ + "${TARGET_DIR}/aarch64-apple-darwin/${REL_TYPE_DIR}/libmatrix_sdk_ffi.a" \ + -output "${GENERATED_DIR}/libmatrix_sdk_ffi_macos.a" + +# iOS Simulator +lipo -create \ + "${TARGET_DIR}/x86_64-apple-ios/${REL_TYPE_DIR}/libmatrix_sdk_ffi.a" \ + "${TARGET_DIR}/aarch64-apple-ios-sim/${REL_TYPE_DIR}/libmatrix_sdk_ffi.a" \ + -output "${GENERATED_DIR}/libmatrix_sdk_ffi_iossimulator.a" + +# Mac Catalyst +lipo -create \ + "${TARGET_DIR}/x86_64-apple-ios-macabi/${REL_TYPE_DIR}/libmatrix_sdk_ffi.a" \ + "${TARGET_DIR}/aarch64-apple-ios-macabi/${REL_TYPE_DIR}/libmatrix_sdk_ffi.a" \ + -output "${GENERATED_DIR}/libmatrix_sdk_ffi_maccatalyst.a" + + +# Generate uniffi files +uniffi-bindgen generate "${SRC_ROOT}/crates/matrix-sdk-ffi/src/api.udl" --language swift --out-dir ${GENERATED_DIR} + +# Move them to the right place +HEADERS_DIR=${GENERATED_DIR}/headers +mkdir -p ${HEADERS_DIR} + +mv ${GENERATED_DIR}/*.h ${GENERATED_DIR}/*.modulemap ${HEADERS_DIR} + +SWIFT_DIR="${GENERATED_DIR}/swift" +mkdir -p ${SWIFT_DIR} + +mv ${GENERATED_DIR}/*.swift ${SWIFT_DIR} + +# Build the xcframework + +if [ -d "${GENERATED_DIR}/MatrixSDKFFI.xcframework" ]; then rm -rf "${GENERATED_DIR}/MatrixSDKFFI.xcframework"; fi + +xcodebuild -create-xcframework \ + -library "${GENERATED_DIR}/libmatrix_sdk_ffi_macos.a" \ + -headers ${HEADERS_DIR} \ + -library "${GENERATED_DIR}/libmatrix_sdk_ffi_iossimulator.a" \ + -headers ${HEADERS_DIR} \ + -library "${GENERATED_DIR}/libmatrix_sdk_ffi_maccatalyst.a" \ + -headers ${HEADERS_DIR} \ + -library "${TARGET_DIR}/aarch64-apple-ios/${REL_TYPE_DIR}/libmatrix_sdk_ffi.a" \ + -headers ${HEADERS_DIR} \ + -output "${GENERATED_DIR}/MatrixSDKFFI.xcframework" + +# Cleanup + +if [ -f "${GENERATED_DIR}/libmatrix_sdk_ffi_macos.a" ]; then rm -rf "${GENERATED_DIR}/libmatrix_sdk_ffi_macos.a"; fi +if [ -f "${GENERATED_DIR}/libmatrix_sdk_ffi_iossimulator.a" ]; then rm -rf "${GENERATED_DIR}/libmatrix_sdk_ffi_iossimulator.a"; fi +if [ -f "${GENERATED_DIR}/libmatrix_sdk_ffi_maccatalyst.a" ]; then rm -rf "${GENERATED_DIR}/libmatrix_sdk_ffi_maccatalyst.a"; fi +if [ -d ${HEADERS_DIR} ]; then rm -rf ${HEADERS_DIR}; fi + +# Debug -> Copy generated files over to ../../../matrix-rust-components-swift +# rsync -a --delete "${GENERATED_DIR}/MatrixSDKFFI.xcframework" "../../../matrix-rust-components-swift/" +# rsync -a --delete "${GENERATED_DIR}/swift/" "../../../matrix-rust-components-swift/Sources/MatrixRustSDK" + diff --git a/apple/MatrixRustSDK/debug_build_xcframework.sh b/apple/MatrixRustSDK/debug_build_xcframework.sh new file mode 100755 index 000000000..0f579c98c --- /dev/null +++ b/apple/MatrixRustSDK/debug_build_xcframework.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +set -eEu + +cd "$(dirname "$0")" + +# Path to the repo root +SRC_ROOT=../.. + +TARGET_DIR="${SRC_ROOT}/target" + +GENERATED_DIR="${SRC_ROOT}/generated" +mkdir -p ${GENERATED_DIR} + +# Release for now. Debug builds cause crashes deep inside the Tokio runtime. +REL_FLAG="--release" +REL_TYPE_DIR="release" + +# Build static the lib for aarch64 simulator + +cargo +nightly build --locked -p matrix-sdk-ffi ${REL_FLAG} --target "aarch64-apple-ios-sim" + +lipo -create \ + "${TARGET_DIR}/aarch64-apple-ios-sim/${REL_TYPE_DIR}/libmatrix_sdk_ffi.a" \ + -output "${GENERATED_DIR}/libmatrix_sdk_ffi_iossimulator.a" + +# Generate uniffi files +uniffi-bindgen generate "${SRC_ROOT}/crates/matrix-sdk-ffi/src/api.udl" --language swift --out-dir ${GENERATED_DIR} + +# Move them to the right place +HEADERS_DIR=${GENERATED_DIR}/headers +mkdir -p ${HEADERS_DIR} + +mv ${GENERATED_DIR}/*.h ${GENERATED_DIR}/*.modulemap ${HEADERS_DIR} + +SWIFT_DIR="${GENERATED_DIR}/swift" +mkdir -p ${SWIFT_DIR} + +mv ${GENERATED_DIR}/*.swift ${SWIFT_DIR} + +# Build the xcframework + +if [ -d "${GENERATED_DIR}/MatrixSDKFFI.xcframework" ]; then rm -rf "${GENERATED_DIR}/MatrixSDKFFI.xcframework"; fi + +xcodebuild -create-xcframework \ + -library "${GENERATED_DIR}/libmatrix_sdk_ffi_iossimulator.a" \ + -headers ${HEADERS_DIR} \ + -output "${GENERATED_DIR}/MatrixSDKFFI.xcframework" + +# Cleanup + +if [ -f "${GENERATED_DIR}/libmatrix_sdk_ffi_iossimulator.a" ]; then rm -rf "${GENERATED_DIR}/libmatrix_sdk_ffi_iossimulator.a"; fi +if [ -d ${HEADERS_DIR} ]; then rm -rf ${HEADERS_DIR}; fi + +# Debug -> Copy generated files over to ../../../matrix-rust-components-swift +echo "$(echo "import MatrixSDKFFIWrapper\n"; cat "${SWIFT_DIR}/sdk.swift")" > "${SWIFT_DIR}/sdk.swift" + +rsync -a --delete "${GENERATED_DIR}/MatrixSDKFFI.xcframework" "../../../matrix-rust-components-swift/" +rsync -a --delete "${GENERATED_DIR}/swift/" "../../../matrix-rust-components-swift/Sources/MatrixRustSDK" diff --git a/crates/matrix-sdk-ffi/Cargo.toml b/crates/matrix-sdk-ffi/Cargo.toml new file mode 100644 index 000000000..e773e95c6 --- /dev/null +++ b/crates/matrix-sdk-ffi/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "matrix-sdk-ffi" +version = "0.1.0" +edition = "2021" +homepage = "https://github.com/matrix-org/matrix-rust-sdk" +keywords = ["matrix", "chat", "messaging", "ffi"] +license = "Apache-2.0" +readme = "README.md" +rust-version = "1.56" +repository = "https://github.com/matrix-org/matrix-rust-sdk" + +[lib] +crate-type = ["cdylib", "staticlib"] + +[features] +default = ["with-runtime"] +with-runtime = ["tokio"] + +[build-dependencies] +uniffi_build = { version = "0.17.0", features = ["builtin-bindgen"] } + +[dependencies] +anyhow = "1.0.51" +extension-trait = "1.0.1" +futures-core = "0.3.17" +futures-util = { version = "0.3.17", default-features = false } +matrix-sdk = { path = "../matrix-sdk" } +once_cell = "1.10.0" +parking_lot = "0.12.0" +sanitize-filename-reader-friendly = "2.2.1" +serde = { version = "1", features = ["derive"] } +serde_json = { version = "1" } +thiserror = "1.0.30" +tokio = { version = "1", optional = true, features = ["rt-multi-thread", "macros"] } +tokio-stream = "0.1.8" +tracing = "0.1.32" +uniffi = "0.17.0" +uniffi_macros = "0.17.0" diff --git a/crates/matrix-sdk-ffi/README.md b/crates/matrix-sdk-ffi/README.md new file mode 100644 index 000000000..ac2311058 --- /dev/null +++ b/crates/matrix-sdk-ffi/README.md @@ -0,0 +1,13 @@ +# FFI bindings for the rust matrix SDK + +This uses [´uniffi´](https://mozilla.github.io/uniffi-rs/Overview.html) to build the matrix bindings for native support and wasm-bindgen for web-browser assembly support. Please refer to the specific section to figure out how to build and use the bindings for your platform. + +## Platforms + +### Swift/iOS sync + + + +### Swift/iOS async + +TBD \ No newline at end of file diff --git a/crates/matrix-sdk-ffi/build.rs b/crates/matrix-sdk-ffi/build.rs new file mode 100644 index 000000000..40ec554ca --- /dev/null +++ b/crates/matrix-sdk-ffi/build.rs @@ -0,0 +1,3 @@ +fn main() { + uniffi_build::generate_scaffolding("./src/api.udl").expect("Building the UDL file failed"); +} diff --git a/crates/matrix-sdk-ffi/src/api.udl b/crates/matrix-sdk-ffi/src/api.udl new file mode 100644 index 000000000..bd34a4872 --- /dev/null +++ b/crates/matrix-sdk-ffi/src/api.udl @@ -0,0 +1,137 @@ +namespace sdk { + [Throws=ClientError] + Client login_new_client(string base_path, string username, string password); + + [Throws=ClientError] + Client guest_client(string base_path, string homeserver); + + [Throws=ClientError] + Client login_with_token(string base_path, string restore_token); + + MediaSource media_source_from_url(string url); +}; + +[Error] +interface ClientError { + Generic(string msg); +}; + +callback interface ClientDelegate { + void did_receive_sync_update(); +}; + +interface Client { + void set_delegate(ClientDelegate? delegate); + + void start_sync(); + + [Throws=ClientError] + string restore_token(); + + boolean is_guest(); + + boolean has_first_synced(); + + boolean is_syncing(); + + [Throws=ClientError] + string user_id(); + + [Throws=ClientError] + string display_name(); + + [Throws=ClientError] + string avatar_url(); + + [Throws=ClientError] + string device_id(); + + sequence rooms(); + + [Throws=ClientError] + sequence load_image(MediaSource source); +}; + +callback interface RoomDelegate { + void did_receive_message(AnyMessage message); +}; + +interface Room { + void set_delegate(RoomDelegate? delegate); + + string id(); + + [Throws=ClientError] + string display_name(); + + string? avatar_url(); + + [Throws=ClientError] + string? member_avatar_url(string user_id); + + [Throws=ClientError] + string? member_display_name(string user_id); + + boolean is_direct(); + + boolean is_public(); + + boolean is_space(); + + boolean is_encrypted(); + + boolean is_tombstoned(); + + string? name(); + + string? topic(); + + BackwardsStream? start_live_event_listener(); + + void stop_live_event_listener(); +}; + +interface BackwardsStream { + sequence paginate_backwards(u64 count); +}; + +interface AnyMessage { + TextMessage? text_message(); + ImageMessage? image_message(); + NoticeMessage? notice_message(); + EmoteMessage? emote_message(); +}; + +interface BaseMessage { + string id(); + string body(); + string sender(); + u64 origin_server_ts(); +}; + +interface TextMessage { + BaseMessage base_message(); + string? html_body(); +}; + +interface ImageMessage { + BaseMessage base_message(); + MediaSource source(); + u64? width(); + u64? height(); + string? blurhash(); +}; + +interface NoticeMessage { + BaseMessage base_message(); + string? html_body(); +}; + +interface EmoteMessage { + BaseMessage base_message(); + string? html_body(); +}; + +interface MediaSource { + string url(); +}; diff --git a/crates/matrix-sdk-ffi/src/backward_stream.rs b/crates/matrix-sdk-ffi/src/backward_stream.rs new file mode 100644 index 000000000..087c1ad0e --- /dev/null +++ b/crates/matrix-sdk-ffi/src/backward_stream.rs @@ -0,0 +1,42 @@ +use core::pin::Pin; +use std::sync::Arc; + +use futures_core::Stream; +use matrix_sdk::{deserialized_responses::SyncRoomEvent, locks::Mutex}; +use tokio_stream::StreamExt; +use tracing::error; + +use super::{ + messages::{sync_event_to_message, AnyMessage}, + RUNTIME, +}; + +type MsgStream = Pin> + Send>>; + +pub struct BackwardsStream { + stream: Arc>, +} + +impl BackwardsStream { + pub fn new(stream: MsgStream) -> Self { + BackwardsStream { stream: Arc::new(Mutex::new(Box::pin(stream))) } + } + + pub fn paginate_backwards(&self, count: u64) -> Vec> { + let stream = self.stream.clone(); + RUNTIME.block_on(async move { + let mut stream = stream.lock().await; + (&mut *stream) + .take(count as usize) + .filter_map(|r| match r { + Ok(ev) => sync_event_to_message(ev), + Err(e) => { + error!("Pagniation error: {e}"); + None + } + }) + .collect() + .await + }) + } +} diff --git a/crates/matrix-sdk-ffi/src/client.rs b/crates/matrix-sdk-ffi/src/client.rs new file mode 100644 index 000000000..387ae2e10 --- /dev/null +++ b/crates/matrix-sdk-ffi/src/client.rs @@ -0,0 +1,163 @@ +use std::sync::Arc; + +use matrix_sdk::{ + config::SyncSettings, + media::{MediaFormat, MediaRequest}, + ruma::{ + api::client::{ + filter::{FilterDefinition, LazyLoadOptions, RoomEventFilter, RoomFilter}, + sync::sync_events::v3::Filter, + }, + events::room::MediaSource, + }, + Client as MatrixClient, LoopCtrl, +}; +use parking_lot::RwLock; + +use super::{room::Room, ClientState, RestoreToken, RUNTIME}; + +impl std::ops::Deref for Client { + type Target = MatrixClient; + fn deref(&self) -> &MatrixClient { + &self.client + } +} + +pub trait ClientDelegate: Sync + Send { + fn did_receive_sync_update(&self); +} + +#[derive(Clone)] +pub struct Client { + client: MatrixClient, + state: Arc>, + delegate: Arc>>>, +} + +impl Client { + pub fn new(client: MatrixClient, state: ClientState) -> Self { + Client { + client, + state: Arc::new(RwLock::new(state)), + delegate: Arc::new(RwLock::new(None)), + } + } + + pub fn set_delegate(&self, delegate: Option>) { + *self.delegate.write() = delegate; + } + + pub fn start_sync(&self) { + let client = self.client.clone(); + let state = self.state.clone(); + let delegate = self.delegate.clone(); + RUNTIME.spawn(async move { + let mut filter = FilterDefinition::default(); + let mut room_filter = RoomFilter::default(); + let mut event_filter = RoomEventFilter::default(); + + event_filter.lazy_load_options = + LazyLoadOptions::Enabled { include_redundant_members: false }; + room_filter.state = event_filter; + filter.room = room_filter; + + let filter_id = client.get_or_upload_filter("sync", filter).await.unwrap(); + + let sync_settings = SyncSettings::new().filter(Filter::FilterId(&filter_id)); + + client + .sync_with_callback(sync_settings, |_| async { + if !state.read().has_first_synced { + state.write().has_first_synced = true + } + + if state.read().should_stop_syncing { + state.write().is_syncing = false; + return LoopCtrl::Break; + } else if !state.read().is_syncing { + state.write().is_syncing = true; + } + + if let Some(ref delegate) = *delegate.read() { + delegate.did_receive_sync_update() + } + LoopCtrl::Continue + }) + .await; + }); + } + + /// Indication whether we've received a first sync response since + /// establishing the client (in memory) + pub fn has_first_synced(&self) -> bool { + self.state.read().has_first_synced + } + + /// Indication whether we are currently syncing + pub fn is_syncing(&self) -> bool { + self.state.read().has_first_synced + } + + /// Is this a guest account? + pub fn is_guest(&self) -> bool { + self.state.read().is_guest + } + + pub fn restore_token(&self) -> anyhow::Result { + RUNTIME.block_on(async move { + let session = self.client.session().await.expect("Missing session"); + let homeurl = self.client.homeserver().await.into(); + Ok(serde_json::to_string(&RestoreToken { + session, + homeurl, + is_guest: self.state.read().is_guest, + })?) + }) + } + + pub fn rooms(&self) -> Vec> { + self.client.rooms().into_iter().map(|room| Arc::new(Room::new(room))).collect() + } + + pub fn user_id(&self) -> anyhow::Result { + let l = self.client.clone(); + RUNTIME.block_on(async move { + let user_id = l.user_id().await.expect("No User ID found"); + Ok(user_id.to_string()) + }) + } + + pub fn display_name(&self) -> anyhow::Result { + let l = self.client.clone(); + RUNTIME.block_on(async move { + let display_name = l.account().get_display_name().await?.expect("No User ID found"); + Ok(display_name) + }) + } + + pub fn avatar_url(&self) -> anyhow::Result { + let l = self.client.clone(); + RUNTIME.block_on(async move { + let avatar_url = l.account().get_avatar_url().await?.expect("No User ID found"); + Ok(avatar_url.to_string()) + }) + } + + pub fn device_id(&self) -> anyhow::Result { + let l = self.client.clone(); + RUNTIME.block_on(async move { + let device_id = l.device_id().await.expect("No Device ID found"); + Ok(device_id.to_string()) + }) + } + + pub fn load_image(&self, media_source: Arc) -> anyhow::Result> { + let l = self.client.clone(); + let source = (*media_source).clone(); + + RUNTIME.block_on(async move { + Ok(l.get_media_content(&MediaRequest { source, format: MediaFormat::File }, true) + .await?) + }) + } +} diff --git a/crates/matrix-sdk-ffi/src/lib.rs b/crates/matrix-sdk-ffi/src/lib.rs new file mode 100644 index 000000000..6407aca7f --- /dev/null +++ b/crates/matrix-sdk-ffi/src/lib.rs @@ -0,0 +1,110 @@ +// TODO: target-os conditional would be good. + +#![allow(unused_qualifications)] + +pub mod backward_stream; +pub mod client; +pub mod messages; +pub mod room; + +use std::{fs, path, sync::Arc}; + +use client::Client; +use matrix_sdk::{store::make_store_config, Client as MatrixClient, ClientBuilder, Session}; +use once_cell::sync::Lazy; +use sanitize_filename_reader_friendly::sanitize; +use serde::{Deserialize, Serialize}; +use tokio::runtime::Runtime; + +pub static RUNTIME: Lazy = + Lazy::new(|| Runtime::new().expect("Can't start Tokio runtime")); + +pub use matrix_sdk::ruma::{api::client::account::register, UserId}; + +pub use self::{backward_stream::*, client::*, messages::*, room::*}; + +pub fn guest_client(base_path: String, homeurl: String) -> anyhow::Result> { + let builder = new_client_builder(base_path, homeurl.clone())?.homeserver_url(&homeurl); + let mut guest_registration = register::v3::Request::new(); + guest_registration.kind = register::RegistrationKind::Guest; + RUNTIME.block_on(async move { + let client = builder.build().await?; + let register = client.register(guest_registration).await?; + let session = Session { + access_token: register.access_token.expect("no access token given"), + user_id: register.user_id, + device_id: register.device_id.clone().expect("device id is given by server"), + }; + client.restore_login(session).await?; + let c = Client::new(client, ClientState { is_guest: true, ..ClientState::default() }); + Ok(Arc::new(c)) + }) +} + +pub fn login_with_token(base_path: String, restore_token: String) -> anyhow::Result> { + let RestoreToken { session, homeurl, is_guest } = serde_json::from_str(&restore_token)?; + let builder = new_client_builder(base_path, session.user_id.to_string())? + .homeserver_url(&homeurl) + .user_id(&session.user_id); + // First we need to log in. + RUNTIME.block_on(async move { + let client = builder.build().await?; + client.restore_login(session).await?; + let c = Client::new(client, ClientState { is_guest, ..ClientState::default() }); + Ok(Arc::new(c)) + }) +} + +pub fn login_new_client( + base_path: String, + username: String, + password: String, +) -> anyhow::Result> { + let builder = new_client_builder(base_path, username.clone())?; + let user = Box::::try_from(username)?; + // First we need to log in. + RUNTIME.block_on(async move { + let client = builder.user_id(&user).build().await?; + client.login(user, &password, None, None).await?; + let c = Client::new(client, ClientState { is_guest: false, ..ClientState::default() }); + Ok(Arc::new(c)) + }) +} + +fn new_client_builder(base_path: String, home: String) -> anyhow::Result { + let data_path = path::PathBuf::from(base_path).join(sanitize(&home)); + + fs::create_dir_all(&data_path)?; + let store_config = make_store_config(&data_path, None)?; + + Ok(MatrixClient::builder().user_agent("rust-sdk-ios").store_config(store_config)) +} + +#[derive(Default, Debug)] +pub struct ClientState { + is_guest: bool, + has_first_synced: bool, + is_syncing: bool, + should_stop_syncing: bool, +} + +#[derive(Serialize, Deserialize)] +struct RestoreToken { + is_guest: bool, + homeurl: String, + session: Session, +} + +#[derive(thiserror::Error, Debug)] +pub enum ClientError { + #[error("client error: {msg}")] + Generic { msg: String }, +} + +impl From for ClientError { + fn from(e: anyhow::Error) -> ClientError { + ClientError::Generic { msg: e.to_string() } + } +} + +uniffi_macros::include_scaffolding!("api"); diff --git a/crates/matrix-sdk-ffi/src/messages.rs b/crates/matrix-sdk-ffi/src/messages.rs new file mode 100644 index 000000000..533b43d02 --- /dev/null +++ b/crates/matrix-sdk-ffi/src/messages.rs @@ -0,0 +1,242 @@ +use std::sync::Arc; + +use extension_trait::extension_trait; +pub use matrix_sdk::ruma::events::room::MediaSource; +use matrix_sdk::{ + deserialized_responses::SyncRoomEvent, + ruma::events::{ + room::{ + message::{ImageMessageEventContent, MessageFormat, MessageType}, + ImageInfo, + }, + AnySyncMessageLikeEvent, AnySyncRoomEvent, SyncMessageLikeEvent, + }, +}; + +#[derive(Clone)] +pub struct BaseMessage { + id: String, + body: String, + sender: String, + origin_server_ts: u64, +} + +impl BaseMessage { + pub fn id(&self) -> String { + self.id.clone() + } + + pub fn body(&self) -> String { + self.body.clone() + } + + pub fn sender(&self) -> String { + self.sender.clone() + } + + pub fn origin_server_ts(&self) -> u64 { + self.origin_server_ts + } +} + +pub struct TextMessage { + base_message: Arc, + html_body: Option, +} + +impl TextMessage { + pub fn base_message(&self) -> Arc { + self.base_message.clone() + } + + pub fn html_body(&self) -> Option { + self.html_body.clone() + } +} + +pub struct ImageMessage { + base_message: Arc, + source: Arc, + info: Option>, +} + +impl ImageMessage { + pub fn base_message(&self) -> Arc { + self.base_message.clone() + } + + pub fn source(&self) -> Arc { + self.source.clone() + } + + pub fn width(&self) -> Option { + self.info.clone()?.width?.try_into().ok() + } + + pub fn height(&self) -> Option { + self.info.clone()?.height?.try_into().ok() + } + + pub fn blurhash(&self) -> Option { + self.info.clone()?.blurhash + } +} + +pub struct NoticeMessage { + base_message: Arc, + html_body: Option, +} + +impl NoticeMessage { + pub fn base_message(&self) -> Arc { + self.base_message.clone() + } + + pub fn html_body(&self) -> Option { + self.html_body.clone() + } +} + +pub struct EmoteMessage { + base_message: Arc, + html_body: Option, +} + +impl EmoteMessage { + pub fn base_message(&self) -> Arc { + self.base_message.clone() + } + + pub fn html_body(&self) -> Option { + self.html_body.clone() + } +} + +pub struct AnyMessage { + text: Option>, + image: Option>, + notice: Option>, + emote: Option>, +} + +impl AnyMessage { + pub fn text_message(&self) -> Option> { + self.text.clone() + } + + pub fn image_message(&self) -> Option> { + self.image.clone() + } + + pub fn notice_message(&self) -> Option> { + self.notice.clone() + } + + pub fn emote_message(&self) -> Option> { + self.emote.clone() + } +} + +pub fn sync_event_to_message(sync_event: SyncRoomEvent) -> Option> { + match sync_event.event.deserialize() { + Ok(AnySyncRoomEvent::MessageLike(AnySyncMessageLikeEvent::RoomMessage( + SyncMessageLikeEvent::Original(m), + ))) => { + let base_message = Arc::new(BaseMessage { + id: m.event_id.to_string(), + body: m.content.body().to_owned(), + sender: m.sender.to_string(), + origin_server_ts: m.origin_server_ts.as_secs().into(), + }); + + match m.content.msgtype { + MessageType::Image(ImageMessageEventContent { source, info, .. }) => { + let any_message = AnyMessage { + text: None, + image: Some(Arc::new(ImageMessage { + base_message, + source: Arc::new(source), + info, + })), + notice: None, + emote: None, + }; + + Some(Arc::new(any_message)) + } + MessageType::Text(content) => { + let mut html_body: Option = None; + if let Some(formatted_body) = content.formatted { + if formatted_body.format == MessageFormat::Html { + html_body = Some(formatted_body.body); + } + } + + let any_message = AnyMessage { + text: Some(Arc::new(TextMessage { base_message, html_body })), + image: None, + notice: None, + emote: None, + }; + Some(Arc::new(any_message)) + } + MessageType::Notice(content) => { + let mut html_body: Option = None; + if let Some(formatted_body) = content.formatted { + if formatted_body.format == MessageFormat::Html { + html_body = Some(formatted_body.body); + } + } + + let any_message = AnyMessage { + text: None, + image: None, + notice: Some(Arc::new(NoticeMessage { base_message, html_body })), + emote: None, + }; + Some(Arc::new(any_message)) + } + MessageType::Emote(content) => { + let mut html_body: Option = None; + if let Some(formatted_body) = content.formatted { + if formatted_body.format == MessageFormat::Html { + html_body = Some(formatted_body.body); + } + } + + let any_message = AnyMessage { + text: None, + image: None, + notice: None, + emote: Some(Arc::new(EmoteMessage { base_message, html_body })), + }; + Some(Arc::new(any_message)) + } + _ => { + let any_message = AnyMessage { + text: Some(Arc::new(TextMessage { base_message, html_body: None })), + image: None, + notice: None, + emote: None, + }; + Some(Arc::new(any_message)) + } + } + } + _ => None, + } +} + +pub fn media_source_from_url(url: String) -> Arc { + Arc::new(MediaSource::Plain(url.into())) +} + +#[extension_trait] +pub impl MediaSourceExt for MediaSource { + fn url(&self) -> String { + match self { + MediaSource::Plain(url) => url.to_string(), + MediaSource::Encrypted(file) => file.url.to_string(), + } + } +} diff --git a/crates/matrix-sdk-ffi/src/room.rs b/crates/matrix-sdk-ffi/src/room.rs new file mode 100644 index 000000000..b0817878b --- /dev/null +++ b/crates/matrix-sdk-ffi/src/room.rs @@ -0,0 +1,143 @@ +use std::sync::Arc; + +use anyhow::Result; +use futures_util::{pin_mut, StreamExt}; +use matrix_sdk::{room::Room as MatrixRoom, ruma::UserId}; +use parking_lot::RwLock; + +use super::{ + backward_stream::BackwardsStream, + messages::{sync_event_to_message, AnyMessage}, + RUNTIME, +}; + +pub trait RoomDelegate: Sync + Send { + fn did_receive_message(&self, messages: Arc); +} + +pub struct Room { + room: MatrixRoom, + delegate: Arc>>>, + is_listening_to_live_events: Arc>, +} + +impl Room { + pub fn new(room: MatrixRoom) -> Self { + Room { + room, + delegate: Arc::new(RwLock::new(None)), + is_listening_to_live_events: Arc::new(RwLock::new(false)), + } + } + + pub fn set_delegate(&self, delegate: Option>) { + *self.delegate.write() = delegate; + } + + pub fn id(&self) -> String { + self.room.room_id().to_string() + } + + pub fn name(&self) -> Option { + self.room.name() + } + + pub fn display_name(&self) -> Result { + let r = self.room.clone(); + RUNTIME.block_on(async move { Ok(r.display_name().await?) }) + } + + pub fn topic(&self) -> Option { + self.room.topic() + } + + pub fn avatar_url(&self) -> Option { + self.room.avatar_url().map(|m| m.to_string()) + } + + pub fn member_avatar_url(&self, user_id: String) -> Result> { + let room = self.room.clone(); + let user_id = user_id; + RUNTIME.block_on(async move { + let user_id = <&UserId>::try_from(&*user_id).expect("Invalid user id."); + let member = room.get_member(user_id).await?.expect("No user found"); + let avatar_url_string = member.avatar_url().map(|m| m.to_string()); + Ok(avatar_url_string) + }) + } + + pub fn member_display_name(&self, user_id: String) -> Result> { + let room = self.room.clone(); + let user_id = user_id; + RUNTIME.block_on(async move { + let user_id = <&UserId>::try_from(&*user_id).expect("Invalid user id."); + let member = room.get_member(user_id).await?.expect("No user found"); + let avatar_url_string = member.display_name().map(|m| m.to_owned()); + Ok(avatar_url_string) + }) + } + + pub fn is_direct(&self) -> bool { + self.room.is_direct() + } + + pub fn is_public(&self) -> bool { + self.room.is_public() + } + + pub fn is_encrypted(&self) -> bool { + self.room.is_encrypted() + } + + pub fn is_space(&self) -> bool { + self.room.is_space() + } + + pub fn is_tombstoned(&self) -> bool { + self.room.is_tombstoned() + } + + pub fn start_live_event_listener(&self) -> Option> { + if *self.is_listening_to_live_events.read() { + return None; + } + + *self.is_listening_to_live_events.write() = true; + + let room = self.room.clone(); + let delegate = self.delegate.clone(); + let is_listening_to_live_events = self.is_listening_to_live_events.clone(); + + let (forward_stream, backwards) = RUNTIME.block_on(async move { + room.timeline().await.expect("Failed acquiring timeline streams") + }); + + RUNTIME.spawn(async move { + pin_mut!(forward_stream); + + while let Some(sync_event) = forward_stream.next().await { + if !(*is_listening_to_live_events.read()) { + return; + } + + if let Some(delegate) = &*delegate.read() { + if let Some(message) = sync_event_to_message(sync_event) { + delegate.did_receive_message(message) + } + } + } + }); + Some(Arc::new(BackwardsStream::new(Box::pin(backwards)))) + } + + pub fn stop_live_event_listener(&self) { + *self.is_listening_to_live_events.write() = false; + } +} + +impl std::ops::Deref for Room { + type Target = MatrixRoom; + fn deref(&self) -> &MatrixRoom { + &self.room + } +}