swiftlang – swifting.io http://swifting.io Sharing passion in Swift Sat, 06 May 2017 15:43:03 +0000 en hourly 1 https://wordpress.org/?v=5.2.3 http://swifting.io/wp-content/uploads/2016/10/swift_4_ptak_40px-1.png swiftlang – swifting.io http://swifting.io 32 32 #41 Architecture Wars – MVC strikes back & takes a photo with AVFoundation http://swifting.io/blog/2017/05/06/41-architecture-wars-mvc-strikes-back-takes-a-photo-with-avfoundation/ http://swifting.io/blog/2017/05/06/41-architecture-wars-mvc-strikes-back-takes-a-photo-with-avfoundation/#comments Sat, 06 May 2017 15:43:03 +0000 http://swifting.io/?p=926 Not so long ago, in a galaxy not so far away… we promised you a series about architectural patterns. This post is a follow up for our initial issue on the topic. And finally, we can share with you the My Cards application written in MVC …



The main idea behind this app is to refactor its code to fit MVVM, VIPER and VIP architectures. But first, let’s dive into app functionalities.


My Cards

The app is intended for storing photos of loyalty cards. No one wants carrying tons of plastic in their wallets, so taking a photo of a card and showing it to a shop assistant might be useful for potential users. Of course, there are such solutions on the market, but we wanted to build something more fancy than a simple to-do list (as probably majority of examples show).


User begins with an empty list. In the meantime application tries to download cards from a backend (mocked in our example). If it succeeds a card will be displayed on the list.


By tapping on a card, its details are shown – a card name, a front and a back photos.


Those can be edited by taping on an Edit button. Person can change photos, name or delete the card entirely.


From the list of cards one can add a new one. New name and photos need to be selected.


Application allows to take a picture of a card or to select a pic from a library with built-in image picker.


Photo camera shows card outline, so it is easier to take pics. Photos are taken by tapping on a camera button.


If one wants to select a pic from the library, the pic needs to be adjusted to fit card outline.


After filling in all details card can be saved. It is stored locally, but the app tries to send a card to the backend.


And actually that’s it! No more functionalities needed at the moment 😉

MVC

Model – View – Controller architecture is the most common architecture in Cocoa Touch world. The basic concept of it is that an object called Controller responds to events triggered by a View. It can update a Model upon an action, and update the View when the Model changes. A View Controller in Cocoa Touch, deals with animations, data persistence and network calls. Some of the logic can be extracted into separate objects, such as Core Data (Persistence) and Network services, so that an app architecture can look like this:


Let’s have a look on all components in the MyCards app!

View Controllers

Thanks to the following command that I had used in Terminal I was able to extract the number of lines in each file and the total is 2592.

find . "(" -name "*.swift" ")" -print0 | xargs -0 wc -l

codebeat badge Not so bad, after all we used our good friend codebeat and received an A grade. The project consists of a few view controllers:

  • CardsViewController – downloads data from the Internet, saves it to a persistent store and displays a list of cards from the persistent store
  • CardDetailsViewController – displays a card details and saves changes to the persistent store and to the Internet. It operates in normal (just display card), edit (modify data), or create (add a new card) modes.
  • CardPhotoViewController – displays card photo in fullscreen
  • PhotoCaptureViewController – takes a photo
  • CropViewController – crops a photo to fit a card outline
  • ImagePickerController – a UIImagePickerController subclass used to distinguish side of a card of which photo is being taken
  • HiddenStatusBarViewController – a superclass for view controllers that require status bar to be hidden (i.e. all shown fullscreen for taking a photo, cropping an image or displaying a picture)
Models

A loyalty card in the application is represented by a Card struct.

struct Card {
    let identifier: String
    let name: String
    let front: UIImage?
    let back: UIImage?
}

extension Card {
    var isValid: Bool {
        guard
            let _ = front,
            let _ = back,
            !name.isEmpty,
            !identifier.isEmpty
            else { return false }
        return true
    }
}

