14 things an iOS developer must know

With the permission of the author, I post the translation of the article by Norberto Gil Vasconcelos "14 must knows for an iOS developer" ( link to the original ). At the time of publication of the article, the Swift 3 version was relevant.

As an iOS developer (currently absolutely dependent on Swift), I created applications from scratch, supported applications, and worked in various teams. For all the time I have been working in this industry, I have often heard the phrase: "You can’t explain, then you don’t understand." So, in an attempt to understand what exactly I do every day, I create a list of what, in my opinion, is important for any iOS developer. I will try to explain every moment as clearly as possible. [Please feel free to correct me, express your opinion or suggest your additions to this list.]


Topics: [ version control | architectural patterns | Objective-C vs. Swift | React | dependency manager | information storage | CollectionViews & TableViews | UI | protocols | short circuits | schemes | tests | geolocation | localizable strings ]


Here is my list, without further ado, in random order.

1 - Version control


Congratulations, you are accepted! Extract the code from the repository and get to work. Stop what?

Version control is necessary for any project, even if you are only a developer. The most commonly used systems are Git and SVN.

SVN is based on a centralized version control system. This is the repository where working copies are created, and to access them you need a network connection. Authorization of changes is carried out in a specific way; the system monitors changes by registering each file, the full history of changes can be viewed only in the repository. Working copies contain only the latest version.

Gituses a distributed version control system. You will have a local repository where you can work, a network connection is necessary only for synchronization. When a working copy is changed, the state of the entire directory is saved, but only the changes made are recorded; Both the repository and working copies have a complete change history.

2 - Architectural patterns


Your fingers tremble with excitement, you figured out version control! Or is it because of coffee? Never mind! You are on the beat and the time has come for programming! Nope. What else to wait for?
Before you sit at the keyboard, you must choose the architectural pattern that you will adhere to. If you have not started the project, you must match the existing pattern.
There is a wide range of patterns used in the development of mobile applications (MVC, MVP, MVVM, VIPER, etc.). I will give a brief overview of the most frequently used in the development for iOS:

  • MVC — Model, View, Controller. Controller Model View, . View Controller , Controller . ? , View, (ViewController) . , MVC. MVC . ( !), , , Model, , . MVC , , iOS .


    MVC –
  • MVVMModel, View, ViewModel. ( ) View ViewModel, ViewModel , ViewModel, View - . ViewModel View, , .


    MVVM –


For a deeper understanding and information about other patterns, I recommend reading the following article .

This may not seem like that much, but well-structured and organized code can prevent a lot of headaches. The big mistake that every developer makes at some point is simply to get the desired result and refuse to organize the code, mistakenly believing that it saves time. If you do not agree, listen to old Benji:
Every minute you spend organizing your business saves you an hour

- Benjamin Franklin

Our goal is to get intuitive and easy to read code that will be easy to use and maintain.

3 - Objective-C vs Swift


When deciding in which programming language to write your application, you should know what capabilities each of them has. If possible, I prefer to use Swift. Why? To be honest, Objective-C has very few advantages over Swift. Most of the examples and tutorials are written in Objective-C, and Swift makes adjustments to the paradigms with each update, which can be disheartening. However, these problems will eventually disappear.

Compared to Objective-C, Swift makes a leap in many ways. It is easy to read, it looks like natural English, and since it is not built on C, this allows you to abandon traditional conventions. For those who know Objective-C, this means no more semicolons, and method calls and expression conditions will not need to be enclosed in brackets. It is also easier to maintain your code: Swift only needs a .swift file instead of .h and .m files because Xcode and the LLVM compiler can detect dependencies and perform incremental builds automatically. In general, you will have to worry less about creating standardized code, and you will find that you can achieve the same results with fewer lines.

Still in doubt? Swift is safer, faster, and takes care of memory management (for the most part!). Do you know what happens in Objective-C when you call a method with an uninitialized pointer variable? Nothing. The expression becomes inactive and is skipped. It sounds great, because it does not lead to the crash of the application, however, it causes a number of bugs and unstable behavior, because of which you will want to think about changing your profession. Seriously. The idea of ​​becoming a professional dog walker sparkled with new colors. At the same time, the Swift counter works with optional values. You will not only get a better idea of ​​what nil can be and set conditions to prevent the use of nil values, but also get runtime crash if nil optional is still used, which will simplify debugging.ARC (automatic reference counting) helps you better manage your memory in Swift. In Objective-C, ARC does not work with procedural C or with an API like Core Graphics.

