Matrix iOS SDK
此开源库允许您构建适用于 Matrix(http://www.matrix.org)的 iOS 应用程序,Matrix 是一个用于互操作即时消息和 VoIP 的开放标准。
此 SDK 通过定义在 http://matrix.org/docs/api/client-server/ 的 Matrix 客户端/服务器 API 实现了一个接口。
在您的应用中使用 SDK
SDK 使用 CocoaPods(《https://cocoapods.org.cn/》)作为库依赖项管理器。为了设置这个
sudo gem install cocoapods pod setup
将 Matrix SDK 的新版本添加到您的应用项目的最佳方式是为您的 Podfile 添加 MatrixSDK 依赖项
# Obj-C pod 'MatrixSDK' # Swift pod 'SwiftMatrixSDK'
如果您想使用 SDK 的开发版本,则使用以下版本:
# Obj-C pod 'MatrixSDK', :git => 'https://github.com/matrix-org/matrix-ios-sdk.git', :branch => 'develop'
# Swift pod 'SwiftMatrixSDK', :git => 'https://github.com/matrix-org/matrix-ios-sdk.git', :branch => 'develop'
选项
如果你想要启用VoIP,请在你的app Podfile中添加以下容器
pod 'WebRTC'
如果你想要启用端到端加密,请添加
pod 'OLMKit' pod 'Realm'
概述
快速概述,以下是使用SDK所需要了解的类。
Matrix API 级别
MXRestClient : | 暴露了Matrix标准中指定的Matrix客户端-服务器API,以便向 homeserver 发送请求。 |
---|
业务逻辑和数据模型
这些类是高级工具,用于处理来自 homeserver 的响应。它们包含维护一致的聊天室数据的逻辑。
MXSession : | 此类处理所有来自 homeserver 的数据。它使用 MXRestClient 实例从 homeserver 获取数据,并将其转发到 MXRoom、MXRoomState、MXRoomMember 和 MXUser 对象。 |
---|---|
MXRoom : | 此类提供获取房间数据和与房间(加入、离开等)交互的方法。 |
MXRoomState : | 这是在某个时间点房间的状态:其名称、主题、可见性(公开/私有)、成员等。 |
MXRoomMember : | 表示房间成员。 |
MXUser : | 这是当前用户知晓的用户,但不属于房间上下文。MXSession公开并维护MXUser列表。它提供了用户id、displayname和当前存在状态。 |
用法
示例应用程序(https://github.com/matrix-org/matrix-ios-console)展示了如何在Matrix之上构建一个聊天应用程序。您可以参考它,玩耍,破解它以理解Matrix SDK的完全集成。本节将回到基础,并提供了基本用例的示例代码。
一个需要导入的文件
Obj-C:
#import <MatrixSDK/MatrixSDK.h>
Swift:
import SwiftMatrixSDK
用例 #1:获取 homeserver 的公共房间
此 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 NSLog(@"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 } }
用例 #2:获取用户已交互的房间
在这里,用户需要进行身份验证。我们将使用 [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 }
用例 #2(bis):使用永久 MXStore 获取用户已交互的房间
我们使用与上面相同的代码,但添加了一个 MXFileStore,它将负责将用户数据存储在文件系统中。这将避免在每次应用恢复时与 homeserver 进行完全同步。应用将能够快速恢复。此外,它还能够脱机运行,同时在同步 homeserver 时运行
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() } }
用例 #3:获取房间消息
我们重用了之前创建的 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 }
用例 #4:向房间发送文本消息
此操作无需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中, homeserver可以在某些事件出现时向用户发送通知。但在APNS中,只有作为应用开发者的 you 可以发送APNS通知,因为这样做需要你自己的APNS私钥。Matrix因此需要一个独立的服务器,与homeservers解耦,以发送推送通知,因为你不能信任任意的homeservers来处理你的应用的APNS私钥。这个被称作'推送网关'。更多关于Matrix中通知如何工作,请参考http://matrix.org/docs/spec/push_gateway/unstable.html
简单来说,为了你的应用能接收推送通知,你需要设置一个推送网关。这是一个公开可访问的服务器,是针对你特定的iOS应用的设计,它可以接收来自Matrix Homeserver的HTTP POST请求并发送APNS。Matrix提供了一个参考推送网关'sygnal',可以在https://github.com/matrix-org/sygnal找到,附带如何设置的说明。
你还可以编写自己的推送网关。关于HTTP推送通知协议的规范,请参考http://matrix.org/docs/spec/push_gateway/unstable.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令牌进行base 64编码(因为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上创建一个推送器。这将向提供的URL发送HTTP通知,作为`data`参数中的`url`键。
关于这些参数的更多信息,请参见客户端/服务器规范(http://matrix.org/docs/api/client-server/#!/Push32notifications/post_matrix_client_r0_pushers_set)。以下是对其中一些参数的简要说明
- appId
- 该功能有两个目的:首先是为了在 homeserver 上创建您的 pushkeys 所在的命名空间,这意味着您应该使用应用程序特定的唯一标识符:强烈推荐使用反向 DNS 风格的标识符。第二个目的是让您的应用识别 Push Gateway,这样 Push Gateway 就知道在与 APNS 网关通信时使用哪个私钥和证书。因此,根据您的应用程序是否处于生产或沙箱推送模式,您应该使用不同的应用 ID,以便 Push Gateway 可以相应地发送 APNS。Matrix 建议相应地使用'.dev'或'.prod'后缀您的 appId。
- 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 项目中的测试既包括单元测试也包括集成测试。
出厂时,测试使用“示例 homeservers 联邦”中的某个 homeserver(位于https://:8080)。(https://github.com/matrix-org/synapse#running-a-demo-federation-of-homeservers)。您必须从您的本地 Synapse 文件夹中启动它们。
$ virtualenv env $ source env/bin/activate $ demo/start.sh --no-rate-limit
然后,您可以从 Xcode 测试导航器选项卡中运行测试,或选择 MatrixSDKTests 计划并单击“测试”操作。
已知问题
CocoaPods可能在OSX 10.8.x上安装失败,显示“i18n需要Ruby版本≥1.9.3”。这是一个已知问题,类似于https://github.com/CocoaPods/CocoaPods/issues/2458,需要与CocoaPods团队提出。
### 动态框架:架构未定义的符号
如果您使用“MatrixSDK”而不是“SwiftMatrixSDK”,您可能会遇到编译时错误,看起来像这样
Undefined symbols for architecture x86_64: "_OBJC_CLASS_$_GAIDictionaryBuilder", referenced from: objc-class-ref in MXGoogleAnalytics.o "_OBJC_CLASS_$_GAI", referenced from: objc-class-ref in MXGoogleAnalytics.o ld: symbol(s) not found for architecture x86_64 clang: error: linker command failed with exit code 1 (use -v to see invocation)
当以下两个条件都成立时,就会出现这种情况
1. MatrixSDK作为框架编译(即在您的podfile中启用了use_frameworks!设置)2. 您的项目还使用了Google Analytics pod。
根本原因在于MXGoogleAnalytics类识别到Google Analytics pod被添加到您的项目中,并尝试添加其头文件。这种类型的行为在静态库中是允许的,但在动态框架中是不允许的。
最简单的解决方案是切换到“SwiftMatrixSDK”pod,即使您不使用Swift(对于Obj-C项目,这些pod实际上是相同的)。SwiftMatrixSDK排除了MXGoogleAnalytics类。如果您想收集有关初始化时间等的数据分析,可以实现自己的MXAnalyticsDelegate并将其实例设置为MXSDKOptions.sharedInstance。有关更多信息,请参阅MXAnalyticsDelegate.h和MXGoogleAnalytics.h/m。
注册
当前SDK仅管理登录密码类型注册。这种类型的注册不受matrix.org上托管的家服务器接受。出于安全和垃圾邮件的原因,该功能已禁用。因此,目前您无法在此家服务器上使用SDK注册新的账户。但您可以登录现有用户。
如果您运行自己的家服务器,默认启动参数启用了登录密码类型注册,您可以为它注册新用户。
版权 & 许可
版权(c)2014-2016 OpenMarket Ltd
根据Apache License,版本2.0(“许可证”);除非您根据法律规定或书面同意使用此作品,否则不得使用此作品。您可以在LICENSE文件中或从以下位置获得许可证:
https://apache.ac.cn/licenses/LICENSE2.0
除非法律规定或书面同意,否则根据许可证分发的软件按“原样”分发,不提供任何明示或暗示的保证或条件。有关权限和限制的特定语言,请参阅许可证。