此开源库允许您构建与Matrix(http://www.matrix.org)兼容的iOS应用,Matrix是一个开放标准,用于互操作的即时消息和VoIP。
此SDK实现了一个与Matrix客户端/服务器API(http://matrix.org/docs/api/client-server/)通信的接口。
SDK使用CocoaPods(https://cocoapods.org.cn/)作为库依赖管理器。为了设置此环境
sudo gem install cocoapods pod setup
将Matrix SDK的最新版本添加到您的应用程序项目中的最佳方式是将MatrixSDK依赖项添加到您的Podfile中
pod 'MatrixSDK'
如果您想使用SDK的开发版本,请使用以下方式
pod 'MatrixSDK', :git => 'https://github.com/matrix-org/matrix-ios-sdk.git', :branch => 'develop'
如果您想使用http://webrtc.org VoIP堆栈启用VoIP,请将以下pod添加到您的应用Podfile中
pod 'MatrixSDK/JingleCallStack'
作为一个快速概述,以下是您需要了解的SDK中的类。
MXRestClient : | 透露Matrix客户端-服务器API,按照Matrix标准向Home服务器发出请求。 |
---|
这些类是高级工具,用于处理来自Home服务器的响应。它们包含维护一致的聊天室数据的逻辑。
MXSession : | 此类处理所有来自Home服务器的数据。它使用MXRestClient实例从Home服务器获取数据,并将其转发到MXRoom、MXRoomState、MXRoomMember和MXUser对象。 |
---|---|
MXRoom : | 此类提供获取房间数据并与房间交互(加入、离开等)的方法。 |
MXRoomState : | 这是房间在某一时间点的状态:它的名称、主题、可见性(公共/私人)、成员等。 |
MXRoomMember : | 代表房间的成员。 |
MXUser : | 这是当前用户所知的用户,在房间上下文之外。MXSession暴露并维护MXUsers列表。它提供用户ID、显示名和当前存在状态。 |
所有核心E2EE功能都实现在外部matrix-sdk-crypto Rustcrate中,该crate取代了存在于此仓库中所有之前的obj-c / Swift实现。MatrixSDK通过单独发布的pod MatrixSDKCrypto集成此crate。
MatrixSDK中的代码主要由包装器、网络逻辑和将加密与通用应用程序功能、同步循环和会话状态连接在一起的粘合代码组成。一些值得注意的类包括
MXCrypto : | 所有加密功能的入口点,例如加密/解密事件、跨签名用户或管理房间密钥备份。它属于当前会话,因此针对当前用户。 |
---|---|
MXRoomEventEncryption /MXRoomEventDecryption | 两个类负责加密和解密消息事件和与之紧密相关的任务,例如共享房间密钥、对迟到的密钥共享做出反应等。 |
MXCryptoMachine : | 基于Rust的OlmMachine的包装器,提供更方便的API。它的三个主要职责是:-在MatrixSDK和MatrixSDKCrypto之间添加一层抽象 - 将原始字符串映射到从中映射出的Rust机器 - 代表Rust机器执行网络请求并将它们标记为完成 |
要发布MatrixSDKCrypto的新版本,请遵循单独的过程。要测试MatrixSDKCrypto的本地/未发布的更改,请构建框架并将Podfile中的pod重定向到pod MatrixSDKCrypto,:path => 您的/本地/rust-crypto-sdk/MatrixSDKCrypto.podspec
示例应用程序(https://github.com/matrix-org/matrix-ios-console)演示了如何在Matrix之上构建聊天应用程序。您可以参考它、玩弄它、对其进行黑客攻击以了解Matrix SDK的完整集成。本节回到基本概念,提供了基本用例的示例代码。
一个要导入的文件
Obj-C:
#import <MatrixSDK/MatrixSDK.h>
Swift:
import MatrixSDK
此API不需要用户进行身份验证。因此,使用initWithHomeServer设置的MXRestClient来完成工作
Obj-C:
MXRestClient *mxRestClient = [[MXRestClient alloc] initWithHomeServer:@"http://matrix.org"]; [mxRestClient publicRooms:^(NSArray *rooms) { // rooms is an array of MXPublicRoom objects containing information like room id MXLogDebug(@"The public rooms are: %@", rooms); } failure:^(MXError *error) { }];
Swift:
let homeServerUrl = URL(string: "http://matrix.org")! let mxRestClient = MXRestClient(homeServer: homeServerUrl, unrecognizedCertificateHandler: nil) mxRestClient.publicRooms { response in switch response { case .success(let rooms): // rooms is an array of MXPublicRoom objects containing information like room id print("The public rooms are: \(rooms)") case .failure: break } }
在这里,用户需要经过身份验证。我们将使用[MXRestClient initWithCredentials]。通常,在用户登录后创建和初始化这两个对象,然后在整个应用程序生存期内或直到用户注销时保持它们
Obj-C:
MXCredentials *credentials = [[MXCredentials alloc] initWithHomeServer:@"http://matrix.org" userId:@"@your_user_id:matrix.org" accessToken:@"your_access_token"]; // Create a matrix client MXRestClient *mxRestClient = [[MXRestClient alloc] initWithCredentials:credentials]; // Create a matrix session MXSession *mxSession = [[MXSession alloc] initWithMatrixRestClient:mxRestClient]; // Launch mxSession: it will first make an initial sync with the homeserver // Then it will listen to new coming events and update its data [mxSession start:^{ // mxSession is ready to be used // Now we can get all rooms with: mxSession.rooms; } failure:^(NSError *error) { }];
Swift:
let credentials = MXCredentials(homeServer: "http://matrix.org", userId: "@your_user_id:matrix.org", accessToken: "your_access_token") // Create a matrix client let mxRestClient = MXRestClient(credentials: credentials, unrecognizedCertificateHandler: nil) // Create a matrix session let mxSession = MXSession(matrixRestClient: mxRestClient) // Launch mxSession: it will first make an initial sync with the homeserver mxSession.start { response in guard response.isSuccess else { return } // mxSession is ready to be used // now wer can get all rooms with: mxSession.rooms }
我们使用与上述相同的代码,但添加了MXFileStore,该MXFileStore将负责在文件系统中存储用户数据。这将避免每次应用程序恢复时都要与主服务器进行完整同步。应用程序将能够快速恢复。此外,它在与主服务器同步时可以离线运行
Obj-C:
MXCredentials *credentials = [[MXCredentials alloc] initWithHomeServer:@"http://matrix.org" userId:@"@your_user_id:matrix.org" accessToken:@"your_access_token"]; // Create a matrix client MXRestClient *mxRestClient = [[MXRestClient alloc] initWithCredentials:credentials]; // Create a matrix session MXSession *mxSession = [[MXSession alloc] initWithMatrixRestClient:mxRestClient]; // Make the matrix session open the file store // This will preload user's messages and other data MXFileStore *store = [[MXFileStore alloc] init]; [mxSession setStore:store success:^{ // Launch mxSession: it will sync with the homeserver from the last stored data // Then it will listen to new coming events and update its data [mxSession start:^{ // mxSession is ready to be used // Now we can get all rooms with: mxSession.rooms; } failure:^(NSError *error) { }]; } failure:^(NSError *error) { }];
Swift:
let credentials = MXCredentials(homeServer: "http://matrix.org", userId: "@your_user_id:matrix.org", accessToken: "your_access_token") // Create a matrix client let mxRestClient = MXRestClient(credentials: credentials, unrecognizedCertificateHandler: nil) // Create a matrix session let mxSession = MXSession(matrixRestClient: mxRestClient) // Make the matrix session open the file store // This will preload user's messages and other data let store = MXFileStore() mxSession.setStore(store) { response in guard response.isSuccess else { return } // Launch mxSession: it will sync with the homeserver from the last stored data // Then it will listen to new coming events and update its data mxSession.start { response in guard response.isSuccess else { return } // mxSession is ready to be used // now we can get all rooms with: mxSession.rooms() } }
我们重用之前创建的mxSession实例
Obj-C:
// Retrieve the room from its room id MXRoom *room = [mxSession room:@"!room_id:matrix.org"]; // Add a listener on events related to this room [room.liveTimeline listenToEvents:^(MXEvent *event, MXEventDirection direction, MXRoomState *roomState) { if (direction == MXTimelineDirectionForwards) { // Live/New events come here } else if (direction == MXTimelineDirectionBackwards) { // Events that occurred in the past will come here when requesting pagination. // roomState contains the state of the room just before this event occurred. } }];
Swift:
// Retrieve the room from its room id let room = mxSession.room(withRoomId: "!room_id:matrix.org") // Add a listener on events related to this room _ = room?.liveTimeline.listenToEvents { (event, direction, roomState) in switch direction { case .forwards: // Live/New events come here break case .backwards: // Events that occurred in the past will come here when requesting pagination. // roomState contains the state of the room just before this event occurred. break } }
让我们使用paginateBackMessages加载一些房间历史记录
Obj-C:
// Reset the pagination start point to now [room.liveTimeline resetPagination]; [room.liveTimeline paginate:10 direction:MXTimelineDirectionBackwards onlyFromStore:NO complete:^{ // At this point, the SDK has finished to enumerate the events to the attached listeners } failure:^(NSError *error) { }];
Swift:
// Reset the pagination start point to now room?.liveTimeline.resetPagination() room?.liveTimeline.paginate(10, direction: .backwards, onlyFromStore: false) { _ in // At this point, the SDK has finished to enumerate the events to the attached listeners }
此操作不需要MXSession中的任何业务逻辑:我们可以直接使用MXRestClient
Obj-C:
[mxRestClient sendTextMessageToRoom:@"the_room_id" text:@"Hello world!" success:^(NSString *event_id) { // event_id is for reference // If you have registered events listener like in the previous use case, you will get // a notification for this event coming down from the homeserver events stream and // now handled by MXSession. } failure:^(NSError *error) { }];
Swift:
client.sendTextMessage(toRoom: "the_room_id", text: "Hello World!") { (response) in if case .success(let eventId) = response { // eventId is for reference // If you have registered events listener like in the previous use case, you will get // a notification for this event coming down from the homeserver events stream and // now handled by MXSession. } }
在Matrix中,主服务器可以在事件到达时向用户发送通知。然而,在APNS中,只有您,应用程序开发者,可以发送APNS通知,因为这样做需要您的APNS私钥。因此,Matrix需要一个从主服务器分离的单独服务器来发送推送通知,因为您不能信任任意的主服务器接受您应用程序的APNS私钥。这被称为“推送网关”。有关Matrix中通知工作原理的更多信息,请参阅https://matrix.org/docs/spec/push_gateway/latest.html
简单来说,为了使您的应用程序接收推送通知,您需要设置一个推送网关。这是一个公开可访问的服务器,专门为您的iOS应用程序而构造,它从Matrix主服务器接收HTTP POST请求并发送APNS。Matrix提供了一个参考推送网关“sygnal”,可在https://github.com/matrix-org/sygnal找到,其中包含有关如何设置的说明。
您还可以编写自己的推送网关。有关HTTP推送通知协议的规范,请参阅https://matrix.org/docs/spec/push_gateway/latest.html。您的推送网关可以监听任何路径上的通知(只要您的应用程序知道该路径以便通知 homeserver),但 Matrix 强烈建议此 URL 的路径为 '/_matrix/push/v1/notify'。
在您的应用程序中,您会首先以正常方式注册 APNS(假设 iOS 8 或更高版本)
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIRemoteNotificationTypeBadge |UIRemoteNotificationTypeSound |UIRemoteNotificationTypeAlert) categories:nil]; [...] - (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings { [application registerForRemoteNotifications]; }
当您收到特定应用程序实例的 APNS 令牌后,您将其编码为文本,并使用它作为 'pushkey' 调用 setPusherWithPushkey,以便通知 homeserver 通过您的推送网关的 URL 发送推送。Matrix 建议对 APNS 令牌进行 base64 编码(因为 sygnal 就是这么做的)
- (void)application:(UIApplication*)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken { NSString *b64Token = [self.deviceToken base64EncodedStringWithOptions:0]; NSDictionary *pushData = @{ @"url": @"https://example.com/_matrix/push/v1/notify" // your push gateway URL }; NSString *deviceLang = [NSLocale preferredLanguages][0]; NSString *profileTag = makeProfileTag(); // more about this later MXRestClient *restCli = [MatrixSDKHandler sharedHandler].mxRestClient; [restCli setPusherWithPushkey:b64Token kind:@"http" appId:@"com.example.supercoolmatrixapp.prod" appDisplayName:@"My Super Cool Matrix iOS App" deviceDisplayName:[[UIDevice currentDevice] name] profileTag:profileTag lang:deviceLang data:pushData success:^{ // Hooray! } failure:^(NSError *error) { // Some super awesome error handling goes here } ]; }
当您调用 setPusherWithPushkey 时,在您会话登录的 homeserver 上创建一个推送者。这会将 HTTP 通知发送到您在 setPusherWithPushkey 的 'data' 参数中提供的作为 'url' 键的 URL。
有关这些参数的更多信息,请参阅客户端/服务器规范(http://matrix.org/docs/api/client-server/#!/Push32notifications/post_matrix_client_r0_pushers_set)。以下包含了一些这些参数的更多信息。
- appId
- 这有两个目的:首先是在 homeserver 上你的推送键存在的命名空间中形成,这意味着你应该使用你应用程序的唯一标识符:强烈建议使用反向DNS样式的标识符。其二,是为了识别你的应用程序对你的推送网关,以便你的推送网关在 speaking to the APNS gateway 时知道应该使用哪个私钥和证书。因此,你应该根据你的应用程序是在产品推送模式还是沙盒推送模式下使用不同的 app ID,以便你的推送网关可以相应地发送 APNS。Matrix 建议根据情况将 appId 后缀为 '.dev' 或 '.prod'。
- profileTag
- 这表示该设备应该遵守哪组推送规则。有关推送规则的更多信息,请参阅客户端/服务器推送规范:http://matrix.org/docs/api/client-server/#!/Push32notifications/post_matrix_client_r0_pushers_set。这是这对设备专用推送规则集合的标识符,该设备将遵守。建议自动生成一个 16 位字符的字母数字字符串,并在整个应用程序数据生命周期中使用此字符串。更高级的使用方式将允许多个设备共享一组推送规则。
仓库中包含一个 Xcode 项目用于开发。此项目不构建应用程序,而是构建测试套件。下一段落说明如何设置测试环境。
在打开 Matrix SDK Xcode 工作区之前,您需要构建它。
项目在 pod 文件中声明了一些第三方库依赖项。您需要运行 CocoaPods 命令来下载它们并设置 Matrix SDK 工作区
$ pod install
然后,打开 MatrixSDK.xcworkspace
。
SDK Xcode 项目中的测试既包括单元测试也包括集成测试。
单元测试类使用后缀 "UnitTests" 来区分。单元测试是不进行任何 HTTP 请求或使用模拟 HTTP 请求的测试。
开箱即用的测试使用 "Demo Federation of Homeservers" 的一个 homeserver(位于 https://:8080)(https://matrix-org.github.io/synapse/develop/development/demo.html?highlight=demo#synapse-demo-setup)。
在您安装 synapse 之前,可能在 Mac OS 上需要安装一些依赖项
- Homebrew:运行
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)”
。更多信息请从这里找到 https://brew.sh.cn - python 3:下载最新稳定版应该没问题。从这里下载
.pkg
并在这里安装它 https://pythonlang.cn/downloads/ - pipx:已安装 Python 后,运行
pip3 install --user pipx
- Rust:运行
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
。更多信息可以在这里找到https://rust-lang.net.cn/tools/install - icu4c:运行
brew install icu4c
- 更新 icu4c 的环境变量:如果使用 zsh,则运行
echo 'export PATH="/opt/homebrew/opt/icu4c/bin:$PATH"' >> ~/.zshrc
。否则,尝试以相同方式更新.bash_profile
。您可能已将另一个文件夹配置为 brew 的二进制文件。在这种情况下,尝试运行brew info icu4c
以找到正确的路径。 - pg_config:您可以通过运行
brew install postgresql
来获取它。
您首先需要在 https://github.com/matrix-org/synapse#synapse-development 上设置 Synapse 开发模式,并按照说明进行操作。
$ pip install --user pipx $ python3 -m pipx ensurepath # To run if `pipx install poetry` complained about PATH not being correctly set $ pipx install poetry $ git clone https://github.com/matrix-org/synapse.git $ cd synapse $ poetry install --extras all
要从 synapse 根目录启动这些测试 homeserver,请键入
$ poetry run ./demo/start.sh --no-rate-limit
要验证 Synapse 实例是否正确运行,请打开网页浏览器并转到 http://127.0.0.1:8080。网页应该确认。
要停止和重置服务器
$ poetry run ./demo/stop.sh $ poetry run ./demo/clean.sh
现在您可以从 Xcode 的 Test navigator 标签运行测试,或选择 MatrixSDKTests 方案并点击“测试”操作。
我们有针对 macOS 目标的测试计划,可以独立运行测试或使用不同的配置。
- 所有测试
- 默认测试计划以运行所有测试。
- 所有测试 - 使用 Sanitizers
- 运行所有测试,具有两种配置:“ASan + UBSan”和“TSan + UBSan”。其中“UBSan”代表意外的行为清理程序,“ASan”代表地址清理程序,“TSan”代表线程清理程序。此配置在 WWDC2019 中得到建议(https://developer.apple.com/videos/play/wwdc2019/413?time=2270)。此测试计划需要构建 2 次和运行 2 次测试。
- 单元测试
- 所有单元测试的测试计划。
- 单元测试 - 使用 Sanitizers
- 所有单元测试均使用上述两种配置:“ASan + UBSan”和“TSan + UBSan”。
CocoaPods 可能会在 OSX 10.8.x 上失败安装,出现“i18n 需要 Ruby 版本 >= 1.9.3”的错误。这是一个已知问题,类似于 CocoaPods/CocoaPods#2458,需要与 CocoaPods 团队提出。
SDK 目前只管理登录密码类型的注册。这种类型的注册不被 matrix.org 上运行的自定义 homeserver 所接受。出于安全和垃圾邮件的原因,此类注册已被禁用。因此,现在您将无法在这样的人ensemever 上使用 SDK 注册新帐户。但您可以用现有用户登录。
如果您运行自己的 home server,则默认启动参数启用了登录密码类型的注册,您将能够向其注册新用户。
版权所有 (c) 2014-2017 OpenMarket Ltd 版权所有 (c) 2017 Vector Creations Ltd 版权所有 (c) 2017-2018 New Vector Ltd
根据 Apache License,版本 2.0(“许可证”;您不得使用本作品,除非遵守许可证规定。您可以在 LICENSE 文件中或通过以下网址获得许可证副本:
https://apache.ac.cn/licenses/LICENSE-2.0
除非适用法律要求或书面同意,否则在本许可证下分发的软件按“原样”的基础提供,不提供任何明示或暗示的保证或条件。有关授权和限制的具体语言请参阅许可证。