4 - React or not React? (that is the question)


Functional reactive programming (FRP) is a new hit. It is intended to simplify the compilation of asynchronous operations and event / data streams. For Swift, this is a general abstraction of computation expressed through the Observable interface.

The easiest way to illustrate this is with a little code. Let's say baby Timmy and his sister Jenny want to buy a new game console. Timmy receives 5 euros from her parents every week, the same goes for Jenny. However, Jenny earns another 5 euros by delivering newspapers on weekends. If both save every cent, we can check every week if the console is available! Each time the value of their savings changes, their aggregate value is calculated. If this is enough, the message is stored in the isConsoleAttainable variable. At any time, we can check the message by subscribing to it.

// 
let timmySavings = Variable(5)
let jennySavings = Variable(10)

var isConsoleAttainable =
Observable
.combineLatest(timmy.asObservable(), jenny.asObservable()) { $0 + $1 }
.filter { $0 >= 300 }
.map { "\($0)    !" }

//  
timmySavings.value = 10
jennySavings.value = 20
isConsoleAttainable
   .subscribe(onNext: { print($0) }) //   

//  
timmySavings.value = 100
jennySavings.value = 200
isConsoleAttainable
   .subscribe(onNext: { print($0) }) // 300    !

This is just an example of what can be done with FRP, once you master it, it will open up a whole new world of possibilities, right up to adopting an architecture other than MVC ... Yes, yes! MVVM!
You can look at two main applicants for the title of chief Swift FRP:


5 - Dependency manager


CocoaPods and Carthage are the most common dependency managers for Cocoa Swift and Objective-C projects. They simplify the process of implementing the library and keeping it up to date.

CocoaPods has many libraries built using Ruby and can be installed using the following command:

$ sudo gem install cocoapods

After installation, you will want to create a Podfile for your project. You can run the following command:

$ pod install

or create a custom Podfile with this structure:

platform :ios, '8.0'
use_frameworks!

target 'MyApp' do
pod 'AFNetworking', '~> 2.6'
pod 'ORStackView', '~> 3.0'
pod 'SwiftyJSON', '~> 2.3'
end

Once created, it's time to install your new modules:

$ pod install

Now you can open the .xcworkspace of your project, do not forget to import your dependencies.

Carthage is a decentralized dependency manager, unlike CocoaPods. The disadvantage of this is that it is becoming increasingly difficult for users to find existing libraries. On the other hand, this approach requires less support work and avoids disruptions due to centralized storage.

For more information on installation and use, check out the GitHub project .

6 - Data storage


Let's start with the easiest way to save data for your applications. NSUserDefaults , so named because it is usually used to save the default user data displayed when the application first loads. For this reason, it is made simple and easy to use, but this also implies some limitations. One of them is the type of objects that this method accepts. Its behavior is very similar to Property List (Plist) , which has the same restriction. Here are six types of objects that they can store:

  • NSData
  • NSDate
  • NSNumber
  • NSDictionary
  • Nsstring
  • NSArray

For compatibility with Swift, NSNumber can accept the following types:

  • UInt
  • Int
  • Float
  • Double
  • Bool

Objects can be stored in NSUserDefaults as follows (First create a constant that will store the key for the stored object):

let keyConstant = "objectKey"

let defaults = NSUserDefaults.standardsUserDefaults()
defaults.setObject("Object to save", objectKey: keyConstant)

To read an object from NSUserDefaults, we can do the following:

if let name = defaults.stringForKey(keyConstant) {
   print(name)
}

There are several convenient methods for reading and writing to NSUserDefaults that receive specific objects instead of AnyObject.

Keychain is a password management system and may contain passwords, certificates, private keys or personal notes. Keychain has two levels of device encryption. The first level uses the lock screen lock code as the encryption key. The second level uses a key generated and stored on the device.