The Card contains an identifier, which should be unique. It is useful for backend or persistent store saves. Card also contains a name and optional front and back images. The struct contains an isValid property that validates whether a card can be saved or not. E.g. it used by the CardDetailsViewController to check if data provided by a user is correct and if the controller should proceed with saving it.

A CardMO class is an NSManagedObject subclass that mirrors the Card in the Core Data. The difference between both is that Managed Object of the card stores front and back photos as binary data (NSData).

In the app the user can take or select a photo for the front or the back of a loyalty card. To distinguish for which side user chooses a photo a Card.Side enum is used.

enum Side {
    case front
    case back
}

When displaying details of a loyalty card with CardDetailsViewController one can also edit card information. The view controller knows whether it is in .edit or .normal mode because of a mode property of type CardDetailsViewController.Mode. The view controller is also used for creating a new loyalty card, hence .create mode exists.

enum Mode {
    case normal
    case edit
    case create
}

Views

Among standard UIKit components application UI uses some custom views:

  • TappableView – can be considered an animatable button, with custom subviews added to its content view. A button tap is forwarded to a delegate to act upon.

  • CloseButton – is a subclass of the TappableView. Its content view displays an X sign.



  • PhotoCamera – is a view that shows a photo camera icon. Its view is drawn with UIBezierPaths



  • PreviewView – consists of aforementioned CloseButton, PhotoCamera and a card outline. It contains also an AVCaptureVideoPreviewLayer to show input from phone’s camera.



If we’re talking about views it’s worth mentioning a few patterns used in the app. First of all, we’re using a Builder protocol to set app all properties on views used in a view controller to display its data. It looks like this:

fileprivate lazy var collectionView: UICollectionView = UICollectionView(frame:
        .zero, collectionViewLayout: self.layout).with {
        $0.dataSource = self
        $0.delegate = self
        $0.backgroundColor = .clear
        $0.register(CardCell.self)
        $0.alpha = 0.0
    }

What it allows us to do is calling with(configure:) method in which we get the instance we can configure. We wanted to extract view configuration from viewDidLoad method to avoid having a massive method and a large ABC metrics in Codebeat. Properties using this pattern are marked as lazy properties. Thanks to that compiler doesn’t complain if we initialize UICollectionView with self.layout property 😉. Btw., if the way we register a cell for reuse seems strange to you, check out our issue #40.

We ❤️ auto layout. Due to that fact, we have a special helper that initializes a view with a .zero frame and sets translatesAutoresizingMaskIntoConstraints to false. You just need to call constrained() method on a UIView subclass in order to tap into it. Unfortunately it won’t work with classes such as UICollectionView or UITableView due to their designated initializers, but you can use the Builder protocol to do it by yourself.

Constraints for a view or for view controller’s views are created in configureConstraints() method. We use anchors API to create NSLayoutConstraint relations between views and we activate them in bulk using a class method.

func configureConstraints() {
    var constraints: [NSLayoutConstraint] = []
    //...
    constraints.append(closeButton.heightAnchor.constraint(equalToConstant: 40))
    constraints.append(closeButton.widthAnchor.constraint(equalToConstant: 40))

    //...
    NSLayoutConstraint.activate(constraints)
}

Services

Additionally, a view controller can be supported by some services:

  • CoreDataWorker
  • NetworkLoader
CoreDataWorker

CoreDataWorker is a pattern presented in issue #28 Better CoreData with Swift Generics, which is used for fetching data from and saving data to a persistent store.

NetworkLoader

NetworkLoader – is a class conforming to a set of protocols that enable downloading and uploading data from and to a backend. It is underlaid by an NSURLSession instance.

A NetworkLoader is initialized with an URL to a backend. A shared instance is created in a separate file in an extension, in order to be easily replaced with a new URL.

extension NetworkLoader {
    static let shared: NetworkLoader = NetworkLoader(URL(string: "http://localhost:8000")!)
}

A SimpleHTTPServer in Python is used as a backend for the application. After downloading our project just run this command from ./MyCards/MyCards/ directory:

