GithubHelp home page GithubHelp logo

matrix-org / matrix-ios-sdk Goto Github PK

View Code? Open in Web Editor NEW
451.0 451.0 210.0 22.31 MB

The Matrix SDK for iOS

License: Apache License 2.0

Ruby 0.10% Objective-C 75.94% Shell 0.05% Python 0.10% Swift 23.60% C 0.18% Jinja 0.03%

matrix-ios-sdk's Introduction

image

image

image

image

image

Matrix iOS SDK

This open-source library allows you to build iOS apps compatible with Matrix (http://www.matrix.org), an open standard for interoperable Instant Messaging and VoIP.

This SDK implements an interface to communicate with the Matrix Client/Server API which is defined at http://matrix.org/docs/api/client-server/.

Use the SDK in your app

The SDK uses CocoaPods (http://cocoapods.org/) as library dependency manager. In order to set this up:

sudo gem install cocoapods
pod setup

The best way to add the last release of the Matrix SDK to your application project is to add the MatrixSDK dependency to your Podfile:

pod 'MatrixSDK'

If you want to use the develop version of the SDK, use instead:

pod 'MatrixSDK', :git => 'https://github.com/matrix-org/matrix-ios-sdk.git', :branch => 'develop'

Options

If you want to enable VoIP using the http://webrtc.org VoIP stack, add the following pod to your app Podfile:

pod 'MatrixSDK/JingleCallStack'

Overview

As a quick overview, there are the classes to know to use the SDK.

Matrix API level

MXRestClient

Exposes the Matrix Client-Server API as specified by the Matrix standard to make requests to a homeserver.

Business logic and data model

These classes are higher level tools to handle responses from a homeserver. They contain logic to maintain consistent chat room data.

MXSession

This class handles all data arriving from the homeserver. It uses a MXRestClient instance to fetch data from the homeserver, forwarding it to MXRoom, MXRoomState, MXRoomMember and MXUser objects.

MXRoom

This class provides methods to get room data and to interact with the room (join, leave...).

MXRoomState

This is the state of room at a certain point in time: its name, topic, visibility (public/private), members, etc.

MXRoomMember

Represents a member of a room.

MXUser

This is a user known by the current user, outside of the context of a room. MXSession exposes and maintains the list of MXUsers. It provides the user id, displayname and the current presence state.

End-to-end Encryption

All core E2EE functionality is implemented in an external matrix-sdk-crypto Rust crate, which replaces all previous obj-c / Swift implementation that used to exist in this repository. MatrixSDK integrates this crate via pod MatrixSDKCrypto published separately.

Code in MatrixSDK consists mostly of wrappers, networking logic and glue code connecting encryption with general app functionality, sync loops and session state. Some of the notable classes include:

MXCrypto

Main entry-point into all cryptographic functionality, such as encrypting/decrypting events, cross-signing users, or managing room key backups. It is owned by the current session and therefore specific to the current user.

MXRoomEventEncryption/MXRoomEventDecryption

Two classes responsible for encrypting and decrypting message events and tasks that are closely dependent, such as sharing room keys, reacting to late key-shares etc.

MXCryptoMachine

Wrapper around Rust-based OlmMachine, providing a more convenient API. Its three main responsibilities are: - adding a layer of abstraction between MatrixSDK and MatrixSDKCrypto - mapping to and from raw strings passed into the Rust machine - performing network requests and marking them as completed on behalf of the Rust machine

To publish a new version of MatrixSDKCrypto follow a separate process. To test local / unpublished changes in MatrixSDKCrypto, build the framework and re-direct the pod in your Podfile to pod MatrixSDKCrypto, :path => your/local/rust-crypto-sdk/MatrixSDKCrypto.podspec

Usage

The sample app (https://github.com/matrix-org/matrix-ios-console) demonstrates how to build a chat app on top of Matrix. You can refer to it, play with it, hack it to understand the full integration of the Matrix SDK. This section comes back to the basics with sample codes for basic use cases.

One file to import:

Obj-C:

#import <MatrixSDK/MatrixSDK.h>

Swift:

import MatrixSDK

Use case #1: Get public rooms of an homeserver

This API does not require the user to be authenticated. So, MXRestClient instantiated with initWithHomeServer does the job:

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
    }
}

Use case #2: Get the rooms the user has interacted with

Here the user needs to be authenticated. We will use [MXRestClient initWithCredentials]. You'll normally create and initialise these two objects once the user has logged in, then keep them throughout the app's lifetime or until the user logs out:

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
}

