
Mobile Banking for iOS: Adding Block Architecture to Cocoa MVC
If you are writing a mobile banking application for iOS, what are your priorities? I think there are two of them:
The situation is such that you need to be able to make changes (and in particular roll out new banking products) really quickly. But at the same time, do not slip into the Hindu code and copy-paste (see paragraph 1). All this despite the fact that the application is really huge in functionality, at least in the idea (banks want a lot more than they can). Accordingly, in many cases, these are projects for tens of man-years. Those who participated in such projects have probably already realized that the task is not trivial, and school knowledge will not help here.
It’s immediately clear that this is an architecture task. Before writing the code, I saw a simple and beautiful block architecture of the frontend of our RBS. I have not seen this before in mobile applications, but in the end I made a similar solution, quite effective and scalable, within the framework of the iOS SDK, without adding massive frameworks. I want to share the main points.
Yes, about all of these SDK frameworks, about Appleschools, the features of the MVC pattern and about quick code in the official documentation are known to everyone who tried to write and maintain serious applications (or at least read Habr ) - I won’t repeat it. There will also be no basics of programming, files with examples and photos with cats.

The first google picture for “advanced architecture”
Operations are made up of atoms.
An operation is an interface for performing a specific action in an ABS. For example, a transfer between your accounts.
Atom is a stable “capsule” of MVC, a kind of brick from which you can build houses of any size and shape. Each brick has its own essence in the UI: inscription, input field, picture. Each significant UI block is encapsulated in such an MVC brick. For example,
These MVC bricks are written once. Further, each operation is built from these bricks. Putting a brick is one line. One line! Next, get the value - this is again one line. Everything else is decided by inheritance and polymorphism in subclasses of Atom. The task is to simplify the code of the operations themselves, as they can vary greatly. Bricks do not fundamentally change (or even do not change at all ).
An atom can be a more complex element. It can encapsulate some logic and child

My concept is already used in construction
For example, the operation of sending a payment order “in quick form” (from the code I hid all the points related to security, and yes, the rights to the code belong to Digital Technologies of the Future LLC):
And this is the whole operation code !!! Nothing more is needed, provided that you have all the atoms you need. In this case, I already had them. If not, write a new one and draw on the storyboard. Of course, you can inherit from existing ones.
The last line
I do not provide class code
As you know, when entering any form, depending on the values of some fields, other fields can change or hide, as we saw in the example of the BIC above. But there is one field, and in full payment there are many times more, so you need a simple mechanism that will require a minimum of gestures to dynamically show / hide fields. How easy is this to do? Atoms come to the rescue :) as well
Option 1 :
This is an example for payments. A similar system works for electronic payments in favor of various providers, where depending on some choice (for example, pay by meters or an arbitrary amount), the user fills in a different set of fields.
Here the guys created a “certain framework” for these purposes, but somehow I had enough of a few lines.
Option 2 , if you lack the predicate language, or you don’t know it, then we consider the dependencies as a function, for example, isDestInnCorrect ():
I think maybe it will be more beautiful if you rename the property:
A text atom
Unfortunately, the compiler will not verify for you that this operation implements the method
And actually setting the visibility in the base class
Let's see what other checks will be. Perhaps the operation should not be involved, i.e. everything will be rewritten for the option
In general, with regard to object patterns in client-server interaction, I cannot but note the exceptional convenience when working with JSON, which is provided by the JSONModel library . Dot notation instead of square brackets for the resulting JSON objects, and immediately typing the fields, including arrays and dictionaries. As a result, a strong increase in readability (and as a result of reliability) of code for large objects.
We take a class
But all this is easily used from Swift code. The only thing is that the application does not crash due to an incorrect server response format, all
The concept of “atoms” really worked and allowed to create a good scalable architecture of the mobile bank. The operation code is very simple to understand and modify, and its support, while maintaining the original idea, will take O (N) time. And not the exhibitor into which many projects are rolling.
The disadvantage of this implementation is that the display is done in a simple way
And how do you fit your architecture into the iOS SDK on large projects?
- Reliability;
- Rate of change.
The situation is such that you need to be able to make changes (and in particular roll out new banking products) really quickly. But at the same time, do not slip into the Hindu code and copy-paste (see paragraph 1). All this despite the fact that the application is really huge in functionality, at least in the idea (banks want a lot more than they can). Accordingly, in many cases, these are projects for tens of man-years. Those who participated in such projects have probably already realized that the task is not trivial, and school knowledge will not help here.
What to do?
It’s immediately clear that this is an architecture task. Before writing the code, I saw a simple and beautiful block architecture of the frontend of our RBS. I have not seen this before in mobile applications, but in the end I made a similar solution, quite effective and scalable, within the framework of the iOS SDK, without adding massive frameworks. I want to share the main points.
Yes, about all of these SDK frameworks, about Apple