What does it mean? This is not entirely super secure, especially if you are not using a password on the lock screen. There are also ways to access the key used in the second level, since it is stored on the device.

The best solution is to use your own encryption. (Do not store the key on the device)

CoreData- This is a framework developed by Apple so that your application interacts with the database in an object-oriented manner. This simplifies the process by reducing the amount of code and eliminating the need to test this section.

You should use CoreData if your application needs persistent data, this greatly simplifies the process of saving them and allows you not to create / test your own way of communicating with the database.

7 - CollectionViews & TableViews


Almost every application has one or more CollectionViews and / or TableViews. Knowing how they work and when to use one or another will prevent complex changes in your application in the future.

TableViews display a list of items in a single column vertically and are limited only by vertical scrolling. Each item is represented by a UITableViewCell, which can be fully customized. They can be sorted by sections and rows.

CollectionViews also display a list of items, but they can have multiple columns and rows (for example, a grid). They can be scrolled horizontally and / or vertically, and each element is represented by a UICollectionViewCell. Like UITableViewCells, they can be customized as desired and sorted by section and row.

They both have similar functionality and use reusable cells to improve mobility. The choice of what you need depends on the complexity that you want to have on the list. CollectionView can be used to represent any list and, in my opinion, is always the best choice. Imagine you want to submit a contact list. The list is simple, you can implement it with a single column, so you select a UITableView. Everything is working! After a few months, your designer will decide that the contacts should be displayed in a grid format, not a list. The only way to do this is to change the implementation of the UITableView to the implementation of the UICollectionView. I'm trying to say that although your list may be simple and a UITableView may be enough if there is a high chance of a design change, probablyit is best to implement this list using a UICollectionView.

Whatever choice you make, it is a good idea to create a generic TableView / CollectionView. This facilitates implementation and allows the reuse of large amounts of code.

8 - Storyboards VS Xibs VS Programmable UI


Each of these methods can be used separately to create a user interface, but nothing prevents you from combining them.

Storyboards provide a broader view of the project that designers will like, allowing you to see the flow of the application as well as its windows. The disadvantage is that with the addition of more windows, the connections become more confusing and the loading time of the Storyboard increases. Merge issues are much more common because the entire user interface is in a single file. They also become much more difficult to solve.

Xibsprovide visual viewing of windows or portions of a window. The advantages are ease of reuse, fewer merge conflicts than Storyboards, and ease of viewing the contents of each window.

Programming user interface gives you more control over it, reduces the frequency of merge conflicts, if they arise, they are easier to remove. The disadvantage is less visualization and additional time required for writing.

The above approaches to creating a user interface vary greatly. But, in my subjective opinion, the best option is a combination of all three. Several Storyboards (now we can switch between Storyboards!), With Xibs for any visual object that is not the main window, and, finally, a little programming for additional control, so necessary in certain situations.

9 - Protocols!


In everyday life, protocols exist so that in a certain situation we know how to respond. Suppose you are a firefighter, and an emergency has occurred. Each firefighter must follow a protocol that sets out the requirements for a successful response. The same applies to protocols in Swift / Objective-C.

A protocol defines a sketch of methods, properties, and other requirements for specified functions. It can be adopted by a class, structure, or enumeration, which will then have the actual implementation of these requirements.

Here is an example of creating and using a protocol:

For my example, I will need an enumeration that lists the different types of materials used to extinguish a fire.

enum ExtinguisherType: String {

   case water, foam, sand

}

Next I will create an emergency response protocol.

protocol RespondEmergencyProtocol {

   func putOutFire(with material: ExtinguisherType)

}

Now I will create a fireman class that obeys the protocol.

class Fireman: RespondEmergencyProtocol {

    func putOutFire(with material: ExtinguisherType) {

       print("Fire was put out using \(material.rawValue).")

    }

}

Fine! And now we use our fireman.

var fireman: Fireman = Fireman()

fireman.putOutFire(with: .foam)

The result should be the following: “Fire was put out using foam.”

Protocols are also used in Delegation. This allows classes or structures to delegate certain functions to an instance of another type. A protocol is created with delegated responsibilities to ensure that their functionality is of the appropriate type.
A small example!