python -m SimpleHTTPServer 8000

The directory contains the cards.json file which consists of a card data. The data can be converted into model layer by using a Parser.

The NetworkLoader class conforms to a set of protocols that describe interfaces for downloading and uploading data. The ParsedDataDownloading protocol and its download(from: parser:callback:) method uses a Parser to transform a downloaded JSON file into a struct that corresponds to data from the file. The JSONConvertibleDataUploading protocol also uses a JSONDataConverting parser that is able to translate a model type into a valid JSON to be sent to a backend. Both methods expect a service endpoint to be passed as an argument. The endpoint is used to create a URL along with a URL passed to initialize the NetworkLoader.

protocol ParsedDataDownloading {
    func download(from endpoint: String, parser: Parser, callback: @escaping (Any?) -> Void)
}

protocol JSONConvertibleDataUploading {
    func upload(object: Any, to endpoint: String, parser: JSONDataConverting, callback: @escaping (Any?) -> Void)
}

In our project the CardParser class conforms to the Parser protocol and is able to convert the cards.json content into an array of Card instances. It also conforms to the JSONDataConverting protocol to convert an array of Cards back to a JSON.

Taking a photo with AVFoundation

The most unusual part of this application is taking a photo. On iOS you are allowed to use a built in UIImagePickerController to select a picture from the Photo Library, the Saved Photos Album or to capture a photo. Using the UIImagePickerController didn’t let us build a seamless user experience so we decided to built our own PhotoCaptureViewController that uses AVFoundation components. Let’s have a look how to display an input from camera in a view and how to take a picture.

First of all, when view loads, we have to check whether our application is allowed to take pictures/videos. We can do that by calling a method on the AVCaptureDevice class:

AVCaptureDevice.authorizationStatus(forMediaType: AVMediaTypeVideo)

If the method returns .notDetermined value we can request an access with another class method:

AVCaptureDevice.requestAccess(forMediaType: AVMediaTypeVideo) { 
    granted in 
    // completion
    guard granted else { return }
}

The main object that allows communication with a photo camera is of AVCaptureSession type. In order not to have any delays on the main thread we can use a DispatchQueue to perform tasks related to a session.

fileprivate let session = AVCaptureSession()
fileprivate let queue = DispatchQueue(label: "AV Session Queue", attributes: [], target: nil)

When all views are loaded we can configure a session using the queue. First of all the configuration process consists of checking the authorization status and getting an object representing a back camera (extension on AVCaptureDevice used in the code below). When both conditions are satisfied, we can beginConfiguration() of the session 😉. We can set a quality for the picture by using the sessionPreset property of the session. The AVCaptureSessionPresetPhoto means that we use capture settings suitable for high resolution photo quality output. Then we can try to create an AVCaptureDeviceInput with the back camera and then to add it as an input to the session. We can also add an AVCapturePhotoOutput to the session. Our output is a property on the PhotoCaptureViewController. The output captures in high resolution and does not enable Live Photo.

fileprivate func configureSession() {
    queue.async {
        guard .authorized == authorizationStatus else { return }
        guard let camera: AVCaptureDevice = AVCaptureDevice.backVideoCamera else { return }
        defer { session.commitConfiguration() }
        
        session.beginConfiguration()
    session.sessionPreset = AVCaptureSessionPresetPhoto
        
        do {
            let input = try AVCaptureDeviceInput(device: camera)
            guard session.canAddInput(input) else { return }
            session.addInput(input)
        } catch { return }
        
        guard session.canAddOutput(output) else { return }
        session.addOutput(output)
    }
}

We want to start our session when the view controller’s view appears and stop it when the view disappears. This can be done by calling the startRunning() and stopRunning() methods on the session.

fileprivate func startSession() {
    queue.async {
        guard self.authorizationStatus == .authorized else { return }
        guard !self.session.isRunning else { return }
    self.session.startRunning()
    }
}

fileprivate func stopSession() {
    queue.async {
        guard self.authorizationStatus == .authorized else { return }
        guard self.session.isRunning else { return }
        self.session.stopRunning()
    }
}

Once we know how to setup and start/stop a session, let’s have a look on how to take a photo 📸. In our view controller the takePhoto() method is used for that purpose. Once again job is executed on the queue created for session-related tasks. Taking a photo is done by calling the capturePhoto(with:, delegate:) method on the output. The first argument of the method is an object representing settings for the photo. In our case those are set to auto-flash mode and high-res picture.

fileprivate func takePhoto() {
    queue.async { [unowned self] in
        let photoSettings = AVCapturePhotoSettings()
        photoSettings.flashMode = .auto
        photoSettings.isHighResolutionPhotoEnabled = true
        self.output.capturePhoto(with: photoSettings, delegate: self)
    }
}

When a photo is taken a AVCapturePhotoCaptureDelegate‘s method gets called. It has a long name you can lookup below, I won’t dare repeating it in the text 😉. From a photoSampleBuffer that is provided as an argument we try to create a JPEG photo. Things wouldn’t be interesting if we didn’t have to play with the photo. Our interface displays a card outline that a user can use to center a card in this rounded rectangle. We have to process and crop the photo based on that. This is what the process(_:) method actually does. If we are successful with processing the photo, the method returns a UIImage we can further use in our app.

func capture(_ captureOutput: AVCapturePhotoOutput,
                         didFinishProcessingPhotoSampleBuffer photoSampleBuffer: CMSampleBuffer?,
                         previewPhotoSampleBuffer: CMSampleBuffer?,
                         resolvedSettings: AVCaptureResolvedPhotoSettings,
                         bracketSettings: AVCaptureBracketedStillImageSettings?,
                         error: NSError?) {
                         
    guard let sample = photoSampleBuffer,
              let data = AVCapturePhotoOutput.jpegPhotoDataRepresentation(
                  forJPEGSampleBuffer: sample, 
                  previewPhotoSampleBuffer: previewPhotoSampleBuffer),
        let photo = process(data)
    else { print("Error capturing photo: \(error)"); return }
    //Do something with the photo
}

And actually that’s all it takes to take a basic photo with AVFoundation!

Summary

We hope you are enjoying our app. We strongly encourage you to dive into the whole project and point out improvements for our implementation. As mentioned at the beginning, we wanted to build a non-trivial application that will be the foundation for porting the source code from a standard MVC architecture to MVVM, VIPER and VIP architectures. In the last section you can find links to articles describing some patterns we used in the project. Stay tuned, architecture wars are not over!

May the force be with you!

Links

]]>
http://swifting.io/blog/2017/05/06/41-architecture-wars-mvc-strikes-back-takes-a-photo-with-avfoundation/feed/ 1
# 40 How to swiftly dequeue a cell? http://swifting.io/blog/2017/04/19/40-how-to-swiftly-dequeue-a-cell/ http://swifting.io/blog/2017/04/19/40-how-to-swiftly-dequeue-a-cell/#comments Wed, 19 Apr 2017 09:04:07 +0000 http://swifting.io/?p=920 The UITableView and UICollectionView classes serve table-like and matrix-like layout for content to be displayed on screen. Both components display their data in corresponding UITableViewCell and UICollectionViewCell subclasses. Both components contain mechanisms for reusing a cell, that was initialised earlier and is not visible on the screen, e.g. when user scrolled the content. You can find a nice description and visualisations on that subject here.

Reusing a UITableViewCell – an old way

In order to reuse a cell in a tableView, the cell has to be registered for reuse with an identifier. Usually it is done at the time you configure your views, e.g. in viewDidLoad.

lazy var tableView: UITableView = UITableView(frame: .zero, style: .plain)

override func viewDidLoad() {
    super.viewDidLoad()
    tableView.register(MyCell.self, forCellReuseIdentifier: "MyCell")
}

To reuse a cell it has to be dequeued from a tableView. Dequeueing is done by dequeueReusableCell(withIdentifier:for:) method. It dequeues a cell or initialises a new one if none is queued for reuse.

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    guard let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath)
    as? MyCell else { return UITableViewCell() }
    //Configure cell
}

Have you noticed the "MyCell" duplicated in viewDidLoad and tableView(cellForRowAt:)? It felt ugly to me so I have decided to refactor that code, when I repeated it in a few view controllers.

I refactored the code so that a cell I registered for reuse from a stored cellClass and a computed property cellIdentifier.

let cellClass: AnyClass = MyCell.self
var cellIdentifier: String { return String(describing: cellClass) }

override func viewDidLoad() {
    super.viewDidLoad()
    tableView.register(self.cellClass, forCellReuseIdentifier: self.cellIdentifier)
}

Actually, this approach is kinda nice if we have a few cells to be registered for reuse. We can do some functional magic 🔮, like this:

let cellClasses: [AnyClass] = [MyCell.self, MyOtherCell.self, UITableViewCell.self]
var cellIdentifiers: [String] { return cellClasses.map{ String(describing: $0) } }

override func viewDidLoad() {
    super.viewDidLoad()
    let reuse = zip(cellClasses, cellIdentifiers)
    for (cell, cellIdentifier) in reuse {
        tableView.register(cell, forCellReuseIdentifier: cellIdentifier)
    }
}

The cellIdentifier property is of course used in tableView(cellForRowAt:). What I still don’t like in this code is that I still have to cast the reused cell to an appropriate type, even though I stored a cellClass 😭.

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath)
    as? MyCell else { return UITableViewCell() }
    //Configure cell
}

Finally, my colleague Alek has invented a great solution to my problem!

Extending UITableViewCell

First of all, it is a tedious work to write cellIdentifier property in every view controller. Why not to extend the UITableViewCell?

public extension UITableViewCell {
    public static var identifier: String { return String(describing: self) }
}

Then we can further extend a UITableView with a new version of register method to make use of identifier property on a UITableViewCell.

public extension UITableView {
    public func register(_ cell: UITableViewCell.Type) {
        register(cell, forCellReuseIdentifier: cell.identifier)
    }

But the most beautiful magic 🔮 happens when we extend UITableView with new version of dequeueReusableCell(...) method! It uses generics, takes as an argument a class of a cell, an indexPath and a configure closure to which a cell of desired type (i.e. of CellClass.Type) is passed. No more guard and casting in tableView(cellForRowAt:)! Yuppii 😊!

public func dequeueReusableCell(of
        class: CellClass.Type,
        for indexPath: IndexPath,
        configure: ((CellClass) -> Void) = { _ in }) -> UITableViewCell {
            let cell = dequeueReusableCell(withIdentifier: CellClass.identifier, for: indexPath)

            if let typedCell = cell as? CellClass {
                configure(typedCell)
            }
            
        return cell
    }
}

So now we can easily dequeue a cell of an appropriate type! How neat is that 😀 !?

let cellClass = MyCell.self
//... register a cell for reuse
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    return tableView.dequeueReusableCell(of: cellClass, for: IndexPath(row: 0, section: 0)) { cell in
        cell.textLabel?.text = indexPath.description
    }
}

Wrap up

The presented approach simplifies cell registration for reuse and allows to avoid guard-cell-casting. The same approach can be used for UICollectionView.

What you cannot do with the presented solution is taking cellClass from an array, because a compiler doesn’t know type at compile time.


class TableViewDataSource: NSObject, UITableViewDataSource {
    let cellClasses: [AnyClass] = [MyCell.self, UITableViewCell.self]
    func numberOfSections(in tableView: UITableView) -> Int {
        return 3
    }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 5
    }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        return tableView.dequeueReusableCell(of: cellClasses[indexPath.section], for: indexPath) { cell in
        //ERROR: Cannot convert value of type '()?' to closure result type 'Void' (aka '()')
            cell.textLabel?.text = indexPath.description
        }
    }
}

What you can do with that is of course using a switch statement, e.g. over section, to infer a cell type.

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    switch indexPath.section {
        case 0:
            return tableView.dequeueReusableCell(of: MyCell.self, for: indexPath) { cell in
                cell.textLabel?.text = indexPath.description
      }
        default:
            return tableView.dequeueReusableCell(of: UITableViewCell.self, for: indexPath) { cell in
                cell.textLabel?.text = indexPath.description
            }
    }
}

You can play with the solution by downloading our playgrounds or by grabbing gists from links section.

Links

]]>
http://swifting.io/blog/2017/04/19/40-how-to-swiftly-dequeue-a-cell/feed/ 2
#14 Code Review – Tips & Swifts http://swifting.io/blog/2016/04/24/14-code-review-tips-swifts/ http://swifting.io/blog/2016/04/24/14-code-review-tips-swifts/#comments Sun, 24 Apr 2016 21:20:22 +0000 http://swifting.io/?p=368 Last week, in issue #13, we introduced a definition of Code Review. Today we’ll get deeper into the topic, present some tips & swifts & tricks when performing a review and focus on some mistakes found when reviewing a 🐦Swift code.

General Tips 🔍

It is said that a review goes best when conducted on less than 400 lines of code at a time. You should do a proper and slow review, however, don’t spend on it more than 90 minutes 🕑 – you definitely will get tired after that time. It’s tiring to write and understand your own code and it’s even more tiring to understand someone’s. Other tips from the Internet are:

After some years of experience in software development you know what common programming mistakes are. You also do realise what solutions to common problems are most efficient. It’s a good practice to write it down and to check code being reviewed against a checklist ✅ – it leads improved results. Checklists are especially important for reviewers because, if the author forgets a task, the reviewer is likely to miss it too.

In the git flow mentioned in issue #13, code can get merged after receiving agreed number of approvals for a pull request. The number should depend on your team size, but it’s good to get at least 1️⃣ !😀

When you recommend fixes to the code, suggest their importance 💬. Maybe some of them could be postponed and extracted as separate tasks. Before approving a pull request, verify that defects are fixed 🔧🔨.

Foster in your company a Good Code Review Culture in which finding defects is viewed positively 😀. The point of software code review is to eliminate as many defects as possible, regardless of who "caused" the error. Few things feel better than getting praise from a peer. Reward developers for growth and effort. Offer as many positive comments as possible. I always try to put a line saying that a solution is good and clever (if it really is 😉).

You can also benefit from The Ego Effect 💎. The Ego Effect drives developers to write better code because they know that others will be looking at their code and their metrics. No one wants to be known as the guy who makes all those junior-level mistakes. The Ego Effect drives developers to review their own work carefully before passing it on to others.

And don’t bother with code formatting style …

… there are much better things on which to spend your time, than arguing ☔️ over the placement of white spaces.

Looking for bugs and performance issues is the primary goal and is where the majority of your time should be spent.

So, what to review?

Much better things to look for should be a part of your checklist. When I scrutinise a piece of code I look especially:

  • at if conditions, for and while loops for "off-by-one" errors
  • for interchanged < versus <= and > versus >=
  • for accidental interchange of && with || or bitwise operators like & and | UPDATED 11.06.2016 Swift compiler ensures that expression evaluated in a condition returns a Bool value, hence there’s no possibility not to catch compilation error if one interchanges operators
  • at operators importance and execution UPDATED 11.06.2016 If you happen a "million" of operators in one expression you’re probably doing something wrong by overcomplicating the solution… 😉
  • if dependancy injection is possible (for the sake of unit testing).
  • if variable names as verbose as needed
  • if method names express what they do
  • if a file contains commented out code
  • if a file contains a single class (this one is arguable, depends on agreement with your team and company’s guidelines)
  • for code duplication, function length and class length < x lines – SwiftLint or codebeat mentioned in issue #11 and issue #10 is an option to point out such a code without your participation

When performing a review, one can also get extra points 💎 for pointing out a more efficient implementation of used algorithm 😉.

You should also check for readability. Is the code easy to understand? Do you have to pause frequently during the review to decipher it?

