At one fine moment, when at the interviews I was already convinced that I was a senior iOS developer - I had the feeling that I was rested. I am writing similar code, solving problems in similar ways and the feeling that it is not clear where to develop further. I think it’s not just me who faced this problem - the lack of new ideas, concepts, directions. I would like to tell you about those tools and frameworks that helped me overcome this feeling.
I think most of the developers present here have read guys like the gang of four. Everyone, at least during the interviews, heard the word pattern, someone more (or less) lucky heard the more terrible words - imperative, functional, monad, reactivity and other horrors. In general, quite a lot of bright and interesting ideas are in the world of software development and, fortunately, not all of them exist only in the form of verbal abstractions. I would like in this article to tell a little not so much about the applied toolkit (although it is with him that we encounter most of the working time), as about the examples of the toolkit, for the use of which understanding is needed, which significantly helps in the future. I would like to talk about how (and what) tools change the design process itself, writing the code, at least did it for me.

This article, in my opinion, will be quite difficult for junior-level developers, but here are some wonderful tools - reading the code and examples of which can greatly push you toward development. More experienced developers, for sure, a significant part of this information is known, but, I believe that there is something useful for you. If only because I, to my shame and dismay, met most of the things described in the article rather late.
')
In general, as practice shows, the most useful solutions are usually quite simple. The main optimizations are almost always not in the choice of an algorithm with the asymptotics O (n) instead of O (n log n), but in the choice of the way the code base is organized. If you spend less time fighting your own code, you spend more time improving it. Therefore, lately, I try to devote more time to organizing code than I’ve been using the 201st batch that implements PullToRefresh on a UITableView. And I would like to share some of these ways I know.
- In the first and smallest chapter, I will start with three not very complex, but useful frameworks. Rather, so that was where to start, without diving immediately to the full depth.
- cocoaPods
- MagicalRecord
- Promise Kit
- In the second chapter, I will list the frameworks that can be put at the heart of your application, or some essential part of it - ideas and concepts that seriously affect the entire development process. In order to start using them fully, I would recommend writing at least a couple of small applications based on them - because they have a noticeable effect on thinking when designing new functionality.
- Pure MVC
- Ash framework
- Reactive cocoa
- componentKit
- The third chapter is devoted to testing and allowing you to write test code.
- Chapter four is devoted not so much to the frameworks that you can embed in your code, but also to tools that can facilitate your work with the code.
- mogenerator
- fastlane
- YACC / LEX
In advance, I beg your pardon. As Bilbo Baggins said:
A good half of you, I know twice as bad as they should, and I like the thin half half as much as I should.
Here I am also - in this article there will not be a sufficiently detailed analysis of any of the proposed tools (there is not even a single line of code here!) - only subjective sensations and recommendations for use. And there are a lot of tools and each of them deserves a separate analysis.
BUT! there are these analyzes - and for each tool I tried to indicate where you can find examples of its use, documentation and related information - well, it is really more than enough.
So, let's begin.
Chapter 1. Introduction
cocoaPods
Probably there are no people who have not heard about this tool. A tool for managing external dependencies (third-party) for your project.
You can read about it
here . I will not particularly write here about him, because everyone knows everything very well. And for those who do not know, you can read
here and take note that this tool for iOS developer is a must have. The transition to its use significantly reduces the time of prototyping new applications, and writing your own pods significantly disciplines the process of creating reusable code.
Virtues- Incredibly easy project dependency management. Adding new is made only one line.
- The ability to package your reusable code into convenient and simple containers to facilitate its reuse.
- Separate the code of dependencies from the code of your application - which reduces the temptation to change the code of the third-party toolkit (Sometimes, of course, it is useful and even necessary. Before the first update of the dependency module) and makes organizing the code of these add-ons more carefully and “from the side”.
disadvantages- The process of adding dependencies is simplified so much that now you need to carefully monitor that the project does not add too many of these dependencies for each other. And if this is appropriate for prototyping, then for long-lived projects this is an important question, since not all third-parties are perfect, but you are the ones who are responsible for their mistakes to their users.
- If you want something strange, then you will sometimes have to dig around with it. For example, at some point it took me a substantial amount of time to teach the project how to correctly build different architectures for different targets. Perhaps it has become easier and more comfortable now, but the fact remains.
MagicalRecord
The framework for working with databases, which is a wrapper over CoreData. Read more about it
here .
MagicalRecord helps you start using CoreData, even if you avoided it in every way before. Someone once compared using MagicalRecord without understanding how CoreData works with enjoying a cup of coffee in a burning house - and I can understand it, but in my personal experience - it was much easier for me to understand the subtleties of CoreData just by reading and using MR code in practical tasks.
MR itself is an adaptation of the concept of
ActiveRecord for Objective-C. This concept shifts the focus when working with databases from databases and tables to individual records, linking to them the main operations on working with the database. That is, instead of “Table - delete this record”, we say “record - succeed”, or “record - create, change, give me all the instances corresponding to your entity”.
Its use can significantly reduce the amount of code in your wrapper over the database. And besides, it allows you to write much simpler and safer code for working with the database.
Virtues- Perfectly hides many CoreData rakes from you, significantly reducing the code needed to initialize, save, modify records. Simplifies working with child contexts.
- Initially, it is focused on asynchronous work with data, which means that standard actions with it will not cause UI hangs.
- Low entry threshold - this framework will allow you to experience the merits of databases, but will not lead you through all the depths of hell.
disadvantages- Like all asynchronous parts of the code, it's pretty hard to cover it with tests.
- In addition, you, like in other ways of working with the database, will have to ensure, at the architecture level, the correctness of the database - that the application will not try to write and read in the same place at the same time (no, it will work well for this, but Here's the result you do not really like it)
- It does not completely save you from writing predicates, queries, and other things (It’s not right to say that this is just a flaw, because it would be strange to expect this)
PromiseKit
A framework for working with such a concept as
Promise . You can read more about it
here .
In short, this is a way to organize work with asynchronous code in the form of chains of actions. Why do you need it?
1. Asynchronous code becomes simple and linear regardless of the thread in which it is executed. Before his eyes, his strict structure - what and why will be fulfilled. On one screen you can put the following type of logic: Download two configs. When they hit, check them for validity. After that, start some kind of animation, then display the results.
2. Asynchronous code becomes atomic, encapsulated, and reusable. Very often there are many temptations to break the encapsulation of asynchronous operations — use some common variables, for example — which makes asynchronous code more difficult to change.
3. A comfortable opportunity appears to handle the errors of the entire execution chain. Everyone who wrote chains of asynchronous code encountered the fact that the longer the chain is, the more difficult it is to correctly handle errors, the harder and more cumbersome the code becomes.
Virtues- Considerably simplifies the construction of the execution graph of asynchronous operations. Just to make one operation begin immediately after another or after several other
- Significantly simplifies the processing of errors that occur at different stages of the execution of the chain (especially in the case when the chain in the application makes sense only in case of complete success of all operations and does not make sense in all others)
- Allowing for intelligently encapsulating asynchronous operations — greatly facilitates their multiple use in different places of the application.
disadvantages- Still need to be careful with the memory when working with promises
- It takes some time to get used to working not with “code”, but with “a wrapper that will cause your code”
- Inconvenience during debag.
Chapter 2. Architectural frameworks or something.
PureMVC
You can read about it in detail
here .
Probably one of the first major architectural frameworks that I had to deal with while working on mobile applications.
In short, this is a formal breakdown of the MVC pattern into smaller entities:
1. VO (value Object) - it is also familiar from CoreData Entity
2. Proxy - with its strange name, it nevertheless is a wrapper over a model - are responsible for data access and nontrivial getters (aggregators) of the model
3. Mediator - what is more accustomed to be considered a controller in MVC, controls the display in View and processes the signals coming from it. Can be subscribed to notifications sent through the facade.
4. View - well, View it is View. Code responsible for the visual presentation of information.
5. Facade - Singleton entity that exists all the time the application is running. All objects of the Proxy, Mediator and Command classes must be registered there.
6. Notification - the message sent through the facade. All mediators subscribed to it will receive it (and only they).
7. Command - that which describes the flow of the application - obeying the Command pattern - is started, executed, completed without being held in memory, in essence representing a notification with the executable code.
Virtues- fantastic safety margin of the architecture - in my example, everything was poured into the code for several years and the development did not stand up and survived until the possibility of deep refactoring and the appearance of adequate technical processes appeared.
- Rapid prototyping - the architecture is already well-marked for you. And if you have experience with this structure, you can quickly, but in a modular and iterative form, increase the functionality of the application.
- A very formal structure - if you started using it in the project - it is rather difficult to step aside from it - which leads to the fact that all project developers write the code uniformly.
disadvantages- Provides a huge number of opportunities to write bad code, due to the fact that all critical modules, in fact, are singletones (in this case, registered in the singleton-facade), which means that the project very quickly has all the chances to acquire implicit dependencies
- Hell debug. There is very little explicit binding, respectively, in order to go through the execution chain, you need to follow the line that symbolizes the entity, find the place where this entity is registered, find the class associated with it and go to it. Repeat until ready. The depth of the stack nesting reaches quite cosmic values ​​(I saw hundreds with my own eyes)
Ash framework
You can read about it in detail
here . You can also find examples of use.
Specifically, this framework is more suited to game development, but if at some point you can say that your application has ceased to be stateless, then getting to know this thing will be at least informative.
In short, this is a framework that implements the Entity-Component-System pattern and operates on the following set of entities:
1. Entity - in fact, some entity (for simplicity of description, I will use examples from the game subject in this case, because they are more suited to this framework). For example - a player, enemy or obstacle. In addition, entities may also be not so obvious actors (active objects) - for example, popup Options, lifeBar, or, for example, the whole level. Operates with the minimum information, associated with a certain set of components.
2. Component - some atomic data container. For example - position, speed, destructible, ...
3. Filter - Some filter in order to be able to get not the entire set of Entity - but only those that pass it. It is a set of keys component. Thus, if the filter contains the keys “position” and “speed” - then entities that do not move, or non-gaming entities - will not pass it.
4. System - description of the nature of changes of entities. Operates with a filtered set of entities and somehow converts it. For example, a gravity system. Takes entities passing through the filter — have a position, speed, acceleration, exposure to gravity — and recalculates their accelerations taking into account the effect of gravity.
Or MovementSystem - takes all entities that have a position, speed and acceleration - and change speed by the amount of acceleration, and position, by the amount of speed
5. World - some object that requests an update of all systems in a certain order.
In essence, this framework offers to the behavior of objects from a somewhat unusual point of view for a classical OOP — not an object reacts to changes, but the world finds objects that can react and change them independently, using systems.
Virtues- It forces to break logic into orthogonal entities, to think in criteria of modules that do not interfere with each other.
- Ease of changing characters (behavior), Ease of adding new ones.
- The state machine is built into the framework by design. This way you can switch between whole sets of characters and reactions.
disadvantages- This is not a silver bullet. The state machines quickly gain difficulty and control over the code is lost. It requires a very clear understanding of what and how much it should be.
- The orthogonality of the modules and full stateless is a very beautiful concept in the head, in practice it is very difficult to maintain it. And any shared data outside the component very quickly leads to a lot of difficulties and possible bugs like race condition (albeit in one thread).
Reactive cocoa
Probably one of the most significant third-party frameworks for iOS development of the existing now. You can
read about it in detail
here and in
Habré - and I strongly recommend it. He proposes to use some replacement of the basic iOS pattern (MVC) with its
MVVM counterpart and relies on
reactive programming in its use.
In short, Reactive Programming is a development paradigm focused on working with data streams. If the usual development (imperative), the developer programs in the style of "the program should make A, then make B, then make C". If A, B, and C are significant logical operations located in various application modules — as the number of such blocks grows, the coherence of programs increases, their complexity and size increase.
The most famous example of such a binding of various modules for long-running operations is the delegation pattern, but it does not solve all the problems.
- What if we need the completion of this operation to initiate several others at once?
- What if we need to, at various points in the operation of the application, this operation can launch various entities?
- The code associated with a single operation is scattered across a large number of modules and its modification is difficult.
- Adding new operations to the chain of actions (especially in its middle) is usually associated with a sufficiently large amount of code.
What does Reactive Cocoa and MVVM offer:
- Operations are combined into modules, in the interfaces of which wrappers are rendered on the executable code, so-called signals and commands.
- Unlike classic MVC, the signal-based architecture allows you to associate associated entities directly, without layers.
For myself, I consider ReactiveCocoa the development of PromiseKit ideas, extending them to the level of application architecture.
Besides the fact that ReactiveCocoa inherits the advantages and disadvantages of PromiseKit - I would like to add the following:
Virtues- Sequences of application actions take less code, they are easier to grasp, and therefore easier to understand the operation of the application.
- Ease of manipulation with sequences of actions - it is easy to add an intermediate step, add or remove a side step.
disadvantages- Debugging code with Reactive Cocoa is a lot of trouble.
- The ease of working with signals provoke an uncontrolled increase in the connectivity of the application and it is very easy to lose control over its execution when the chains become long and intersecting among themselves.
- Like many other frameworks of this kind - it is better to avoid it in the critical sections of your application - it is not very fast.
componentKit
A recent gift from the Facebook development team, which you can read more about
here . I do not know about you, but more than once I looked at HTML and CSS with sadness - as ways of the task of interfaces. That is, what is called a
declarative UI .
In short, according to Wikipedia, this is a way of describing entities that are not focused on “how to create it”, but on “what it will be like when it was created”. ComponentKit is the development of ideas, the emergence of which in iOS development can be seen in Avtoleayaute - the concept by which - by changing one interface element - there is no need to do many operations so that the rest of the UI remains to look correct. If in the old UIKit by changing the size of some UIView, there was a need to recalculate the positions of all the other UIView relative to it, which sometimes resulted in a nontrivial code for working with coordinates, now it suffices to say that this View will be 20 pixels from View, which is higher and 30 - which is lower - and no matter how we change its size - this ratio will remain unchanged.
ComponentKit went further in this regard - it offers a fairly large number of solutions for standard entities, the work with which used to be a lot of hassle:
- Convenient work with CollectionView / TableView, which involves working not so much through a fixed dataSource, as through filling the table, like the usual work with arrays. (Instead of “according to such an index-pass, we have such a cell” - “put such-and-such a cell on such a place”). If you had to build tables with non-uniform data (for example, building a page-description of housing in an application for renting real estate - where there can be a dozen different types of cells with different behavior on one page)
- The state of elements makes it possible to describe several characters at once, which an element can take depending on the context, without having to distribute it to different .xib, different classes, and so on.
- Convenient adaptive layout, which allows to declare collections of elements that are equally suitable for different device resolutions and screen orientation in a declarative style (all this can be done with AutoLayout - but declarativeness is much more flexible)
Virtues- Description of the interface in the code, but in a concise and adaptive form.
- Sophisticated solutions for standard components
disadvantages- Immunity components - if you want to make a changeable interface with a large number of animations, then this is not your choice.
- The newness of the framework is, of course, a relative disadvantage, but problems with its use may well arise.
Chapter 3. Testing
About testing says a lot and different. Good and bad. Someone says that testing is a silver bullet, someone, on the contrary, it takes time, but in my opinion - this is one of the key stages in the development of programmers - and before you can judge about it - it is necessary pass the.
I would also recommend reading this recent
article - it offers a superficial but pleasant overview of what tools in this regard are available to iOS developers.
If to speak seriously - TDD is probably the most strongly influenced me, as a developer, concept. And the point is not that autotests allow you to avoid regression, allow you to write code faster, less worried because of the changes. The main advantage of autotests is that not every code can be tested. It is through testing that abbreviations such as
DRY and
SOLID make sense.
Expecta
You can read about it in detail on the page of its
repository , as well as its parent
repository . There is also a sufficient number of examples of its use - enough to start using it.
If in your code:
1. There are many implicit dependencies (for example, singletons that you jerk with or without) - this code is incredibly difficult to test, because you need to satisfy all these dependencies. And from the point of view of application development, a change in the code of these most implicit dependencies will affect an unknown number of places in the code in an unknown way. Definitely bad.
2. If there are more non-stateless connections in your code, this code is difficult to test, because you need to test entire bundles of modules in all possible combinations of their steates (that is, the number of test cases grows exponentially). And from the point of view of the development of the application - in each of the combinations of states there can be some kind of bug, and the risks that the linked module will not be in the same state that we expect from it are still very high. And all additions to the code have a very, very high price.
3. If your code is not DRY, then you have to write tests for each of the repeated pieces of code, which increases the amount of work. And from the point of view of application development, the difficulty of maintaining the working capacity of a repeating code is proportional to the number of repetitions.
And many many others.
Thus, it turns out that the code being tested is in itself more supported, simpler in itself, simpler when making changes. And in general, in itself, it saves time.
And the best way to check that your code - we test - is to cover it with tests. If I understand correctly, over time, the need for most tests disappears, because you are already writing out the test code out of habit, but you still have to get to that time.
So, a bunch of Specta / Expecta is a framework for writing tests in the style of
BDD .
Virtues- The BDD approach itself is good because you have in one source both the testing code and the specification that the testing corresponds to. On the basis of it, it is possible to generate documents that are understandable not only to IT specialists, which can be discussed both with QA specialists and with customers from the business side.
- The framework is simple and elegant, which allows you to leave the tests easily readable and reduce the risks of supporting the tests (it becomes not so necessary to test your tests)
disadvantages- In fact, when testing real-world applications, the rakes on rakes are lying - and you have to go through all these rakes. Testing asynchronous databases, testing the UI and generally testing something asynchronous requires serious thought and thinking.
- A small and, I hope, temporary drawback - the integration with the XCode of this tool has been unstable lately.
OCMock
You can read about it in detail
here .
Unfortunately, when testing, it is not always possible to completely eliminate external and implicit dependencies of a module without significantly complicating the code. And here OCMock comes to the rescue - it allows you to emulate the behavior of external objects (including system frameworks), isolating your module from all external influences (network connection speeds, unexpected errors) and allows you to work with a guaranteed pure context (the server will return , , , , NSUserDefaults , , , ).
«» SOLID Enterprise — - . « » « ».
Blood Magic
, —
1101_debian ,
.
, , .
, . ( , . — )
Dependency injection — .
— , , , — — . , .h , . - ( , , OCMock ). -, « ? ? - ?».
- , -. , , 4-5 .
- - — , , .
4.
mogenerator
, , .
. , , — .
— , , runtime, , , :
. Objective-C — Runtime — . , (DRY , , )
, ?
— ?
— ?
, ( , , ...).
-- , ( ).
, mogenerator , — . , , - .
fastlane
.
, , , continuous delivery .
:
, , , (AppleScript, Bash, Python, Ruby) . , . ,
DSL . , DSL ( ), , . FastLane — , DSL — .
- , .
- Continous delivery ( Jenkins, )
- , , , . ( Slack)
disadvantagesYACC/LEX
, , , DSL . , , :
, , :
- — , DSL -, JSON/XML/… .
- , , , ( - , -, - -.)
- DSL - ( ) . ( YACC/LEX — ObjC Swift Ruby ( DSL — , ))
, — .
disadvantagesInstead of conclusion
, , , , ( , , ), — ( !) , . , , - — .
, . Until.