Notes of the iOS programmer about his hammers, sledgehammers and micrometers

    One fine moment, when they were already convincing me during interviews that I was a senior iOS developer, I had the feeling that I was stubborn. I am writing similar code, I solve problems in similar ways and I feel that it is not clear where to develop further. I think this problem was not the only one I encountered - a lack of new ideas, concepts, directions. I would like to tell you about the tools and frameworks that helped me overcome this feeling.

    I think most of the developers here were reading guys like a gang of four. Everyone, even at the interviews, heard the word pattern, someone more (or less) lucky heard the terrible words - imperative, functional, monad, reactivity and other horrors. In general, quite a few bright and interesting ideas go around in the world of software development and, fortunately, not all of them exist only in the form of verbal abstractions. In this article, I would like to talk a little bit not so much about applied tools (although it is with him that we encounter most of the working time), but about examples of tools that require understanding, which will greatly help in the future. I would like to talk about how (and which) tools change the very process of designing, writing code, at least they did it for me.

    This article, in my opinion, will be quite complicated for junior level developers, however, wonderful tools are listed here - reading the code and application examples of which can push you to development very much. For more experienced developers, for sure, a substantial part of this information is known, but I believe that something useful will be found for you. At least because, to my shame and horror, I met most of the things described in the article quite late.

    In general, as practice shows, the most useful solutions are usually quite simple. The main optimizations almost always consist not in choosing an algorithm with the asymptotic behavior of O (n) instead of O (n log n), but in choosing a way to organize the code base. If you spend less time struggling with your own code, you spend more time improving it. Therefore, the last time I try to devote more time to organizing the code than the 201st pod that implements PullToRefresh on a UITableView. And I would like to share some of the ways I know.

    • In the first and smallest chapter, I will start with three not-so-complicated, but useful frameworks. Rather, in order to have something to start with, without diving immediately to the full depth.
      • cocoaPods
      • Magicalrecord
      • Promise kit

    • In the second chapter, I will list the frameworks that can be taken as the basis 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 to the full, I would recommend writing at least a couple of small applications based on them - because they significantly affect thinking when designing new functionality.
      • Pure mvc
      • Ash framework
      • Reactive cocoa
      • componentKit

    • The third chapter is about testing and what will allow you to write testable code.
      • Expecta
      • OCMock
      • Blood magic

    • Chapter Four is devoted not so much to the frameworks that you can embed in your code, but to tools that can facilitate your work with the code.
      • mogenerator
      • fastlane
      • YACC / LEX

    I’ll ask you forgiveness in advance. As Bilbo Baggins said:
    I know the good half of you twice as bad as it should be, and I love the thin half half as much as I should.

    So here I am - in this article there will not be enough detailed analysis of any of the proposed tools (there is not even a single line of code here!) - only subjective feelings and recommendations for use. And there are a lot of tools and each of them deserves a separate analysis.
    BUT! there are these reviews - and for each tool I tried to indicate where you can find examples of its use, documentation and related information - good, it really is more than enough.

    So, let's begin.

    Chapter 1. Introductory


    There are probably no people here who would not hear about this instrument. A tool for managing external dependencies (third-party) for your project.
    You can read about it here . I will not particularly describe here about him, since everyone knows everything perfectly. And for those who do not know, you can read here and take into account that this tool for the iOS developer is a must have. The transition to its use significantly reduces the time for prototyping new applications, and writing your own hearths significantly disciplines the process of creating reusable code.

    • Incredibly easy project dependency management. Adding new ones is done with just one line.
    • The ability to pack your reusable code in convenient and simple containers that facilitate its reuse.
    • Separation of the code of dependencies from the code of your application - which reduces the temptation to change the code of the third-party toolkit (Sometimes this, of course, is useful and even necessary. Before the first update of the dependency module) and forces you to organize the code of these add-ons more accurately and from the side.

    • The process of adding dependencies is so simplified that now you need to carefully monitor that so many of these dependencies are not added to the project for each sneeze. 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 it is you who is responsible for their mistakes.
    • If you want something strange, then you will have to rummage with it sometimes very much. For example, at some point it took me considerable time to teach the project to correctly assemble different architectures for different targets. Perhaps it has become easier and more comfortable now, but the fact remains.


    A database framework that is a wrapper on CoreData. Read more about it here .
    MagicalRecord will help you get started using CoreData, even if you previously avoided it in every way. 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 from my personal experience - I found it much easier to understand the intricacies of CoreData just by reading and using the MR code in practical tasks.

    MR itself is an adaptation of the ActiveRecord conceptfor Objective-C. This concept shifts focus when working with the database from databases and tables to individual records, linking basic operations with the database to them. That is, instead of “Table - delete this record”, we say “record - failed”, or “record - create, change, give me all instances that correspond to your essence”.

    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.

    • It hides many CoreData rakes from you, significantly reducing the code needed to initialize, save, modify records. Simplifies work with child contexts.
    • Initially focused on asynchronous work with data, which means standard actions with it will not cause UI hangs.
    • Low entry threshold - this framework will allow you to feel the virtues of databases, but will not lead through all the depths of hell.

    • Like all asynchronous parts of the code, it's pretty hard to cover with tests.
    • In addition, you, as in other ways of working with the database, will have to guarantee at the architecture level that the database is correct - that the application will not try to write and read in the same place at one time (no, it will succeed, but Here is the result you do not really like)
    • It does not completely relieve you of writing predicates, queries and other things (You can’t say that this is directly a flaw, because it would be strange to expect such a thing)


    A framework for working with a concept such as Promise . You can read more about it here .
    In short, this is a way of organizing work with asynchronous code in the form of chains of actions. Why is this needed?
    1. Asynchronous code becomes simple and linear, regardless of which thread it is running on. Before his eyes, his strict structure - what and why will be fulfilled. On one screen, you can put the following logic: Download two config files. When they come, 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 - to use some common variables, for example - which makes asynchronous code more difficult to change.
    3. There is a comfortable opportunity for error handling of the entire execution chain. Everyone who wrote chains of asynchronous code was faced with the fact that the longer the chain, the more difficult it is to correctly process errors, the more complicated and more cumbersome the code becomes.

    • Significantly simplifies the construction of a graph of the execution of asynchronous operations. It’s easy to have one operation begin immediately after another or several others
    • Significantly simplifies the processing of errors that occur at different stages of the chain execution (especially if the chain in the application makes sense only if all operations are completely successful and does not make sense in all the others)
    • Allowing to correctly encapsulate asynchronous operations - greatly facilitates their multiple use in different places of the application.

    • Still need to be careful with memory when working with promises
    • It takes some time to get used to working not with the “code”, but with the “wrapper that will call your code”
    • Inconvenience during debug.

    Chapter 2. Architectural frameworks or something


    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 splitting of the MVC pattern into smaller entities:
    1. VO (value Object) - it is familiar from CoreData Entity
    2. Proxy - with its strange name, it nonetheless represents a Wrap over the model - they are responsible for access data and non-trivial getters (aggregators) of model
    3. Mediator - what is more common to consider as a controller in MVC, controls the display in the View and processes the signals coming from it. Can be subscribed to notifications sent through the facade
    4. View - well, View it is View. The code responsible for the visual presentation of information.
    5. Facade - singleton entity that exists all the time the application. All objects of the Proxy, Mediator, and Command
    6 classes must be registered in it when creating it . Notification - a message sent through the facade. It will be received by all the mediators subscribed to it (and only them)
    7. Command - what flow applications describe - obeying the Command pattern - are launched, executed, completed without being held in memory, which in essence are a notification with the executed code.

    • a fantastic margin of safety for architecture - in my example, everything was poured into the code for several years and the development did not get up and lived to see the possibility of deep refactoring and the appearance of adequate technical processes.
    • Rapid prototyping - architecture is already well-marked for you. And if you have experience working with this structure, you can quite quickly, but at the same time in a modular and iterative form, increase the functionality of the application.
    • A very formal structure — if you already started using it in a project — it’s rather difficult to step aside from it — which leads to the fact that all project developers write code in a uniform manner.

    • It provides a huge number of opportunities to write bad code, due to the fact that all critical modules are, in fact, singletones (in this case, registered in the singleton facade), which means that the project very quickly has every chance of building implicit dependencies
    • Hell of a debaga. There is very little explicit binding, respectively, in order to go along the execution chain you need to go along 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 done. The stack nesting depth reaches completely cosmic values ​​(I saw a hundred with my own eyes)

    Ash framework

    Read about it in detail here . Use cases can also be found there.
    Specifically, this framework is more suitable for game development, but if at some point you can say that your application has ceased to be stateless, then it will be at least informative to familiarize yourself with this thing.
    In short, this is a framework that implements the Entity-Component-System pattern and operates with the following set of entities:

    1. Entity - in fact, some entity (for simplicity of description, I will use examples from the gaming theme in this case, because they are more suitable for this framework). For example, a player, an enemy, or an obstacle. In addition, entities can be not so explicit actors (acting objects) - for example, the Options popup, lifeBar, or, for example, the entire level. It operates with the minimum of information associated with a certain set of components.
    2. Component - some atomic container with data. For example - position, speed, destructibility, ...
    3. Filter - A certain filter in order to be able to get not the entire Entity set - but only those that pass it. It is a set of component keys. Thus, if the filter contains the keys "position" and "speed" - then entities that do not move, or non-game entities - it will not pass.
    4. System - a description of the nature of entity changes. Operates with a filtered set of entities and somehow transforms it. For example, a gravity system. It takes the entities passing through the filter - they have a position, speed, acceleration, susceptibility to gravity - and recalculates their accelerations taking into account the influence of gravity.
    Or MovementSystem - takes all entities that have a position, speed and acceleration - and change the speed by the value of acceleration, and the position by the value of speed
    5. World - some object that requests updating all systems in a certain order.

    In fact, this framework offers the behavior of objects from a somewhat unusual point of view for the classical OOP - not the object reacts to changes, but the world finds objects that are able to react and change them independently, through systems.

    • It forces to break the logic into orthogonal entities, to think in terms of modules that do not interfere with each other
    • The ease of changing characters (behavior), The ease of adding new ones.
    • The state machine is built into the by design framework. Thus, you can switch between entire sets of characters and reactions.

    • This is not a silver bullet. State machines quickly gain complexity and lose control over the code. It requires a very clear understanding of what and how much should be in it.
    • The orthogonality of the modules and the complete stateless is a very beautiful concept in the head, but in practice it is very difficult to maintain. And any shared data outside the components very quickly leads to a lot of difficulties and possible bugs of the race condition type (albeit in the same thread).

    Reactive cocoa

    Probably one of the most significant third-party frameworks for iOS development from existing today. You can read about it in detail here and on the hub - and I highly recommend doing this. He proposes to use some replacement of the main iOS pattern (MVC) with its analogue MVVM and relies on reactive programming in its use .
    In short - Reactive programming is a development paradigm focused on working with data streams. If during the usual development (imperative), the developer programs in the style of "the program must do A, then do B, then do C". If A, B, and C are significant logical operations located in various application modules — with an increase in the number of such blocks — the connectedness of programs increases, their complexity and size increase.
    The most famous example of this binding of various modules for long 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 various entities to start this operation at different times of the application?
    • The code associated with one operation is scattered across a large number of modules and its modification is difficult.
    • Adding new operations to the action chain (especially in the middle of it) is usually associated with a fairly large amount of code.

    What Reactive Cocoa and MVVM offer:
    • Operations are combined into modules, in the interfaces of which wrappers are made over the executable code, the so-called signals and commands.
    • Unlike the classic MVC, the signal-based architecture allows you to associate associated entities directly, without interlayers.

    For myself, I consider ReactiveCocoa the development of PromiseKit ideas, expanding them to the level of the application architecture.

    In addition to the fact that ReactiveCocoa inherits the advantages and disadvantages of PromiseKit, I would like to add the following:
    • The application’s action sequences take up less code, it’s easier to catch a glance, which means it’s easier to understand how the application works.
    • Ease of manipulation with sequences of actions - it is easy to add an intermediate step, add or remove a side step.

    • Code debugging with Reactive Cocoa is very difficult.
    • The ease of working with signals provokes 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 intersect each other
    • 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.


    A recent gift from the Facebook development team, which you can read more about here . I don’t know about you, but more than once I looked sadly at HTML and CSS - as ways to solve interfaces. That is, on what is called a declarative UI .
    If in short - this, according to Wikipedia, is a way of describing entities that are focused not on “how to create it”, but on “what it will be like when it is created”. ComponentKit is a development of ideas, the appearance of which in iOS development can be seen in AutoLayout - a concept in which - changing one element of the interface - there is no need to do many operations so that the rest of the UI remains to look correct. If in the old UIKit changing the size of some UIView - there was a need to recalculate the positions of all other UIViews relative to it, which sometimes led to a nontrivial code for working with coordinates, now it’s enough to say that this View will be 20 pixels from the View, which is higher and by 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, 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, but through filling the table, similar to the usual work with arrays. (Instead of “at such an index-pass, we have such a cell” - “put such and such a cell in such and such a place in the table”). If you had to build tables with heterogeneous data (for example, building a description page of housing in an application for renting real estate - where on one page there can be dozens of different types of cells with different behaviors)
    • Element states provide an opportunity to describe several characters at once, which an element can take depending on the context, without the need to distribute it into different .xib, different classes and so on
    • A convenient adaptive layout that allows declaratively describe collections of elements that are equally suitable for different device resolutions and screen orientations (all this can be done with AutoLayout - but declarativeness is much more flexible)

    • Description of the interface in code, but in a concise and adaptive form.
    • Smart solutions for standard components

    • Immunity of components - if you want to make a changeable interface with a lot of animations, then this is not your choice.
    • The novelty of the framework is, of course, a relative flaw, 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 just 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 judge it - through this 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.
    Seriously speaking, TDD is perhaps the most influential concept for me as a developer. And the point is not that autotests allow you to avoid regression, allow you to write code faster, worry less about changes. The main benefit of autotests is that not every code can be tested. Thanks to testing, abbreviations such as DRY and SOLID make sense .


    You can read about it in detail on the page of its repository , as well as its parent repository . There are a sufficient number of examples of its use - enough to start using it.

    If your code:
    1. There are many implicit dependencies (for example, singletones that you pull 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 same 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 links in your code, this code is difficult to test because you need to test entire bundles of modules in all possible combinations of their states (that is, the number of test cases grows exponentially). And from the point of view of the development of the application, there may be some kind of bug in each of the combinations of states, and the risks are very high that the connected module will not be in the state that we expect from it. And all additions in the code have a very, very high price.
    3. If your code is not DRY, then you will 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 health of a repeating code is proportional to the number of repetitions.

    And many many others.
    Thus, it turns out that the test code itself is more supported, simpler in itself, easier to make changes. And in general, it saves a breakthrough in itself.
    And the best way to verify that your code - we are testing - is to cover it with tests. If I understand correctly, over time, the need for most tests disappears, because you are already in the habit of writing a testable code, but you still have to get to that time.

    So, the Specta / Expecta bundle is a framework for writing tests in the style of BDD .

    • The BDD approach itself is good in that you have both the testing code and the specification that the testing corresponds to in one source. 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 easy to read and reduce the risks of supporting the tests (it becomes not so necessary to test your tests)

    • In fact - when testing real applications, there is a rake on the rake - and you need to go through all this rake. Testing asynchronous databases, testing UIs and generally testing something asynchronous requires serious reflection and thought over.
    • A small and, I hope, temporary drawback - the integration with this tool’s Xcode has been unstable lately


    Read about it in detail here .
    Unfortunately, when testing, it is not always possible to completely eliminate external and implicit module dependencies 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 speed, unexpected errors) and allows you to work with a guaranteed clean context (the server will return that, what we need, regardless of the fact that according to the life cycle of the application, we cannot expect such an answer, NSUserDefaults for the necessary keys contain what is needed for a particular test, not what the previous tests left there or God knows what else e).

    This tool quickly learns to think in the criteria of the sandbox and allows you to not follow the SOLID principles completely into the Enterprise coding style - which, due to its verbosity, is extremely slow to develop. It allows you to choose where the line should go in the choice between "absolutely clean code" and "simple code".

    • Significantly simplifies the process of testing modules with external dependencies

    Blood magic

    The framework developed by one of the habrazhitel - 1101_debian , you can read more here .
    Perhaps I would like to mention this framework next to the topic of testing because it helps in answering the question HOW to write the tested code.
    In this article, I have repeatedly written about such a terrible thing as implicit dependencies. And the second way to get rid of them (the first is a thoughtful look at the code in order to understand which of the dependencies can be taken out of the module being developed at all. Oddly enough - damn effective way) is Dependency injection - in other words, all implicit dependencies must be made explicit.

    In short, it is good practice to make a module truly independent, testable, and, just as importantly, reusable, to get rid of implicit dependencies. In other words, the .h file should be enough to fully understand what exactly this module uses to work. Firstly, it greatly facilitates the testing of the object (we are guaranteed to know the list of modules necessary for the module to work and we can wet them all using OCMock or in any other accessible way). Secondly, this gives us another reason to think about the question: “But does this class know too much about the code being executed? Maybe break it into several? Or maybe there is some way to logically group these dependencies into separate modules? ”

    • Являясь некоторым дополнением к языку, позволяет избежать некоторого количества бойлер-кода. Например, ленивое инстанцирование зависимости делается в одно слово, а не в метод из 4-5 строк.
    • Позволяет управлять зависимостями на некотором мета-уровне — подменяя зависимость сразу в нескольких, пользующихся ею, классах.

    Глава 4. Внешние инструменты


    A simple but extremely useful tool in everyday development that facilitates the connection of an abstract database model with its representation in code. You can read more about it here . In short, it helps in organizing the code for working with the model in such a way that updating the model is the most simple and painless - so that the regeneration of the model does not affect custom logic.
    What I see for myself the fundamental benefit of this tool is that it became the first tool for me, which quite easily allows you to discover the huge world of code outside of runtime, which is, in my opinion, a very important stage in development programmer:
    Tasks should be solved with the tools intended for this. Not for all Objective-C tasks is the most convenient language and not for all tasks - Runtime is an appropriate runtime. And besides, the knowledge should be presented in the project in a single copy (DRY in the broad sense, not only with regard to the code itself)
    Why do you need to monitor the correspondence of the model and its representation with your hands, if this can be done as part of one process?
    Why fill the database in runtime - if you can generate it before the deployment?
    Why have multiple copies of documentation for separateness - if you can generate it from comments in the code?
    For integration testing of working with an external service, it is quite possible to prepare the context through scripts (create test users, fill out their profiles, ...).

    And many, many, many other tasks that are solved by external tools (I will talk about one of them in a bit more detail at the end of this article).

    The second aspect that I like mogenerator is that it showed an elegant model for matching generated code and written code - so that they do not conflict with each other. And finally, he just slightly reduced the amount of boiler code for working with the model.

    • It becomes much more comfortable to modify the .xcdatamodel model
    • The tool is well configurable, which allows you to influence the generated code, its placement in the project. At the same time, in order to start using, you need the very minimum of settings


    You can and should read more about it here .
    In short, this is a set of tools designed to facilitate the continuous delivery of your projects as much as possible.
    It allows you to:
    • Build your application
    • Change application settings for build
    • Take screenshots
    • Run tests
    • Submit application to
    • Send convenient notifications about all this

    and much much more.

    In addition, this tool, like the previous one, helps you enter the world of external tools, think about using scripting languages ​​(AppleScript, Bash, Python, Ruby) to help work on the project. For me it was a very long process, so I think every step in it is very useful and important. In addition, this tool raises such an interesting topic as DSL . The topic is very complex, so before creating my own DSLs (which I will briefly write in the last section of this article), I highly recommend finding successful examples of using them in order to feel their meaning. FastLane is one of the most telling examples of why DSL is very, very healthy.

    • Simple, convenient and functional.
    • Integrates with well-known Continous delivery servers (such as Jenkins, for example)
    • Allows you to write fairly complex scripts that deploy, test, and prepare your application. And write about it to you (for example, in Slack)

    • Not detected. Really handy toolkit

    YACC / LEX

    And finally, this amazing couple, heavy artillery in the DSL world and one of the most powerful tools for creating your own languages. You can read about it a lot where, but in particular, there are a couple of articles on the hub:

    I recommend that you familiarize yourself with them, because the tool is really complex, but has an amazing range of applicability:
    • Вы можете очень комфортно упаковывать и сериализовать сложные логические сущности — например, мне приходилось писать DSL для описания стейт-машин, которые в форму JSON/XML/… ну никак не укладывались.
    • Вы можете редуцировать полный язык программирования до крайне легковесного, с той целью, чтобы задачи можно было переложить на плечи людей, не знакомых с полноценным программированием (написание тест-кейсов для автотестов, написание конфигов уровней и логики врагов для гейм-дизайнеров, написание бизнес-правил для бизнес-процессов.)
    • Вы можете использовать совсем легковесные DSL для описания каких-то маленьких (но присутствующих в большом количестве) процессов в работе вашего приложения. (Для этого не всегда даже нужны YACC/LEX — хватает и ObjC и Swift или Ruby (Писать DSL на свифте — вообще праздник, радость и удовольствие))

    In general, the range of tasks for which this is suitable is huge.

    • A very convenient form for describing the semantics of a language.
    • Very high runtime execution speed (C language parser code is generated from the language notation, which generates a very concise AST )
    • A very wide scope - it would be a desire, in this language you can write full-fledged interpreters of full-fledged programming languages

    • Extremely high threshold of entry - to write something significant, you need to break your brain tightly and for the first time it will take a really long time.
    • If you use this thing in Runtime, then the memory management falls on you with all the burden.

    Instead of a conclusion

    Of course, I understand that on the one hand there are still so many things, probably very important and convenient, that I have not mentioned (or I don’t know, but I will be very grateful if you recommend them to me), but on the other hand, it turned out quite a lot of diverse and (oh horror!) heterogeneous material, which is not analyzed in detail. But all these projects have wonderful documentation, I will be happy to answer your questions, and if something in your opinion deserves more detailed coverage, I will gladly describe my experience with this.
    Thank you for reading to this place. Till.

    Also popular now: