Telegram IOS Source Code Tutorial Part 2: What is SSignalKit?

Next article No. 1, in this article we will study SSignalKit.

You can refer to the English version here:

https://hubo.dev/2020-05-11-source-code-walkthrough-of-telegram-ios-part-2/

Telegram-iOS uses Reactive programing in most of their modules. The following are the 3 main frameworks that use rx in the project:

This post focuses on SwiftSignalKit to explain how to design with use cases. About Rx programming is a relatively new mindset compared to object-oriented programming that most developers already know. So you should go to its homepage to understand its algorithms in a deep, intuitive way here:

https://rxmarbles.com/

Signal

Signal is a class to explain the concept of "change over time" – real-time event processing. It is described below:

To install Signal, it accepts 1 closure, including the data type that will spawn T, error spawn E, completion state. When it registers, the start function will allow registering 1 observer closure.

Subscriber

Subscriber contains the logic of each observer closure with thread safety:

A subscriber will terminate when there is an error or receive the completed event. Subscriber status will not be able to receive additional:

  • putNext sends new data to next closure until the subscriber has not been terminated.
  • putError sends an error error to the closure error and marks that the subscriber has terminated.
  • putCompletion calls the completed closure function and the subscriber markup has ceased.

Operators – operators

A rich set of algorithms is defined on Signal. These characters are grouped based on their functions: Catch, Combine, Dispatch, Loop, Mapping, Meta, Reduce, SideEffects, Single, Take, and Timing.

Let's take some group mapping as an example:

Like the map() function, they convert closure functions to Signal's new closure data type. The |> to help the above |> into pipes.

The |> can be inspired by the pipeline operator of the java script world. With swift's back support, all the algorithms read visually:

Queue – queue

Queue is the wrapper class on the GDC for queue management to use to dispatch data in Signal. There are 3 types of queues used: globalMainQueue, globalDefaultQueue,and globalBackgroundQueue.

There is no mechanism to limit excessive sending to queues, which we think can improve.

Disposable

Protocol Disposable defines something that can be processed, such as canceling resources or cancelling tasks. There are 4 protocols that can cover all possible use cases: ActionDisposable, MetaDisposable, DisposableSet,and DisposableDict.

Promise

The Promise and ValuePromise classes are built for a situation where many observers are interested in the same data source. Promise supports using Signal to update data, while ValuePromise is defined for accepting live data editing.

Use Cases

Consider some actual cases used in the project, demo for the use of SwiftSignalKit.

#1 Request Authorization

Ios applications want to access sensitive information such as contact, camera, location.. subject to permission from the user. While chatting with friends, Telegram-iOS has the feature of sending location as a message. Take a look at it authorized to get location through Signal.

The asymable steps of this authorization are described by SwiftSignalKit. The authorizationStatus function in the DeviceAccess file.swift returns 1 Signal to check the current authorization state:

When LocationPickerController displays, it checks authorizationStatus and launches DeviceAccess.authrizeAccess if the permission is in a no determined state.

Signal.start returns a Disposable instance. The best way is to keep it by 1 variable and dispose it in the deinit function:

#2 Change Username

For a more complex example, telegram uses UsernameSetupController to change the username. Usernames to create public links, so that others contact them.

UsernameSetupController

The implementation must meet the following requirements:

  • Controller must start with the current theme and current username. Telegram is a powerful theme system, all controllers must have themes.
  • The input string needs to be validated local length and its characters.
  • The name, once authenticed locally, is sent to the backend to check its availability. The number of requests must be limited when the user enters too fast.
  • UI feedback needs to follow the input input. On-screen notification screen message status of username: checking, valid, invalid, available or not available. The right navigation button will be turned on when the username is available.
  • When the user wants to update the username, the Indicator must be displayed to announce that the process is updating.

There are 3 data sources that may change over time: theme, current account, revision status. Themes and accounts are the basic data components of the application, so they have their own signals: SharedAccountContext.presentationData and Account.viewTracker.peerView. I will try to present them in another post. Let's focus on the state of change described by Signal step by step.

#1. Struct UsernameSetupControllerState defines data with 3 components: input text input, validation status and updating flag. The functions helper provides for it to update and retrieve 1 new instance:

The state change is provided by statePromise in ValuePromise, which also provides a concise feature to ignore repeated data updates. There is also stateValue that holds the latest values because ValuePromise's data is not exposed. It is a common pattern in the project with promise values associated with state values. Designing internal value reads can be an improvement of ValuePromise IMO.

#3 The validation process can be implemented by piped Signal. The delay algorithm holds the request delay for 0.3 seconds. If entered too quickly, the previous requests will be cancelled by step 4:

#4 a MetaDisposable keeps the Signals, and updates the data in statePromise and stateValue when next changes the value of TextFieldNode. When running checkAddressNameDisposable.set(), the previous function is removed by canceling the task inside the delayed function at step #3.

TextFieldNode is a child class of ASDisplayNode and wraps of UITextField to enter text. Telegram-iOS leverages asym asymable porting from AsyncDisplayKit to handle complex UI that becomes smooth and responsive.

#5 combineLatest algorithms are used to combine 3 Signals to update UI controllers as they change.

Conclusion

SSignalKit is Telegram-iOS reactive programing solution. Core components, such as Signal and Promise, are implemented in a slightly different approach than other reactive frameworks. It is commonly used on modules used to connect UI and change data.

The designs that are recommended for use are closures. There are many nested closures, and make the indentation backwards. The project also uses a variety of closure actions for more flexibility. For me, it's a miracle that Telegram engineers still maintain the best Telegram-iOS code quality for debug Signals in a simple way.

Leave a Comment