Next article No. 1, in this article we will study SSignalKit.
You can refer to the English version here:
Telegram-iOS uses Reactive programing in most of their modules. The following are the 3 main frameworks that use rx in the project:
- MTSignal: Can be considered reactive programing for Objective-C. It mainly uses mtprotokitmodule, implement MTProto – Telegram mobile protocol.
- SSignalKit: It is a descendant of MTSignal, with more complex processing and richer algorithms.
- SwiftSignalKit: similar to SSignalKit but it supports swift.
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:
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 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:
putNextsends new data
to nextclosure until the subscriber has not been terminated.
putErrorsends an error error to
the closure errorand marks that the subscriber has terminated.
the completedclosure function and the subscriber markup has ceased.
Operators – operators
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:
There is no mechanism to limit excessive sending to queues, which we think can improve.
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:
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.
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.
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.
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:
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.
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:
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.
TextFieldNodeis a child class of ASDisplayNode and wraps of UITextField to enter text. Telegram-iOS leverages asym asymable
porting from AsyncDisplayKitto handle complex UI that becomes smooth and responsive.
combineLatest algorithms are used to combine 3 Signals to update UI controllers as they change.
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.