Use case #2 (bis): Get the rooms the user has interacted with (using a permanent MXStore)

We use the same code as above but we add a MXFileStore that will be in charge of storing user's data on the file system. This will avoid to do a full sync with the homeserver each time the app is resumed. The app will be able to resume quickly. Plus, it will be able to run in offline mode while syncing with the 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()
    }
}

Use case #3: Get messages of a room

We reuse the mxSession instance created before:

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
    }
}

Let's load a bit of room history using 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
}

Use case #4: Post a text message to a room

This action does not require any business logic from MXSession: We can use MXRestClient directly:

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.
    }
}

Push Notifications

In Matrix, a homeserver can send notifications out to a user when events arrive for them. However in APNS, only you, the app developer, can send APNS notifications because doing so requires your APNS private key. Matrix therefore requires a seperate server decoupled from the homeserver to send Push Notifications, as you cannot trust arbitrary homeservers with your application's APNS private key. This is called the 'Push Gateway'. More about how notifications work in Matrix can be found at https://matrix.org/docs/spec/push_gateway/latest.html

In simple terms, for your application to receive push notifications, you will need to set up a push gateway. This is a publicly accessible server specific to your particular iOS app that receives HTTP POST requests from Matrix Home Servers and sends APNS. Matrix provides a reference push gateway, 'sygnal', which can be found at https://github.com/matrix-org/sygnal along with instructions on how to set it up.

You can also write your own Push Gateway. See https://matrix.org/docs/spec/push_gateway/latest.html for the specification on the HTTP Push Notification protocol. Your push gateway can listen for notifications on any path (as long as your app knows that path in order to inform the homeserver) but Matrix strongly recommends that the path of this URL be '/_matrix/push/v1/notify'.

In your application, you will first register for APNS in the normal way (assuming iOS 8 or above):

UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIRemoteNotificationTypeBadge
                                                                                     |UIRemoteNotificationTypeSound
                                                                                     |UIRemoteNotificationTypeAlert)
                                                                                     categories:nil];
[...]

- (void)application:(UIApplication *)application
        didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
{
    [application registerForRemoteNotifications];
}

When you receive the APNS token for this particular application instance, you then encode this into text and use it as the 'pushkey' to call setPusherWithPushkey in order to tell the homeserver to send pushes to this device via your push gateway's URL. Matrix recommends base 64 encoding for APNS tokens (as this is what sygnal uses):

- (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
        }
    ];
}

When you call setPusherWithPushkey, this creates a pusher on the homeserver that your session is logged in to. This will send HTTP notifications to a URL you supply as the 'url' key in the 'data' argument to setPusherWithPushkey.