protocol FireStationDelegate: AnyObject {

func handleEmergency()

}

The fire department delegates to the firefighter emergency response measures.

class FireStation {
   weak var delegate: FireStationDelegate?

   func emergencyCallReceived() {
      delegate?.handleEmergency()
   }
}

This means that the firefighter will also have to comply with the FireStationDelegate protocol.

class Fireman: RespondEmergencyProtocol, FireStationDelegate {

   func putOutFire(with material: ExtinguisherType) {
      print("Fire was put out using \(material.rawValue).")
   }

   func handleEmergency() {
      putOutFire(with: .water)
   }

}

All that needs to be done is for the firefighter on calls to be appointed as a delegate to the fire station, and he will handle the received emergency calls.

let firestation: FireStation = FireStation()
firestation.delegate = fireman
firestation.emergencyCallReceived()

As a result, we get: “Fire was put out using water.”

10 - Short circuits


It will only be about Swift closures. They are mainly used to return a trailing block or with high order functions. Final blocks are used, as the name implies, to execute a block of code after the task is completed.
Closures in Swift are similar to blocks in C and Objective-C.

Closures are first class objects *, so they can be nested and passed (like blocks in Objective-C).

In Swift, functions are a special case of closures.

Source - fuckingswiftblocksyntax.com **

This resource is a great place to learn closure syntax.

* First-class objects - objects that can be used without restrictions: assign to a variable, pass / return from a function, create / destroy during program execution, etc. More details . (hereinafter - approx. translator)
** The site does not work, but there are still pictures in waybackmachine, an example .


eleven - Scheme


In short, circuits are any easy way to switch between configurations. Let's start with the basic information. The workspace contains various related projects. A project can have various targets — the targets determine the product to be assembled and the method of assembly. Also, a project may have various configurations. The schema in Xcode defines the collection of targets for the assembly, the configuration used during the assembly, and the collection of tests to execute.

Purple shows one possible pattern.

12 - Tests


If you take the time to test your application, you're on the right track. This, of course, is not a panacea, you cannot fix every bug, nor can you guarantee that your application will be free from any problems; and yet I think the pros outweigh the cons.

Let's start with the downsides of unit testing:

  • Increased development time;
  • Increase the amount of code.

Pros :

  • The need to create modular code (to simplify testing);
  • Obviously detecting most bugs before release;
  • Simplification of support.

In combination with the Tools utility , you will have everything to make the application flexible and work without errors and crashes.

There are many tools that you can use to test your application. Depending on what you want to track, you can select one or more of them. Perhaps the most commonly used tools are Leaks , Time Profiler, and Allocations .

13 - Geolocation


In many applications, some functions require locating a user. So it would be nice to have a general idea of ​​how the location works for iOS.

There is a framework called Core Location, which allows you to access everything you need:

The Core Location framework allows you to determine the current location or direction of movement of a device. The framework uses available hardware to determine the position and direction of the user. You can use the classes and protocols of this framework to configure and schedule events related to location and direction. You can also use Core Location to track movements across geographic regions. In iOS, you can also determine the distance to the Bluetooth beacon *.

* As I understand it, it's about iBeacon technology

Cool, isn't it? Check out the Apple documentation and the example there to better understand the capabilities of the framework.

14 - Localizable strings


What should be implemented in any application. This allows you to change the language depending on the region in which the device is located. Even if your application is in only one language, it may be necessary to add new ones in the future. If all text is entered using localizable strings, all you have to do is add the translated version of the Localizable.strings file for the new language.

The resource can be added to the language through the file inspector. To get a string with NSLocalizedString, you need to write the following:

NSLocalizedString(key:, comment:)

Unfortunately, to add a new line to the Localizable file, this must be done manually. Here is an example structure:

{
"APP_NAME" = "MyApp"
"LOGIN_LBL" = "Login"
...
}

Now another language (Portuguese), the localized file:

{
"APP_NAME" = "MinhaApp"
"LOGIN_LBL" = "Entrar"
...
}

There are even ways to implement the plural.

Always share what you have learned.

- Master Yoda

Hope this article has been helpful!

All Articles