feat: Initial apple platforms support

Co-authored-by: Stefan Ceriu <stefan.ceriu@gmail.com>
Co-authored-by: Benjamin Kampmann <ben.kampmann@gmail.com>
This commit is contained in:
Jonas Platte
2022-04-12 16:36:31 +02:00
committed by Jonas Platte
parent 41799a08ed
commit ab9d8eb52b
28 changed files with 2078 additions and 0 deletions
+4
View File
@@ -1,6 +1,10 @@
Cargo.lock
target
generated
master.zip
emsdk-*
.idea/
## User settings
xcuserdata/
.vscode/
@@ -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 = "<group>"; };
181AA19A27B52AB40005F102 /* MatrixSDKFFI.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = MatrixSDKFFI.xcframework; path = ../../generated/MatrixSDKFFI.xcframework; sourceTree = "<group>"; };
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 = "<group>"; };
189A89C327B417CA0048B0A5 /* MatrixRustSDK-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "MatrixRustSDK-Bridging-Header.h"; sourceTree = "<group>"; };
189A89C727B4201C0048B0A5 /* ClientError+Error.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ClientError+Error.swift"; sourceTree = "<group>"; };
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 = "<group>"; };
18CE89D927B2939900CA89E1 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
18CE89DB27B2939A00CA89E1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
18CE8A0127B293A900CA89E1 /* MatrixRustSDK.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = MatrixRustSDK.entitlements; sourceTree = "<group>"; };
/* 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 = "<group>";
};
189A89B827B40BB10048B0A5 /* Generated */ = {
isa = PBXGroup;
children = (
189A89B927B40BBF0048B0A5 /* sdk.swift */,
);
name = Generated;
sourceTree = "<group>";
};
18CE89CB27B2939900CA89E1 = {
isa = PBXGroup;
children = (
18CE89D627B2939900CA89E1 /* MatrixRustSDK */,
18CE89E727B2939A00CA89E1 /* MatrixRustSDKTests */,
18CE89F127B2939A00CA89E1 /* MatrixRustSDKUITests */,
18CE89D527B2939900CA89E1 /* Products */,
189A89AB27B2E16B0048B0A5 /* Frameworks */,
);
sourceTree = "<group>";
};
18CE89D527B2939900CA89E1 /* Products */ = {
isa = PBXGroup;
children = (
18CE89D427B2939900CA89E1 /* MatrixRustSDK.app */,
18CE89E427B2939A00CA89E1 /* MatrixRustSDKTests.xctest */,
18CE89EE27B2939A00CA89E1 /* MatrixRustSDKUITests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
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 = "<group>";
};
18CE89E727B2939A00CA89E1 /* MatrixRustSDKTests */ = {
isa = PBXGroup;
children = (
18CE89E827B2939A00CA89E1 /* MatrixRustSDKTests.swift */,
);
path = MatrixRustSDKTests;
sourceTree = "<group>";
};
18CE89F127B2939A00CA89E1 /* MatrixRustSDKUITests */ = {
isa = PBXGroup;
children = (
18CE89F227B2939A00CA89E1 /* MatrixRustSDKUITests.swift */,
);
path = MatrixRustSDKUITests;
sourceTree = "<group>";
};
/* 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 */;
}
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
@@ -0,0 +1,98 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1320"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "18CE89D327B2939900CA89E1"
BuildableName = "MatrixRustSDK.app"
BlueprintName = "MatrixRustSDK"
ReferencedContainer = "container:MatrixRustSDK.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "18CE89E327B2939A00CA89E1"
BuildableName = "MatrixRustSDKTests.xctest"
BlueprintName = "MatrixRustSDKTests"
ReferencedContainer = "container:MatrixRustSDK.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "18CE89ED27B2939A00CA89E1"
BuildableName = "MatrixRustSDKUITests.xctest"
BlueprintName = "MatrixRustSDKUITests"
ReferencedContainer = "container:MatrixRustSDK.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "18CE89D327B2939900CA89E1"
BuildableName = "MatrixRustSDK.app"
BlueprintName = "MatrixRustSDK"
ReferencedContainer = "container:MatrixRustSDK.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "18CE89D327B2939900CA89E1"
BuildableName = "MatrixRustSDK.app"
BlueprintName = "MatrixRustSDK"
ReferencedContainer = "container:MatrixRustSDK.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
@@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
@@ -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
}
}
@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
@@ -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 { }
@@ -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()
}
}
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict/>
</plist>
@@ -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"
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
</dict>
</plist>
@@ -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()
}
}
}
@@ -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")
}
}
}
@@ -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 its 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()
}
}
}
}
+39
View File
@@ -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)
+94
View File
@@ -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"
+58
View File
@@ -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"
+38
View File
@@ -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"
+13
View File
@@ -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
+3
View File
@@ -0,0 +1,3 @@
fn main() {
uniffi_build::generate_scaffolding("./src/api.udl").expect("Building the UDL file failed");
}
+137
View File
@@ -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<Room> rooms();
[Throws=ClientError]
sequence<u8> 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<AnyMessage> 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();
};
@@ -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<Box<dyn Stream<Item = Result<SyncRoomEvent, matrix_sdk::Error>> + Send>>;
pub struct BackwardsStream {
stream: Arc<Mutex<MsgStream>>,
}
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<Arc<AnyMessage>> {
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
})
}
}
+163
View File
@@ -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<RwLock<ClientState>>,
delegate: Arc<RwLock<Option<Box<dyn ClientDelegate>>>>,
}
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<Box<dyn ClientDelegate>>) {
*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<String> {
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<Arc<Room>> {
self.client.rooms().into_iter().map(|room| Arc::new(Room::new(room))).collect()
}
pub fn user_id(&self) -> anyhow::Result<String> {
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<String> {
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<String> {
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<String> {
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<MediaSource>) -> anyhow::Result<Vec<u8>> {
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?)
})
}
}
+110
View File
@@ -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<Runtime> =
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<Arc<Client>> {
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<Arc<Client>> {
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<Arc<Client>> {
let builder = new_client_builder(base_path, username.clone())?;
let user = Box::<UserId>::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<ClientBuilder> {
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<anyhow::Error> for ClientError {
fn from(e: anyhow::Error) -> ClientError {
ClientError::Generic { msg: e.to_string() }
}
}
uniffi_macros::include_scaffolding!("api");
+242
View File
@@ -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<BaseMessage>,
html_body: Option<String>,
}
impl TextMessage {
pub fn base_message(&self) -> Arc<BaseMessage> {
self.base_message.clone()
}
pub fn html_body(&self) -> Option<String> {
self.html_body.clone()
}
}
pub struct ImageMessage {
base_message: Arc<BaseMessage>,
source: Arc<MediaSource>,
info: Option<Box<ImageInfo>>,
}
impl ImageMessage {
pub fn base_message(&self) -> Arc<BaseMessage> {
self.base_message.clone()
}
pub fn source(&self) -> Arc<MediaSource> {
self.source.clone()
}
pub fn width(&self) -> Option<u64> {
self.info.clone()?.width?.try_into().ok()
}
pub fn height(&self) -> Option<u64> {
self.info.clone()?.height?.try_into().ok()
}
pub fn blurhash(&self) -> Option<String> {
self.info.clone()?.blurhash
}
}
pub struct NoticeMessage {
base_message: Arc<BaseMessage>,
html_body: Option<String>,
}
impl NoticeMessage {
pub fn base_message(&self) -> Arc<BaseMessage> {
self.base_message.clone()
}
pub fn html_body(&self) -> Option<String> {
self.html_body.clone()
}
}
pub struct EmoteMessage {
base_message: Arc<BaseMessage>,
html_body: Option<String>,
}
impl EmoteMessage {
pub fn base_message(&self) -> Arc<BaseMessage> {
self.base_message.clone()
}
pub fn html_body(&self) -> Option<String> {
self.html_body.clone()
}
}
pub struct AnyMessage {
text: Option<Arc<TextMessage>>,
image: Option<Arc<ImageMessage>>,
notice: Option<Arc<NoticeMessage>>,
emote: Option<Arc<EmoteMessage>>,
}
impl AnyMessage {
pub fn text_message(&self) -> Option<Arc<TextMessage>> {
self.text.clone()
}
pub fn image_message(&self) -> Option<Arc<ImageMessage>> {
self.image.clone()
}
pub fn notice_message(&self) -> Option<Arc<NoticeMessage>> {
self.notice.clone()
}
pub fn emote_message(&self) -> Option<Arc<EmoteMessage>> {
self.emote.clone()
}
}
pub fn sync_event_to_message(sync_event: SyncRoomEvent) -> Option<Arc<AnyMessage>> {
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<String> = 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<String> = 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<String> = 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<MediaSource> {
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(),
}
}
}
+143
View File
@@ -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<AnyMessage>);
}
pub struct Room {
room: MatrixRoom,
delegate: Arc<RwLock<Option<Box<dyn RoomDelegate>>>>,
is_listening_to_live_events: Arc<RwLock<bool>>,
}
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<Box<dyn RoomDelegate>>) {
*self.delegate.write() = delegate;
}
pub fn id(&self) -> String {
self.room.room_id().to_string()
}
pub fn name(&self) -> Option<String> {
self.room.name()
}
pub fn display_name(&self) -> Result<String> {
let r = self.room.clone();
RUNTIME.block_on(async move { Ok(r.display_name().await?) })
}
pub fn topic(&self) -> Option<String> {
self.room.topic()
}
pub fn avatar_url(&self) -> Option<String> {
self.room.avatar_url().map(|m| m.to_string())
}
pub fn member_avatar_url(&self, user_id: String) -> Result<Option<String>> {
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<Option<String>> {
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<Arc<BackwardsStream>> {
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
}
}