Some 🐦swifts

Boom! Time for some loosely coupled Swift code examples that I look for when reviewing Swift code❗️Wait a moment, this exclamation mark looks strangely familiar … ❗️Look out for code that uses it …
Swift
var foo: Object!
print("\(foo.someString)")

Make sure that your peers (and you as well) use the force unwrapping operator wisely in their code.

If you have ever ended up with code like the one below, then probably something went wrong … 💣💥

override init(_ objectId: String?, 
viewModel1: ViewModel1, 
viewModel2: ViewModel2, 
viewModel3: ViewModel3, 
viewModel4: ViewModel4, 
viewModel5: ViewModel5, 
viewModel6: ViewModel6, 
viewModel7: ViewModel7, 
viewModel8: ViewModel8, 
viewModel9: ViewModel9, 
viewModel10: ViewModel10,
isSomething: Bool = false) { ... }

This is an excerpt from from one of my projects, object types and names are changed . It’s an initialiser of a ‘container’ view model that encapsulates ‘child’ view models used by a container view controller. If you happen to achieve this and there’s no way to change it, probably usage of a configurator facade, default argument values or convenience initialisers are the best solution to live up with your legacy.

class Configurator {
    class func mainViewController() -> MainViewController {
        let dependency1 = Configurator.dependency1()
        let dependency2 = Configurator.dependency2()
        let dependency3 = Configurator.dependency3()
        let dependency4 = Configurator.dependency4()

        return MainViewController(dependency1: dependency1,
            dependency2: dependency2,
            dependency3: dependency3,
            dependency4: dependency4)
    }
}

The off-by-one errors and interchanged greater than & less than were my favourite mistakes at some point of time. I’ve caught myself a few times last year with using if i > array.count instead of if i < array.count ☔️.

Remember local autoreleasepool? For those who answered ‘what!?’ here is some explanation. I pay attention when reviewing body of a loop to check if local autoreleasepool could be used to reduce peak memory footprint.

for i in 1...100
autoreleasepool {
    NSString *superImportantString = "Super \(i)"
    //strange operations take place
    //superImportantString can be released earlier thanks to local autoreleasepool
}

More than a year ago, when Swift was still in its early days, I hated when my team members overused var. It’s ok when needed, but not all the time! Thanks god that now Swift compiler warns about not mutated var.

Another matter is your object’s properties. If it depends on some values, never ever use var. Use let. Always value more immutable state over var! I wonder how many people would argue with me about that☔️😉. And if your property really have to be mutable, expose it ‘to the public’ as immutable like that:

private(set) var foo: AnyObject
let bar: AnyObject

Swift’s protocols are a great deal – do you prefer composition over inheritance? Is such an inheritance chain ok for you? One superclass would be probably ok, but this becomes an exaggeration:

class ChildViewModel: TableViewModel {}
class TableViewModel: CRUDViewModel {}
class CRUDViewModel: BaseViewModel {}
class BaseViewModel {}

Final thoughts

Kofi Annan, Ghanaian ex-Secretary-General of the United Nations, when in primary school, was attending a weekly lesson in ‘spoken English’. He and his peers were asked by the teacher what did they see at the picture:


All shouted together, that they saw a black dot! Professor stepped back and said ‘So, not a single one saw the white sheet of paper. (…). This is the awful thing about human nature. People never see the goodness of things, and the broader picture. (…)’.

It’s also true for code review. We perceive better differences than loads of new code when using a merge tool. We get a cognitive overload if flooded with more content. Have that in mind and use the first rule – review for less than 90 minutes 🕑, take breaks. Remember that:

  • new code is more difficult to understand
  • not all bugs can be caught during a review
  • hot fixes 🔩 without review can happen … 😉

And always – Review your own code first! Before committing a code look through all changes you have made!


Let us know in comments or on Twitter what is on your checklists for code review!