You can read more about these parameters in the Client / Server specification (http://matrix.org/docs/api/client-server/#!/Push32notifications/post_matrix_client_r0_pushers_set). A little more information about some of these parameters is included below:

appId

This has two purposes: firstly to form the namespace in which your pushkeys exist on a homeserver, which means you should use something unique to your application: a reverse-DNS style identifier is strongly recommended. Its second purpose is to identify your application to your Push Gateway, such that your Push Gateway knows which private key and certificate to use when talking to the APNS gateway. You should therefore use different app IDs depending on whether your application is in production or sandbox push mode so that your Push Gateway can send the APNS accordingly. Matrix recommends suffixing your appId with '.dev' or '.prod' accordingly.

profileTag

This identifies which set of push rules this device should obey. For more information about push rules, see the Client / Server push specification: http://matrix.org/docs/api/client-server/#!/Push32notifications/post_matrix_client_r0_pushers_set This is an identifier for the set of device-specific push rules that this device will obey. The recommendation is to auto-generate a 16 character alphanumeric string and use this string for the lifetime of the application data. More advanced usage of this will allow for several devices sharing a set of push rules.

Development

The repository contains a Xcode project in order to develop. This project does not build an app but a test suite. See the next section to set the test environment.

Before opening the Matrix SDK Xcode workspace, you need to build it.

The project has some third party library dependencies declared in a pod file. You need to run the CocoaPods command to download them and to set up the Matrix SDK workspace:

$ pod install

Then, open MatrixSDK.xcworkspace.

Tests

The tests in the SDK Xcode project are both unit and integration tests.

Unit tests classes use the suffix "UnitTests" to differentiate them. A unit test is a test that does not make any HTTP requests or uses mocked HTTP requests.

Out of the box, the tests use one of the homeservers (located at http://localhost:8080) of the "Demo Federation of Homeservers" (https://matrix-org.github.io/synapse/develop/development/demo.html?highlight=demo#synapse-demo-setup).

Before you install synapse you may need few dependencies to be installed on Mac OS:

  • Homebrew: run /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)โ€. More information can be found here https://brew.sh
  • python 3: downloading the latest stable version should be fine. Download the .pkg and install it from here https://www.python.org/downloads/
  • pipx: with python installed run pip3 install --user pipx
  • Rust: run curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh. more information can be found here https://www.rust-lang.org/tools/install
  • icu4c: Run brew install icu4c
  • Update env variables for icu4c: if you use zsh run echo 'export PATH="/opt/homebrew/opt/icu4c/bin:$PATH"' >> ~/.zshrc. Otherwise try to update .bash_profile in the same way. You may have configured another folder for brew binaries. In that case try to run brew info icu4c to spot the correct path.
  • pg_config: you can get it by running brew install postgresql

You first need to follow instructions to set up Synapse in development mode at https://github.com/matrix-org/synapse#synapse-development. The cookbook is:

$ 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

To launch these test homeservers, type from the synapse root folder:

$ poetry run ./demo/start.sh --no-rate-limit

To verify that the synapse instance is actually running correctly, open a web browser and go to http://127.0.0.1:8080. A web page should confirm it.

To stop and reset the servers:

$ poetry run ./demo/stop.sh
$ poetry run ./demo/clean.sh

You can now run tests from the Xcode Test navigator tab or select the MatrixSDKTests scheme and click on the "Test" action.

Test Plans

We have test plans for the macOS target to run tests separately or with different configurations.

AllTests

Default test plan to run all tests.

AllTestsWithSanitizers

Run all tests with 2 configurations: "ASan + UBSan" and "TSan + UBSan". "UBSan" for Unexpected Behavior Sanitizer. "ASan" for Address Sanitizier. "Tsan" for Thread Sanitizer. This setup was advised at WWDC2019 (https://developer.apple.com/videos/play/wwdc2019/413?time=2270). This test plan requires 2 builds and 2 test runs.

UnitTests

Test plan for all unit tests.

UnitTestsWithSanitizers

All unit tests with the 2 configurations described above: "ASan + UBSan" and "TSan + UBSan".

Known issues

CocoaPods may fail to install on OSX 10.8.x with "i18n requires Ruby version >= 1.9.3.". This is a known problem similar to CocoaPods/CocoaPods#2458 that needs to be raised with the CocoaPods team.

Registration

The SDK currently manages only login-password type registration. This type of registration is not accepted by the homeserver hosted at matrix.org. It has been disabled for security and spamming reasons. So, for now, you will be not be able to register a new account with the SDK on such homeserver. But you can login an existing user.

If you run your own homeserver, the default launch parameters enables the login-password type registration and you will be able to register a new user to it.

Copyright & License ==================

Copyright (c) 2014-2017 OpenMarket Ltd Copyright (c) 2017 Vector Creations Ltd Copyright (c) 2017-2018 New Vector Ltd

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with the License. You may obtain a copy of the License in the LICENSE file, or at:

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

matrix-ios-sdk's People

Contributors

aaronraimist avatar alfogrillo avatar alisoftware avatar anderas avatar ara4n avatar aramsargsyan avatar aringenbach avatar avery-pierce avatar billcarsonfr avatar cvwright avatar dbkr avatar ferologics avatar gileluard avatar giomfo avatar ismailgulek avatar jannikgra avatar johennes avatar johnflanagan-spok avatar langleyd avatar manuroe avatar maximeevrard42 avatar morozkin avatar nimau avatar paleksandrs avatar pixlwave avatar sbiosoftwhare avatar stefanceriu avatar velin92 avatar ylecollen avatar yostyle avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

matrix-ios-sdk's Issues

MXRoom isDirect returns YES for rooms that are not direct

MXRoom objects may think that an instance is a direct room when in fact it is not.
(Present in Riot IOS using SDK 0.8.1, but not with SDK 0.7.11 or Riot Desktop)

How to replicate:
User A (riot desktop) creates a room (not a conversation, but a room) and invites only user B.
User B accepts the invitation (riot iOS). For user B, the room shows under people instead of Rooms.
User A invites user C. User C accepts (Riot iOS). User B still sees the room under People, and the MXRoom object of that particular room still returns YES when calling isDirect.
User C also sees the room under People.

MXFileStore: Store events JSON strings instead of doing a lot of things in [MXEvent encodeWithCoder]

The app, with accounts with hundreds of room, spends a lot of time in saving events - specially when saving all state events of a room.

A significant part of this time is spent when serialising the event data in [MXEvent encodeWithCoder]:

which is a pity because we receive these events in serialised form from a JSON response form the homeserver.

Proposal:
Hack the mechanism in the AFNetworking JSON deserialiser to keep the original JSON string of the event from homeserver responses and use this JSON string when saving this event.
The JSON string object can be released once the event is stored to save memory.

Some tests must be done because this change must not affect events loading which is more visible for the end-user.

[e2e issue] client badly considers someone is not a room member

I have found an interesting and systematic issue related to e2e encryption on iOS and synapse.
A creates an e2e room with B
They send messages -> OK.
A invites C
C send a message: A can decrypt this message but not B.
After some investigation. I found the reason. C considers B is not a room member so it does not send him the needed megolm keys.
Problem is linked to MxRoomState -> handleStateEvent. He considers the user is no more part of the room and remove him.
If I have a look on the /sync response received by C just after he has joined the room:

B is in room->joins->state->events with state{ membership = join;type = "m.room.member";} => correct
But in timeline events , the last events we receive related to B is "B has been invited"
But the events related to "B has joined room" is missing even if more recent than event "B has been invited"
Last event in this timeline events is "C has joined room"
I can provide you in isntrumented log if you want.
So For me, there is an issue on Synapse which does not send correct event to C.
But client should be robust to this use case.

To conclude, I will try to implement following work around on client: if a m.room.member event in the timeline event is older than m.room.member state event associated to same member, its should be ignored.
But perhaps this issue concerns all state events.

I have added this check in MxRoomState -> handleStateEvent for RoomMember only. It seems to fix the issue.
Manu, do you think it is an acceptable work around? Or can we fix it in a better way?

Implement MXRealmStore

Implement MXStore with https://realm.io.

A prototype has been done to test storing of rooms states, which makes a lot of IO accesses with MXFileStore.

With MXFileStore, the first write and every change in Matrix HQ state takes 2.4s on my 5s
With Realm, the first write of 13k state events takes 4.4s but next updates take few ms

Time to store Matrix HQ state First write (ms) Update of a single state event (ms)
MXFileStore 2400 2400
Realm hack 4400 few

From the 1st update, Realm is the winner.

Loading seems similar in both cases.

Implementation has been started at https://github.com/matrix-org/matrix-ios-sdk/tree/MXRealmStore.

[MxHttpClient] When server returns a code error 401, only errorcode and error are managed

To secure login operation (and protect paswword), some protocols exist based on 3 steps operations.
For example RFC 2617 (used in SIP or HTTP)
A new matrix login based on such a protocol would be like this:

Step1: matrix client posts a first request "_matrix/client/v0/NewLogin" with missing parameters
=> server anwers with 401 status code bur provides a json response containing parameters used for step2

Step2: correct input parameters are computed by matrix client based on first response returned by server

Step3: matrix client posts another request with all needed parameters on same url "_matrix/client/v0_wn/NewLogin"
=> If succes, server answers with status code 200 otherwise with 40* as status code.

Currently it is not possible to implement such a protocol because when server answers with status code 401, MXHTTPClient only returns an NSerror, so that only errorcode and error fields are parsed to be tranformed into an NSError. Other fields in response are ignored.

Is it possible to update MXHTTPClient to manage this case that is to say it will return a json containing all fields returned by server even if error code different than 200?

"duplicate symbol" error when using both OLMKit & use_frameworks in podfile

An error message occurs when trying to compile a project that uses pod 'MatrixSDK', pod 'OLMKit', and use_frameworks! in the podfile.

duplicate symbol _OLMKitVersionString in:
    [...]/Build/Intermediates/Pods.build/Debug-iphonesimulator/OLMKit.build/Objects-normal/x86_64/OLMKit.o
    [...]/Build/Intermediates/Pods.build/Debug-iphonesimulator/OLMKit.build/Objects-normal/x86_64/OLMKit_vers.o
ld: 1 duplicate symbol for architecture x86_64

In order for the pods to be visible to Swift, use_frameworks! must be set.

I created a two simple demo projects that illustrate this error โ€“ one in Obj-C and one in Swift (the projects do pretty much the same thing)

https://github.com/aapierce0/Dummy-Matrix-Client-Objc
https://github.com/aapierce0/Dummy-Matrix-Client-Swift

Update WebRTC pod

We are not using the last version which may crash less.

But the last version, 56.10.15101, does not work: libjingle complains about wrong internal state.

[MxRestClient] Update it to simplify category management

To add new client server requests, it is useful to create a category for MxClient.
In particulary, if you want to create a new loggin service or if you want to update access_token regularly and without needing to perform a logout/new loggin, some MxRestClient attributes should become public.
Proposal:
Add in MXRestClient.h a property for credentials
example: @Property (nonatomic) MXCredentials *credentials
Following attributes should also become public
httpClient
identityHttpClient
processingQueue
completionQueue

no such module "SwiftMatrixSDK"

Hello matrix team.
My XCode doesn't recognize "SwiftMatrixSDK", we added "pod SwiftMatrixSDK" into Podfile.
Swift version 4.
XCode version 9.0.
Thank you.

[e2e] Sometimes some events are not decrypted when importing keys

But they are decrypted when restarting the app.

The reason is a race condition between the main thread and the thread behind the MXCrypto.decryptionQueue when calling [MXMegolmDecryption retryDecryption].

Because of #205, MXCrypto.decryptionQueue locks the main thread when decrypting an event. In the case of [MXMegolmDecryption retryDecryption], there is no hacky lock of the main thread which create unpredictable behaviour.

Content prefix path.

When my home server something like this https://foo.org/something, and I want upload image, your method provide me path like "/_matrix/media/v1".

In MXHTTPClient try request method
NSString *URLString = [[NSURL URLWithString:path relativeToURL:httpManager.baseURL] absoluteString];

path start with /_matrix/media/v1.... and base url look like https://foo.org/something/ and we have result as https://foo.org/_matrix/media/v1.... (http://nshipster.com/nsurl/)

Could you make kMXContentPrefixPath changeable or properties?

MXRealmCryptoStore has duplicates of MXOlmInboundGroupSession

MXRealmCryptoStore has duplicates of MXOlmInboundGroupSession objects for the same pair (sessionId, senderKey).

We could merge the 2 columns "sessionId" and "senderKey" into one "$(sessionId)-$(senderKey)" to get uniqueness for the same pair.

MXRoom cleaning

Some properties should be moved to MXRoomSummary:

  • directUserId & isDirect
  • partialTextMessage
  • [MXStore paginationTokenOfRoom:]
  • [MXStore hasReachedHomeServerPaginationEndForRoom:]

Add a isCancelled property to MXHTTPOperation

Thus we could avoid to call mutateTo on an cancelled operation.

MXHTTPOperation could inherit from NSOperation that would help to mix HTTP requests and encryption/decryption requests but the feasibility needs to be checked.

All rooms are showing the same avatar

I uploaded an image from my mobile device to one of my rooms. Now that image is showing up as the avatar for all rooms that I am in.

Not sure if this is an sdk issue or a Riot iOS app issue. Ideas?

Limit thread switching when handling http request response

The current schema is:
AFNetworking thread => Main thread => MXRestClient processingQueue thread => Main thread

By using AFURLSessionManager.completionQueue, it could be:
AFNetworking thread => MXRestClient processingQueue thread => Main thread

If the sdk user defines a MXRestClient.completionQueue, it could even be:
AFNetworking thread => MXRestClient.completionQueue thread

MXFileStore: Aggregate commit operations

On an initialSync, there is a lot of data to store so that the 1st commit can take tens of seconds.
Each next commit will be queued and executed one by one once the 1st commit is done. Most of the time, these commits are small.

For performance, it would be interesting to aggregate these commits into a single commit operation in order to limit IO and thread switching.
More generally, we should apply this on coming commits where there is a commit that is processing.

macOS port

I'm interested in building a true native macOS client. Is there any reason this framework couldn't (or shouldn't) include a macOS build target?

I'm currently looking into adding it myself. A lot of the work appears to be replacing the UI* classes with their NS* counterparts, like so:

#if TARGET_OS_IPHONE
    UIImage* foo;
#elif TARGET_OS_OSX
    NSImage* foo;
#endif 

... and then #import <Cocoa/Cocoa.h> instead of <UIKit/UIKit.h> when the platform is macOS

I notice that the OLMKit pod currently doesn't support macOS, but after a very brief (10 min) skim through that project, it looks like that would be portable to macOS as well. However, since it's hosted on matrix.org instead of github, I'm not sure what the protocol is for contributing.

MXRoomSummary step #3

Once #310 is done, we can attack:

[MXFileStore] Loaded rooms summaries data of 210 rooms in 804ms

Apps probably do not need to displays all these rooms at once. We could decrease this time at sdk startup by paginating room summaries or by lazy loading them.

[e2e] Tight loop of /keys/query requests

This happens when logging in with an account which is in a room like #megolm:sw1v.org.

/keys/query on the devices in this room returns a non empty failures field like:

    "afaultyhs.org" =     {
        message = "Not ready for retry";
        status = 503;
    };

In this case, the ios (and android) code will retry to download the failed keys at the next /sync completion and so on.
matrix-js-sdk ignores the failures field so it does not have this issue.

MXRoomSummary step #2

Now that app can cache data relative to a room into MXRoomSummary objects, we should not need to load anymore all rooms states, all rooms messages and all rooms account data at startup. We should just need to load all MXRoomSummary which will make app startup quicker. The app will also consume less RAM.

Here are some figures with my account:

[MXFileStore] Start data loading from files
[MXFileStore] Loaded room messages of 210 rooms in 869ms
[MXFileStore] Loaded room states of 210 rooms in 2562ms
[MXFileStore] Loaded rooms summaries data of 210 rooms in 804ms
[MXFileStore] Loaded rooms account data of 210 rooms in 610ms
[MXFileStore] Loaded read receipts of 210 rooms in 923ms
[MXFileStore] Loaded 12352 MXUsers in 600ms
[MXFileStore] Data loaded from files in 6370ms

Loading room state is the longest but without a clear cache (or a limited timeline in a /sync response), there are more and more messages in the store and loading room messages takes longer and longer.

The change implies asynchronous access to :

  • MXRoom object. It still exposes data got from the store and MXRoomAccountData.
  • MXEventTimeline, providing room messages. It is already asynchronous but it is based on a MXStore that has all data preloaded.
  • MXRoomState, the room state.

I suggest to keep things in memory when they have been asynchronously opened. It cannot be worse than keeping everything in memory as we do atm.

These objects must be opened, or populated on demand. There are 2 cases:

  • the user who wants to open a room in the app.
    The room will be a bit slower to open but the timing should be reasonable.
  • a /sync response contains data about a room.
    The additional time to open that room will be transparent to the end user because it will be part of the time to process the /sync response.

[e2e issue] Decrypt error related to new device creation

I find another issue related to e2e and new device creation
B has device device_1
In the past B and C has joined same rooms but they have no more room in common now
-> So for C, list of device of B is [device_1]

B starts application on a new device called device_2
-> C is not notified of this new device because no more common room with B

A invites B and C in room XX
B is the first to accept the invitation from device_2
B checks all room members who has joined the room XX to notify a a new device

B only sends a message new device to A because A is the only one who has already joined the room.
(request matrix.whitenoise.ch/_matrix/client/unstable/sendToDevice/m.new_device)

C accepts the invitation
C checks all room members who has joined the room XX and from now will check new device announcement from A and B but not before
So for C, list of device of B is still [device_1]
So when C sends a message in room XX, it cannot be decrypt from device_2.
If my analysis is correct, is it an already known issue?

Update GZIP pod to 1.2

I tried but:

  • Jenkins/Flywheel (in Xcode 8.2) cannot built it
  • MatrixSDK tests explode unexpectedly in MXRealmCryptoStore. Realm generates weird exceptions at init. Those exceptions can be skipped by removing all primaryKey from the tables (?)

Support `m.direct` event in account_data

see element-hq/element-web#2070:

We've finally defined the behaviour for tracking 1:1 rooms:

the idea is to use User account_data to track the list of DMs we see for a given remote user
which would have a m.direct event in it, whose content gives a bunch of mxid to list-of-roomid mappings (we could split these into separate m.direct events, but these feel ugly)
for bootstrapping: the first time Vector loads without any m.direct event, i'd expect it to synthesize one from the current heuristics of what counts as a 1:1 room.

Emoji keyboard crash

When running the sample app in the simulator I got the following crash:

2015-01-09 23:30:58.546 matrixConsole[19260:167120] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '4246239423_iphone-emoji-control-keylayout: An -observeValueForKeyPath:ofObject:change:context: message was received but not handled.
Key path: center
Observed object: <UIInputSetHostView: 0x7fe3fac21c50; frame = (0 667; 375 258); layer = <CALayer: 0x7fe3fac2de60>>
Change: {
    kind = 1;
}
Context: 0x0'
*** First throw call stack:
(
    0   CoreFoundation                      0x0000000106382f35 __exceptionPreprocess + 165
    1   libobjc.A.dylib                     0x0000000105915bb7 objc_exception_throw + 45
    2   CoreFoundation                      0x0000000106382e6d +[NSException raise:format:] + 205
    3   Foundation                          0x00000001052bf78f -[NSObject(NSKeyValueObserving) observeValueForKeyPath:ofObject:change:context:] + 73
    4   Foundation                          0x00000001051ee9a6 NSKeyValueNotifyObserver + 356
    5   Foundation                          0x00000001051edbcd NSKeyValueDidChange + 466
    6   Foundation                          0x00000001051f228f -[NSObject(NSKeyValueObserverNotification) didChangeValueForKey:] + 118
    7   UIKit                               0x0000000106b0ff04 -[UIView(Geometry) _applyISEngineLayoutValues] + 855
    8   UIKit                               0x0000000106b1001e -[UIView(Geometry) _resizeWithOldSuperviewSize:] + 150
    9   CoreFoundation                      0x00000001062bb042 __53-[__NSArrayM enumerateObjectsWithOptions:usingBlock:]_block_invoke + 114
    10  CoreFoundation                      0x00000001062ba76f -[__NSArrayM enumerateObjectsWithOptions:usingBlock:] + 255
    11  UIKit                               0x0000000106b0ea60 -[UIView(Geometry) resizeSubviewsWithOldSize:] + 143
    12  UIKit                               0x000000010712a091 -[UIView(AdditionalLayoutSupport) _is_layout] + 143
    13  UIKit                               0x0000000106b13ceb -[UIView(Hierarchy) _updateConstraintsAsNecessaryAndApplyLayoutFromEngine] + 560
    14  UIKit                               0x0000000106b1f973 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 521
    15  QuartzCore                          0x0000000106931de8 -[CALayer layoutSublayers] + 150
    16  QuartzCore                          0x0000000106926a0e _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 380
    17  UIKit                               0x0000000106b13847 -[UIView(Hierarchy) layoutBelowIfNeeded] + 611
    18  UIKit                               0x0000000107174a17 -[UIInputSetHostView layoutIfNeeded] + 105
    19  UIKit                               0x0000000106b175ce +[UIView(Animation) performWithoutAnimation:] + 65
    20  UIKit                               0x00000001071755d0 -[UIInputWindowController performWithoutAppearanceCallbacks:] + 28
    21  UIKit                               0x00000001071755fd -[UIInputWindowController performWithoutCallbacksOrNotifications:] + 35
    22  UIKit                               0x0000000107178b60 -[UIInputWindowController updateToPlacement:withNormalAnimationsAndNotifications:] + 176
    23  UIKit                               0x0000000106f6dfe5 -[UIInputViewAnimationControllerBasic prepareAnimationWithHost:startPlacement:endPlacement:] + 33
    24  UIKit                               0x0000000107177ecf -[UIInputWindowController moveFromPlacement:toPlacement:starting:completion:] + 590
    25  UIKit                               0x000000010717bdc2 -[UIInputWindowController setInputViewSet:] + 599
    26  UIKit                               0x0000000107177a7a -[UIInputWindowController performOperations:withAnimationStyle:] + 50
    27  UIKit                               0x0000000106f67bce -[UIPeripheralHost(UIKitInternal) setInputViews:animationStyle:] + 1054
    28  UIKit                               0x0000000106c1d31d -[UIResponder becomeFirstResponder] + 468
    29  UIKit                               0x0000000106b12e03 -[UIView(Hierarchy) becomeFirstResponder] + 99
    30  UIKit                               0x00000001071d9ad7 -[UITextField becomeFirstResponder] + 51
    31  UIKit                               0x0000000106e619c1 -[UITextInteractionAssistant(UITextInteractionAssistant_Internal) setFirstResponderIfNecessary] + 177
    32  UIKit                               0x0000000106e63a30 -[UITextInteractionAssistant(UITextInteractionAssistant_Internal) oneFingerTap:] + 2263
    33  UIKit                               0x0000000106e592e6 _UIGestureRecognizerSendActions + 262
    34  UIKit                               0x0000000106e57f89 -[UIGestureRecognizer _updateGestureWithEvent:buttonEvent:] + 532
    35  UIKit                               0x0000000106e5cba6 ___UIGestureRecognizerUpdate_block_invoke662 + 51
    36  UIKit                               0x0000000106e5caa2 _UIGestureRecognizerRemoveObjectsFromArrayAndApplyBlocks + 254
    37  UIKit                               0x0000000106e52b1d _UIGestureRecognizerUpdate + 2796
    38  UIKit                               0x0000000106aecff6 -[UIWindow _sendGesturesForEvent:] + 1041
    39  UIKit                               0x0000000106aedc23 -[UIWindow sendEvent:] + 667
    40  UIKit                               0x0000000106aba9b1 -[UIApplication sendEvent:] + 246
    41  UIKit                               0x0000000106ac7a7d _UIApplicationHandleEventFromQueueEvent + 17370
    42  UIKit                               0x0000000106aa3103 _UIApplicationHandleEventQueue + 1961
    43  CoreFoundation                      0x00000001062b8551 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
    44  CoreFoundation                      0x00000001062ae41d __CFRunLoopDoSources0 + 269
    45  CoreFoundation                      0x00000001062ada54 __CFRunLoopRun + 868
    46  CoreFoundation                      0x00000001062ad486 CFRunLoopRunSpecific + 470
    47  GraphicsServices                    0x0000000109e189f0 GSEventRunModal + 161
    48  UIKit                               0x0000000106aa6420 UIApplicationMain + 1282
    49  matrixConsole                       0x0000000104d4cf23 main + 115
    50  libdyld.dylib                       0x0000000107fc8145 start + 1
    51  ???                                 0x0000000000000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException

Swift version?

Hi...

Do you plan release a Swift version of the SDK?

Thanks

Create subspecs

for:

  • MatrixSDK/JingleCallStack: that provides the MXJingleCallStack voip wrapper
    The app will not need to set up the stack by itself
  • MatrixSDK/GoogleAnalytics: to send some sdk stats to a GA account
  • MatrixSDK/UIKitBackgroundTask: to provide the default background task handler which works only with UIKit.

MXJingleCallStackCall's createLocalMediaStream method freezes

Hi,

I am using Matrix SDK framework using Pods in iOS and the version is "0.9.2".

Description of the issue:
Once I start a video call it works as expected but for consecutive calls, when MXJingleCallStackCall's createLocalMediaStream is called, it calls RTCPeerConnectionFactory class's method avFoundationVideoSourceWithConstraints which freezes for almost 15 seconds and then returns the video source. 'nil' is passed as its argument, I am not sure what causes this issue.

What is the expected result?
RTCPeerConnectionFactory class's method avFoundationVideoSourceWithConstraints should not freeze.

What do you see instead?
UI hangs on video call once RTCPeerConnectionFactory class's method avFoundationVideoSourceWithConstraints is called but not for first time.

Please look into it and provide a solution asap for the same.

Thanks in advance.

Room Summary Notification Count is not computed correctly until entering a room with at least one message

The notification Count property of a room's summary does not compute correctly unless we have joined / entered a room. This problem occurs with the Matrix KIT and is also present in Riot.

How to replicate:

  • Create a Room (Member A) and invite one or more members (Member B and Member C).
  • Before anyone has joined or sent any messages, go back to the Room List screen
  • Send messages with Member B or Member C to the room
  • The Notification Count of the Room Summary for A will never increment above 0
  • After the First time that Member A opens the room where there are already some messages, Member A will have the correct value in the Notification Count of the Room Summary.

Pod file problem in sample

Getting the following error when trying to run the sample app:

diff: /../Podfile.lock: No such file or directory
diff: /Manifest.lock: No such file or directory
error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.

I'm new to CocoaPods so hopefully this is a simple things.

Thanks

MXFileStore: Less threads switching

Each [MXFileStore saveXXX] operation (they are currently 7 save operations: saveRoomsMessages, saveRoomsState, ... ) posts a block on the file store processing queue.
That means that for one [MXFileStore commit], we can launch 7 times the file store processing queue thread. (This is actually 8 because the [MXFileStore commit] does an additional dispatch_async).

That makes a lot of thread switching which can penalise performance.

The code can be refactored to go only once on the file store processing queue.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.