The first google picture for “advanced architecture”
Essence of the decision
Operations are made up of atoms.
An operation is an interface for performing a specific action in an ABS. For example, a transfer between your accounts.
Atom is a stable “capsule” of MVC, a kind of brick from which you can build houses of any size and shape. Each brick has its own essence in the UI: inscription, input field, picture. Each significant UI block is encapsulated in such an MVC brick. For example,
UISegmentedControl
encapsulated in SegmentedAtom
.These MVC bricks are written once. Further, each operation is built from these bricks. Putting a brick is one line. One line! Next, get the value - this is again one line. Everything else is decided by inheritance and polymorphism in subclasses of Atom. The task is to simplify the code of the operations themselves, as they can vary greatly. Bricks do not fundamentally change (or even do not change at all ).
An atom can be a more complex element. It can encapsulate some logic and child
ViewController
's. For example, an atom for selecting an account from the list (moreover, by filter from the context). The main thing is that all complexity remains inside the atom. Outside, it remains just as easy to use. 
My concept is already used in construction
For example, the operation of sending a payment order “in quick form” (from the code I hid all the points related to security, and yes, the rights to the code belong to Digital Technologies of the Future LLC):
class PayPPQuickOperation : Operation, AtomValueSubscriber {
private let dataSource = PayPPDataSource()
private var srcTitle, destBicInfo: TextAtom!
private var destName, destInn, destKpp, destAccount, destBic, destDesc, amount: TextInputAtom!
private var btnSend: ButtonAtom!
override init() {
super.init()
create()
initUI()
}
func create() {
srcAccount = AccountPickerAtom(title: "Списать со счета")
destName = TextInputAtom(caption: "ФИО или полное наименование получателя")
destInn = TextInputAtom(caption: "ИНН получателя", type: .DigitsOnly)
destKpp = TextInputAtom(caption: "КПП получателя", type: .DigitsOnly)
destAccount = TextInputAtom(caption: "Счет получателя", type: .DigitsOnly)
destBic = TextInputAtom(caption: "БИК", type: .DigitsOnly)
destBicInfo = TextAtom(caption: "Введите БИК, банк будет определен автоматически")
destDesc = TextInputAtom(caption: "Назначение платежа")
amount = TextInputAtom(caption: "Сумма, ₽", type: .Number)
btnSend = ButtonAtom(caption: "Перевести")
// Здесь можно быстро переставлять местами операции:
atoms = [
srcAccount,
destName,
destInn,
destKpp,
destAccount,
destBic,
destBicInfo,
destDesc,
amount,
btnSend,
]
destInn!.optional = true
destKpp!.optional = true
btnSend.controlDelegate = self // тап придёт в onButtonTap ниже
destBic.subscribeToChanges(self)
}
func initUI() {
destName.wideInput = true
destAccount.wideInput = true
destDesc.wideInput = true
destBicInfo.fontSize = COMMENT_FONT_SIZE
destName.capitalizeSentences = true
destDesc.capitalizeSentences = true
}
func onAtomValueChanged(sender: OperationAtom!, commit: Bool) {
if (sender == destBic) && commit {
dataSource.queryBicInfo(sender.stringValue,
success: { bicInfo in
self.destBicInfo.caption = bicInfo?.data.name
},
failure: { error in
// обработка ошибки, в данном случае не страшно
self.destBicInfo.caption = ""
})
}
}
func onButtonTap(sender: AnyObject?) {
// если несколько кнопок, то для каждого sender будет своё действие
var hasError = false
for atom in atoms {
if atom.needsAttention {
atom.errorView = true
hasError = true
}
}
if !hasError {
var params: [String : AnyObject] = [
"operation" : "pp",
"from" : srcAccount.account,
"name" : destName.stringValue,
"kpp" : destKpp.stringValue,
"inn" : destInn.stringValue,
...
"amount" : NSNumber(double: amount.doubleValue),
]
self.showSignVC(params)
}
}
}
And this is the whole operation code !!! Nothing more is needed, provided that you have all the atoms you need. In this case, I already had them. If not, write a new one and draw on the storyboard. Of course, you can inherit from existing ones.
onAtomValueChanged()
- this is the implementation of the protocol AtomValueSubscriber
. We subscribed to the changes in the BIC text field and there we make a request that returns the name of the bank by BIC. The value commit == true
for the text field comes from the event UIControlEventEditingDidEnd
. The last line
showSignVC()
is to show ViewController
for the signing operation, which is just another operation, which consists of the same simple atoms (formed from the elements of the security matrix that come from the server). I do not provide class code
Operation
because You may find a better solution. I decided (in order to save development time) to feed atoms
tabular ViewController
'y. All the "bricks" are drawn in IB table cells, instantiated by cell id. The table provides automatic height conversion and other amenities. But it turns out that now the placement of atoms is possible only in the table. For an iPhone it’s good, for an iPad it may not be very. So far, only a few banks in the Russian appstore correctly use the space on the tablets, the rest stupidly copy the UI of iPhones and only add the left menu. But ideally, yes, you need to redo it on UICollectionView
.Dependencies
As you know, when entering any form, depending on the values of some fields, other fields can change or hide, as we saw in the example of the BIC above. But there is one field, and in full payment there are many times more, so you need a simple mechanism that will require a minimum of gestures to dynamically show / hide fields. How easy is this to do? Atoms come to the rescue :) as well
NSPredicate
. Option 1 :
func createDependencies() {
// ppType - выбор типа платёжки, что-то вроде выпадающего списка (EnumAtom)
ppType.atomId = "type"
ppType.subscribeToChanges(self)
let taxAndCustoms = "($type == 1) || ($type == 2)"
...
// а это поля ввода:
field106.dependency = taxAndCustoms
field107.dependency = taxAndCustoms
field108.dependency = taxAndCustoms
...
This is an example for payments. A similar system works for electronic payments in favor of various providers, where depending on some choice (for example, pay by meters or an arbitrary amount), the user fills in a different set of fields.
Here the guys created a “certain framework” for these purposes, but somehow I had enough of a few lines.
Option 2 , if you lack the predicate language, or you don’t know it, then we consider the dependencies as a function, for example, isDestInnCorrect ():
destInn.subscribeToChanges(self)
destInnError.dependency = "isDestInnCorrect == NO"
I think maybe it will be more beautiful if you rename the property:
destInnError.showIf("isDestInnCorrect == NO")
A text atom
destInnError
, in case of incorrect input, tells the user how to fill in the TIN correctly according to the 107th order. Unfortunately, the compiler will not verify for you that this operation implements the method
isDestInnCorrect()
, although this can probably be done with macros. And actually setting the visibility in the base class
Operation
(sorry for Objective-C):- (void)recalcDependencies {
for (OperationAtom *atom in atoms) {
if (atom.dependency) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:atom.dependency];
BOOL shown = YES;
NSDictionary *vars = [self allFieldsValues];
@try {
shown = [predicate evaluateWithObject:self substitutionVariables:vars];
}
@catch (NSException *exception) {
// тут отладочная инфа
}
atom.shown = shown;
}
}
}
Let's see what other checks will be. Perhaps the operation should not be involved, i.e. everything will be rewritten for the option
let verifications = Verifications.instance
...
if let shown = predicate.evaluateWithObject(verifications,
substitutionVariables: vars) { ...
One more thing
In general, with regard to object patterns in client-server interaction, I cannot but note the exceptional convenience when working with JSON, which is provided by the JSONModel library . Dot notation instead of square brackets for the resulting JSON objects, and immediately typing the fields, including arrays and dictionaries. As a result, a strong increase in readability (and as a result of reliability) of code for large objects.
We take a class
JSONModel
, inherit from it ServerReply
(because each answer contains a basic set of fields), we ServerReply
inherit server responses to specific types of requests. The main disadvantage of the library is that it is not on Swift (because it works on some language life hacks), and for the same reason, the syntax is strange ...Piece of example
@class OperationData;
@protocol OperationOption;
#pragma mark OperationsList
@interface OperationsList : ServerReply
@property (readonly) NSUInteger count;
@property OperationData *data;
...
@end
#pragma mark OperationData
@interface OperationData : JSONModel
@property NSArray *options;
@end
#pragma mark OperationOption
@interface OperationOption : JSONModel
@property NSString *account;
@property NSString *currency;
@property BOOL isowner;
@property BOOL disabled;
...
But all this is easily used from Swift code. The only thing is that the application does not crash due to an incorrect server response format, all
NSObject
-fields must be marked as < Optional
> and checked for nil
. If you want your property so that it does not interfere JSONModel
, write them modifier < Ignore
>, or situation (readonly)
.conclusions
The concept of “atoms” really worked and allowed to create a good scalable architecture of the mobile bank. The operation code is very simple to understand and modify, and its support, while maintaining the original idea, will take O (N) time. And not the exhibitor into which many projects are rolling.
The disadvantage of this implementation is that the display is done in a simple way
UITableView
, it works on smartphones, and for tablets, in the case of an “advanced” design, you need to redo View and partially Presenter (immediately a common solution for smartphones and tablets). And how do you fit your architecture into the iOS SDK on large projects?