The only true measure of code quality


]]>
http://swifting.io/blog/2016/04/24/14-code-review-tips-swifts/feed/ 1
#7 Do I love or crash something? – shortly on capture lists http://swifting.io/blog/2016/02/28/7-do-i-love-or-crash-something-shortly-on-capture-lists/ http://swifting.io/blog/2016/02/28/7-do-i-love-or-crash-something-shortly-on-capture-lists/#respond Sun, 28 Feb 2016 10:19:25 +0000 http://swifting.io/?p=220 I love pizza… or pasta… or it just crashes…

Some time ago I have attended an iOS meetup in Poznań, on which the attendants were given a short quiz during a break. We had to answer what would be printed in the console after code execution. One of the code snippets looked similarly to this one:

var myLove = "pizza"
let iLove = { [myLove] in
    print(" I love \(myLove)")
}
myLove = "pasta"
iLove()

I have answered that the code would crash, since I have never ever seen before a closure’s capture list without any specifier, i.e. ‘unowned’ or ‘weak’. Two people who solved correctly the whole 3-questions long quiz were supposed to get a fancy reward. I was sure the code would crash after execution of the snippet. In fact it cannot crash. The code is correct. Want to know what would be printed out to the console? Read further 😉 !

This thing in a closure that starts and ends with a square bracket [ ]…

What a capture list is? A closure can capture values from their surrounding context, i.e. an enclosing function, class, struct or any scope defined by curly braces { }. Value is captured implicitly by using a variable defined in this surrounding context or explicitly by declaring a captured value in square brackets [myLove, myLover] in. This declaration is called closure’s capture list. Swift’s capture lists are used to break strong reference cycles between a captured object and a closure.

Capturing a value: strong, weak and unowned reference

Value can be captured strongly by a closure, i.e. a strong reference to an object is created, like here:

var capturedObject = MyObject()
let closure = {
    capturedObject.foo()
}

If a closure was defined as a property of a class and if we used inside that closure another property and did not use a capture list, a strong reference cycle would be created (you should rather avoid this). This code leads to a memory leak:

class Capturer {
    var capturedObject = MyObject()
    let closure = {
        capturedObject.foo()
    }   
}

To avoid a leak, value should by captured as weak or unowned in closure’s capture list. A weak value is used inside a closure as an optional, whereas an unowned as an implicitly unwrapped optional (code crashes if value is nil). Beware!

class Capturer {
    var capturedObject = MyObject()
    let closure = { [unowned capturedObject]
        capturedObject.foo()
    }   
    let closure = { [weak capturedObject]
        guard let capturedObject = capturedObject else { return }
        capturedObject.foo()
    }   
}

When a closure does not escape

Sometimes closure passed as a function’s argument does not escape and a reference cycle between objects won’t be created. When does it happen? For sure when the closure that is a function’s argument is tagged with the @noescape attribute. It assures that the closure won’t be used anywhere else apart function’s scope, so you can omit capture list.

Another interesting information I’ve found is that global functions do not capture any values. Thanks to that we can omit for example [unowned self] in GCD dispatch calls (i.e. in dispatch_async() and dispatch_sync() and others).

When passing animation blocks to UIView.animateWithDuration() you can also omit capture list. Good point from krakendev that since animateWithDuration is a static method on UIView the closure passed that uses self does not have to be captured using a capture list.

Missing "in" keyword

Last week Chris Eidhof mentioned in a tweet a missing ‘in’ keyword that caused compiler to treat [x] as an unused array literal. Remember about the in in closures (when needed of course) :)!

Ok, but what about this pizza thing?

I didn’t win this fancy price. This really strange for me syntax was correct. Value captured in a closure without any specifier causes closure to store a copy of the value at the time of closure definition. Want to know what iLove() did? Check this link for the answer. BTW, What do you think about the Bluemix?

If you want to dig more on closures and retain cycles you can check the documentation, krakendev and digital leaves blog posts.

And in fact I love both: pasta and pizza. And the code won’t crash ;)!

]]>
http://swifting.io/blog/2016/02/28/7-do-i-love-or-crash-something-shortly-on-capture-lists/feed/ 0