Rupert  Beatty

Rupert Beatty

1673078940

SwiftEchoes-Tips: Swift tips and tricks

🔥 SwiftEchoes - Tips

Here's list of tips and tricks with all additional sources (playgrounds, images) that I would like to share. Also you can find them on Twitter @szubyak, where you can ask questions and respond with feedback. I will really glad to have you there! 😀

63 How to make UIStoryboard usage safer

Below I want to show you a few simple ways how to make usage of string literals connected with UIStoryboard much more safer.

Global constant string literals
At first it sounds like a good idea. You only need to define the constant once and it will be available everywhere. Then if you want to change its value, there is only one place in code to change for which the effects will cascade throughout the project. But global constants have some disadvantages. Not as much as global variables but still enought to stop using them. It's not the best practice. I will cover the topic of andvatages and disadavntages of global constants/variables usage in my next posts.

Relatable storyboard names
Your storyboards should be named after the sections of which they cover. It's a general rule. If you have a storyboard which houses view controllers are related to Profile, then the name of that storyboard’s file should be Profile.storyboard.

Uniform storyboard identifiers
When you want to use Storyboard Identifiers on your view controllers, usage of the class names as identifiers will be a good practice. For example, “ProfileViewController” would be the identifier for ProfuleViewController. Adding this to your naming convention is a good idea.

Enum
Try to consider enums as uniform, centralized global string literal identifiers for 'UIStoryboard'. You can create UIStoryboard class extension which defines all the storyboard files you have in your project. You can also add the convenience initializer or class function to add more syntactic sugar.

extension UIStoryboard {
    enum Storyboard: String {
        case profile
        case login
        
        var name: String {
            return rawValue.capitalized
        }
    }
    
    convenience init(storyboard: Storyboard, bundle: Bundle? = nil) {
        self.init(name: storyboard.name, bundle: bundle)
    }
    // let storyboard = UIStoryboard(storyboard: .profile)
    
    class func storyboard(storyboard: Storyboard, bundle: Bundle? = nil) -> UIStoryboard {
        return UIStoryboard(name: storyboard.name, bundle: bundle)
    }
    // let storyboard = UIStoryboard.storyboard(.profile)
}

I hope provided solutions will really help you to maintain your storyboards.

62 "Massive" Storyboard

I'm prety sure that almost every one has heard about "main" MVC problem known as Massive View Controller and how fashionable architectures (MVVM, VIPER, CleanArchitecture) valiantly fight it. But I want to pay attation to not less common problem - massive storyboard. Because of this many developers prefer to make layouts in code insted of storyboard. I've to admit that developers despise storyboards not without reason.

Let's list main storyboard problems:

  • Storyboard always become unwieldy and unmanageable.
  • Storyboard is slow. Extremely slow. As it grows in size, it doesn't only become unhandy for developers but also for XCode. Once you tap on a storyboard file of this size, you can leave your seat, grab a cup of coffee and come back. You’ll be lucky if XCode has loaded the storyboard.
  • Merge Conflicts in Source Control. It’s always a painful experience when multiple developers are working on a project as it almost always leads to merge conflicts in storyboard. Merging storyboards is hell because of their nature - storyboards are just hugeass XML files.

Inspite of the issues, I'm convinced there is a way to make storyboard great again 🇺🇸, resolving most of the above.

"A storyboard is meant to explain a story, not a saga."

Storyboard can be easily splitted into multiple storyboards, with each one representing an individual story. Yes, it's OK not to have one big, fat, slowloading storyboard file but have 30+ devided storyboards.

Also don't forget about Storyboard References introduced in iOS 9.

But splitting storyboard will not help with one serious problem. Storyboard usage has strong coupling with one silent killer known as String Literals. Look for the solution in 63 StoryboardIdentifiable protocol

61 XCTUnwrap assertion function

In Xcode 11 new assertion function has been added for use in Swift tests. XCTUnwrap asserts that an Optional variable’s value is not nil, returning the unwrapped value of expression for subsequent use in the test. It protects you from dealing with conditional chaining for the rest of the test. Also it removes the need to use XCTAssertNotNil(_:_:file:line:) with either unwrapping the value. It’s common to unwrap optional before checking it for a particular value so that’s really where XCTUnwrap() will come into its own.


struct Note {
    var id: Int
    var title: String
    var body: String
}

class NotesViewModel {

    static func all() -> [Note] {
        return [
            Note(id: 0, title: "first_note_title", body: "first_note_body"),
            Note(id: 1, title: "second_note_title", body: "second_note_body"),
        ]
    }
}


func testGetFirstNote() throws {
    let notes = NotesViewModel.all()
    let firstNote =  try XCTUnwrap(notes.first)
    XCTAssertEqual(firstNote.title, "first_note_title")
}

This approach is cleaner than what we might have written previously:

func testGetFirstNote() {
    let notes = NotesViewModel.all()
    if let firstNote = notes.first {
        XCTAssertEqual(firstNote.title, "first_note_title")
    } else {
        XCTFail("Failed to get first note.")
    }
}

60 UITableViewCell identifier

To register or dequeue UITableViewCell object we need to provide its string type identifier. Typing string by hand is wasting time and could couse you typos. In this case I would recomend to use extension which declares static identifier property inside UITableViewCell to avoid these problems.

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

let cell = tableView.dequeueReusableCell(withIdentifier: ExampleTableViewCell.identifier)
tableView.register(ExampleTableViewCell.self, forCellReuseIdentifier: ExampleTableViewCell.identifier)

print(ExampleTableViewCell.identifier) //ExampleTableViewCell

59 AlertPresentable protocol

In my current project I work on I present alerts almost on every view controller. To reduce lines of codes and time spent on duplicate code I created AlertPresentable layer and want to share it with you. Any ViewController which implements AlertPresentable protocol receive opportunity to present any type of alerts discribed in this layer just by one line of code.

protocol AlertPresentable {
    func presentErrorAlert(with message: String)
    func presentSuccessAlert(with message: String, action: ((UIAlertAction) -> Void)?)
    func presentConfirmationAlert(with message: String, action: ((UIAlertAction) -> Void)?)
}

extension AlertPresentable where Self: UIViewController {

    func presentErrorAlert(with message: String) {
        presentAlert(message: message, actions: [UIAlertAction(title: "OK", style: .cancel, handler: nil)])
    }

    func presentSuccessAlert(with message: String, action: ((UIAlertAction) -> Void)?) {
        presentAlert(message: message, actions: [UIAlertAction(title: "OK", style: .cancel, handler: action)])
    }

    func presentConfirmationAlert(with message: String, action: ((UIAlertAction) -> Void)?) {
        presentAlert(message: message, actions: [UIAlertAction(title: "Yes", style: .default, handler: action), UIAlertAction(title: "No", style: .cancel, handler: nil)])
    }

    private func presentAlert(title: String? = "", message: String? = nil, actions: [UIAlertAction] = []) {
        let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
        actions.forEach { (action) in
            alertController.addAction(action)
        }
        present(alertController, animated: true, completion: nil)
    }
}

Usage:

class ViewController: UIViewController, AlertPresentable {

    override func viewDidLoad() {
        super.viewDidLoad()

        presentErrorAlert(with: "User not found")

        presentSuccessAlert(with: "File downloaded") { _ in
            // use downloaded file
        }

        presentConfirmationAlert(with: "Are you sure you would like to sign out?") { _ in
            // sign out user
        }
    }
}

58 CollectionView extension for adaptive grid layout

Implementation of grid CollectionView layout is a commont task. But I found that calculation of cell width when you dont know how many cells can fit in one row of CollectionView is not a common task.

Here is my extension for calculation width of cell in grid CollectionView to make your layot adaptive.

extension UICollectionView {

    func flexibleCellWidth(minCellWidth: CGFloat, minimumInteritemSpacing: CGFloat) -> CGFloat {
        let contentWidth = frame.size.width - contentInset.left - contentInset.right
        let numberOfItemsInRow = Int((contentWidth + minimumInteritemSpacing) / (minCellWidth + minimumInteritemSpacing))
        let spacesWidth = CGFloat(numberOfItemsInRow - 1) * minimumInteritemSpacing
        let availableContentWidth = contentWidth - spacesWidth
        return availableContentWidth / CGFloat(numberOfItemsInRow)
    }
}

Based on minimal cell width and minimal interitem spacing it autocalculates number of cells in row and return cell width for the best placement.

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    let cellWidth = collectionView.flexibleCellWidth(minCellWidth: 72, minimumInteritemSpacing: 10)
    return CGSize(width: cellWidth, height: cellWidth)
}

iPhoneSE

iPhoneX

57 Render HTML within a UILabel

You can render HTML strings within a UILabel using a special initializer of NSAttributedString and passing in NSAttributedString.DocumentType.html for .documentType. But in most cases it is not enough to display it as is. If we want to add custom font or color we need to use CSS (add CSS header to our HTML string).

extension String {

    func htmlAttributedString(with fontName: String, fontSize: Int, colorHex: String) -> NSAttributedString? {
        do {
            let cssPrefix = "<style>* { font-family: \(fontName); color: #\(colorHex); font-size: \(fontSize); }</style>"
            let html = cssPrefix + self
            guard let data = html.data(using: String.Encoding.utf8) else {  return nil }
            return try NSAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding: String.Encoding.utf8.rawValue], documentAttributes: nil)
        } catch {
            return nil
        }
    }
}

Usage example

56 Custom Error by adopting LocalizedError protocol

Custom errors are an integral parts of you work. Swift-defined error types can provide localized error descriptions by adopting the new LocalizedError protocol.

enum UserError: Error {
    case credentialsNotMatch
    case invalidEmail
    case invalidName
}

extension UserError: LocalizedError {
    public var errorDescription: String? {
        switch self {
            case .credentialsNotMatch:
                return NSLocalizedString("Your username and/or password do not match", comment: "Credentials do not match")
            case .invalidEmail:
                return NSLocalizedString("Please enter email address in format: yourname@example.com", comment: "Invalid email format")
            case .invalidName:
                return NSLocalizedString("Please enter you name", comment: "Name field is blank")
        }
    }
}

Example:

func validate(email: String?, password: String?) throws {
    throw UserError.credentialsNotMatch
}

do {
    try validate(email: "email", password: "password")
} catch UserError.credentialsNotMatch {
    print(UserError.credentialsNotMatch.localizedDescription)
}

You can provide even more information. It's common decency to show user except error description also failure reasons and recovery suggestions. Sorry for ambiguous error messages :)

enum ProfileError: Error {
    case invalidSettings
}

extension ProfileError: LocalizedError {
    public var errorDescription: String? {
        switch self {
            case .invalidSettings:
                return NSLocalizedString("Your profile settings are incorrect", comment: "")
        }
    }

    public var failureReason: String? {
        switch self {
            case .invalidSettings:
                return NSLocalizedString("I don't know why", comment: "")
        }
    }
    
    public var recoverySuggestion: String? {
        switch self {
            case .invalidSettings:
                return NSLocalizedString("Please provide correct profile settings", comment: "")
        }
    }
}

let error = ProfileError.invalidSettings
print(error.errorDescription)
print(error.failureReason)
print(error.recoverySuggestion)

55 Result type without value to provide

Result type usage is really popular nowadays.

enum Result<T> {
    case success(result: T)
    case failure(error: Error)
}

func login(with credentials: Credentials, handler: @escaping (_ result: Result<User>) -> Void) {
    // Two possible options:
    handler(Result.success(result: user))
    handler(Result.failure(error: UserError.notFound))
}

login(with:) operation has user value to provide and default Result type fits perfectly here. But let’s imagine that your operation hasn’t got value to provide or you don’t care about it. Default Result type makes you to provide the result value any way.

To fix this inconvenience you need to add extension and instantiate a generic with an associated value of type Void.

func login(with credentials: Credentials, handler: @escaping (_ result: Result<Void>) -> Void)

extension Result where T == Void {
    static var success: Result {
        return .success(result: ())
    }
}

Now we can change our func login(with:) a bit, to ignore result success value if we don’t care about it.

func login(with credentials: Credentials, handler: @escaping (_ result: Result<Void>) -> Void) {
    // Two possible options:
    handler(Result.success)
    handler(Result.failure(error: UserError.notFound))
}

54 Given, When, Then

In unit testing terms, there are three phases to a unit test:

  • Given prepares the preconditions for the test. These preconditions include arguments to the function call, states of the subject, and any test doubles that are used to inspect the test subject in subsequent assertions.
  • The When phase can be as simple as making the call to the function on the test subject. In the when section an operation to be tested is performed.
  • The Then phase verifies the actual results against your expected results. These are done with assertions statements. An assertion can check if something is true or false, nil or non-nil, two objects match or not.
func testSum() {
    // Given
    let firstValue = 4
    let secondValue = 3
    // When
    let sum = sut.sum(firstValue: firstValue, secondValue: secondValue)
    // Then
    XCTAssertEqual(sum, 7)
}

Note: When comparing two values using XCTAssertEqual you should always put the actual value first before the expected value.

53 sut and test lifecycle

The subject under test is always named sut . It refers to a subject that is being tested for correct operation. It is short for "whatever thing we are testing" and is always defined from the perspective of the test. So when you look at your tests, there is no ambiguity as to what is being tested. All other objects are named after their types. Clearly identifying the subject under test in the sut variable makes it stand out from the rest.

The test lifecycle for each test is surrounded by a pair of functions - setUp() and tearDown(). The setUp() function is called before the execution of each test function in the test class. The tearDown() function is called after the execution of each test function in the test class. They provide you a place to prepare the sut ready before the test starts, and clean up the sut and any states after the test finishes. It’s important that each test starts with the desired initial state.

import XCTest

class YourClassTests: XCTestCase {

    // MARK: - Subject under test
    
    var sut: YourClass!
    
    // MARK: - Test lifecycle
    
    override func setUp() {
        super.setUp()
        sut = YourClass()
    }
    
    override func tearDown() {
        sut = nil
        super.tearDown()
    }
}

52 Point on circle perimeter

Could be useful solution for positioning nodes in your game or views in ordinary business application on the perimeter of circle.

func pointOnCircle(radius: Float, center: CGPoint) -> CGPoint {
    // Random angle between 0 and 2*pi
    let theta = Float(arc4random_uniform(UInt32.max)) / Float(UInt32.max-1) * .pi * 2.0
    // Convert polar to cartesian
    let x = radius * cos(theta)
    let y = radius * sin(theta)
    return CGPoint(x: CGFloat(x) + center.x, y: CGFloat(y) + center.y)
}

51 zip() function

Let’s imagine you have an array of LOTR heroes, and an array of weapons that match those heros. How could I create a list with the heros and weapons combined at each index? zip() function is designed to combine two sequences into a single sequence of tuples.

Note: I would recomend to wrap the output from zip() into array to make its output easier to read.

One of the useful features of zip() is that if your two arrays arent equal in size it will choose the shorter one. Yes, it will not crash your application, just ignore element which doesn’t have a match in second sequence.

50 StackView custom spacing

StackView, introduced in iOS 9, made Auto Layout usage much easier, because of reducing the amount of constraints needed to create manually for common layouts. But we faced with the problem, property spacing applies equally to the arranged subviews of StackView. Before iOS 11, there were 2 ways how to overcome this problem. One way is to create views with desired height and use them as spacer views or we could do it by nesting stack views but these two ways always seemed to be an unnecessary complication.

In iOS 11 you can create stack views with custom spacing between views. But there is no way to specify this custom spacing in Storyboard, only in code.

stackView.setCustomSpacing(32.0, after: label)

Also, in iOS 11 Apple introduced default and system spacing properties on the UIStackView class. You can use these to set or reset the custom spacing after a view.

stackview.setCustomSpacing(UIStackView.spacingUseSystem, after: label)

49 Named UIColor

XCode 9.0 gives us opportunity to create named colors. We can do it directly inside assets catalog and use it in code and storyboards. Named colors contain 3 parts: name ("FerrariRed"), color specified as range and device compatibility.

Created named color you can use like this:

view.backgroundColor = UIColor(named: "FerrariRed")

Inside storyboards you can use created named color by selecting it from the color dropdown menu.

48 Result error handling

Error handling is realy common functionality. But if you want to handle errors across asynchronous boundaries or store value/error results for later processing, then ordinary Swift error handling won’t help. The best alternative is a common pattern called a Result type: it stores a value/error “sum” type (either one or the other) and can be used between any two arbitrary execution contexts.

Example of Result type:

enum Result<T> {
    case success(result: T)
    case failure(error: String)
}

Usage of Result type:

protocol TaskStoreProtocol {
    func fetchTasks(handler: @escaping TaskStoreFetchTasksResult)
}

typealias TaskStoreFetchTasksResult = (_ result: Result<[Task]>) -> Void

class TasksStore: TasksStoreProtocol {
    
    func fetchTasks(handler: @escaping TaskStoreFetchTasksResult) {
        // Some useful code, network request or something like this goes here
        if success {
            handler(Result.success(result: fetchedTasks))
        } else {
            handler(Result.failure(error: error))
        }
    }
}

47 Generics: Type parameters

Here I want to introduce you with general info about generic type parameters and their naming style. Type parameters specify and name a placeholder type, and are written immediately after the function’s name, between a pair of angle brackets (such as <T>). Once you specify a type parameter, you can use it to define the type of a function’s parameters, or as the function’s return type, or as a type annotation within the body of the function. In each case, the type parameter is replaced with an actual type whenever the function is called.

You can provide more than one type parameter by writing multiple names within the angle brackets, separated by commas.

In most cases, type parameters have descriptive names, such as Key and Value in Dictionary<Key, Value> and Element in Array<Element>, which tells about the relationship between the type parameter and the generic type or function it’s used in. However, when there isn’t a meaningful relationship between them, it’s traditional to name them using single letters such as T, U, and V.

46 Generics: Basics

You can find usage of generics in several of my tips. So here I want to show you some basics of generics. They are one of the most powerful features of Swift. It gives you opportinity to write flexible, reusable functions. Types that can work with any type, subject to requirements that you define. You can write code that avoids duplication and expresses its intent in a clear, abstracted way.

At first, here is nongeneric function which swaps two Int values:

func swapTwoInts(_ a: inout Int, _ b: inout Int) {
    let temporaryA = a
    a = b
    b = temporaryA
}

As you can see, this func is useful but a bit limited, because you can swap only Int values. If you want to swap two String values, or two Double values, you have to write more functions.

Generic functions can work with any type. Here’s a generic representation of our function.

func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
    let temporaryA = a
    a = b
    b = temporaryA
}

The body of our two functions is the same. But the first line of is slightly different. The generic version of the function uses a placeholder type name (called T) instead of an actual type name (such as Int, String, or Double). The placeholder type name doesn’t say anything about what T must be, but it does say that both a and b must be of the same type T. The actual type to use in place of T is determined each time the function is called.

45 UserDefaults during testing

As with all dependencies, we should reduce UserDefaults to an abstract protocol, inject it into the classes that require it, and thereby decouple ourselves from implementation details and increase the testability of our code by introducing seams. But I'll be honest, the UserDefaults API is so simple and pervasive that standard dependency management feels like overkill. If so, we've probably encountered the same problem: without the ability to inject mocks, testing code that makes use of UserDefaults can be a pain. Any time we use our test device (including running tests on it!) we potentially change the state of its persisted settings.

Here is extension UserDefaults that gives your test method an opportunity to run in clean state and not jinx real persisted settings.

extension UserDefaults {
    static func blankDefaultsWhile(handler:()->Void){
        guard let name = Bundle.main.bundleIdentifier else {
            fatalError("Couldn't find bundle ID.")
        }
        let old = UserDefaults.standard.persistentDomain(forName: name)
        defer {
            UserDefaults.standard.setPersistentDomain( old ?? [:], forName: name)
        }
        UserDefaults.standard.removePersistentDomain(forName: name)
        handler()
    }
}

Usage:

class MyTests: XCTestCase {
    func testSomething() {
        // Defaults can be full of junk.
        UserDefaults.blankDefaultsWhile {
            // Some tests that expect clean defaults.
            // They can also scribble all over defaults with test-specific values.
        }
        // Defaults are back to their pre-test state.
    }
}

44 Additional Info to #38 Protocols: Optional methods

Here is one more trick how to make protocol method to be optional. Let’s remember why we would like to declare a protocol method as optional? It is because we we don’t want to write the implementation if that method will not be used. So Swift has a feature called extension that allow us to provide a default implementation for those methods that we want to be optional.

protocol CarEngineStatusDelegate {
    func engineWillStop()
    func engineDidStop()
}

extension CarEngineStatusDelegate {
    func engineWillStop() {}
}

This way the class/struct that will use our protocol will only need to implement func engineDidStop().

43 Responsible view controller for particular view

How to find the view controller that is responsible for a particular view? This is as easy as walking the responder chain looking for the first UIViewController you find.

extension UIView {
    func findViewController() -> UIViewController? {
        if let nextResponder = self.next as? UIViewController {
            return nextResponder
        } else if let nextResponder = self.next as? UIView {
            return nextResponder.findViewController()
        } else {
            return nil 
        }
    }
}

You should only use it when you really need it – if you’re able to call methods directly using a delegate or indirectly by posting notifications then do it in first place.

42 Move between textfields

Lets imagine full of texfields scene (e.g. UserProfile scene). Moving between textfields using Next/Return on on-screen keyboard is integral functionality.

func textFieldShouldReturn(_ textField: UITextField) -> Bool {
    let nextTag = textField.tag + 1
    if let nextResponder = textField.superview?.viewWithTag(nextTag) {
        nextResponder.becomeFirstResponder()
    } else {
        textField.resignFirstResponder()
    }
    return true 
}

If you need to force the first responder to resign itself and aren’t sure which textfield is in control, it’s easier to use view.endEditing(true).

41 Autogenerated allCases property for your enum (Swift 4.2)

Retrieving all cases of enum is really common task, but it's implementation is not so clean. We were forced to create static variable of array with all cases of our enum manually.

enum Cars {
    case BMW
    case Audi
    case Volkswagen
    case Mercedes

    static let allCases = [BMW, Audi, Volkswagen, Mercedes]
}

In Swift 4.2 we got really useful property allCases, which is autogenerated (all you need to do is make your enum conform to the CaseIterable protocol), so it releases us from additional steps.

enum Cars: CaseIterable {
    case BMW
    case Audi
    case Volkswagen
    case Mercedes
}

for car in Cars.allCases {
    print(car)
}

40 Protocols: Class-only
 

One more thing left to say about protocols. Sometimes protocols should be adopted only by classes. To achieve this behaviour you should define you protocol with class keyword or by inheriting from AnyObject. This is commonly happens because you have a delegate property that needs to use weak storage to avoid the risk of a strong reference cycle (formerly known as a retain cycle).

weak var delegate: CarDelegate?

The weak keyword can't be used with structs and enums, because they are value types.

39 Protocols: Inheritance and composition
 

In previous tip you can observe how to split your protocol to smaller pieces. Here I will show you how to combine your protocols to make bigger one using inheritance and composition. To merge protocols together use & for protocol composition.

typealias Codable = Decodable & Encodable

You can also use protocol inheritance to build larger protocols.

protocol CarEngineStatusDelegate { }
protocol CarMovingStatusDelegate { }
protocol CarDelegate: CarMovingStatusDelegate, CarEngineStatusDelegate { }

38 Protocols: Optional methods
 

If you implement a protocol in Swift you must implement all its requirements. Optional methods are not allowed.

Example:

protocol CarDelegate {
    func engineDidStart()
    func carShouldStartMoving() -> Bool
    func carDidStartMoving()
    func engineWillStop()
    func engineDidStop()
}

But there are a few tricks how to make some methods to be optionals:

  • You can split them in two protocols, and adopt only needed one. ```swift protocol CarMovingStatusDelegate { func carShouldStartMoving() -> Bool func carDidStartMoving() }

protocol CarEngineStatusDelegate { func engineDidStart() func engineWillStop() func engineDidStop() }

* Use the `@objc` attribute on a Swift protocol to receive opportunity to mark methods as being optional, but you may no longer use Swift structs and enums with that protocol, and you may no longer use protocol extensions.
```swift
@objc protocol CarDelegate {
    @objc optional func engineDidStart()
    func carShouldStartMoving() -> Bool
    func carDidStartMoving()
    @objc optional func engineWillStop()
    @objc optional func engineDidStop()
}

Here is the optional method usage:

delegate?.engineWillStop?()

The delegate property is optional because there might not be a delegate assigned. And in the CarDelegate protocol we made engineWillStop() an optional requirement, so even if a delegate is present it might not implement that method. As a result, we need to use optional chaining.

37 Protocols: Naming
 

If we are talking about naming the protocol itself:

  • Protocols that describe what something is should read as nouns (e.g. Collection).
  • Protocols that describe a capability should be named using the suffixes able, ible, or ing (e.g. Equatable, ProgressReporting).

Briefly talking about methods names inside protocol, you can use whatever names you want. But, if your protocol is created to represent delegates please look: #32 Delegate naming

36 Property observers, getter/setter and lazy are mutually exclusive
 

Little thing that surprisingly became a discovery for me, because it's rare case in practice, so I'm not ashamed of it 🤣. Property observers, getter/setter and lazy cant be used together, they are mutually exclusive. So you should choose what pattern makes most profit to you in particaulr situation.

35 Prepare Alamofire standalone functions to unit-testing

Methods are functions associated with a type. Mock the type, and you can intercept the method. In Swift, we prefer to do this with protocols. But a standalone function lives on its own, without any associated data. In other words, it has no self. During unit-testing we want to intercept these calls, for two main reasons:

  • To spy on the arguments a function receives.
  • To stub any return values.

In popular networking 3party library Almofire all request, upload or download functions implemented as standalone functions. So, we have dificulties with intercepting during unit testing. We can't mock associated type, because there is no type. Also responseJSON function is implemented in extension, so we can't make any changes or override it.

I defined a new class with 2 methods (request and responseJSON) with the same method signature as Alamofire's request and responseJSON methods. I called the Alamofire version of these methods inside. And then used this class as a dependency in my sut.

import Alamofire

class AlamofireWrapper {
    @discardableResult
    public func request(_ url: URLConvertible, method: HTTPMethod = .get, parameters: Parameters? = nil, encoding: ParameterEncoding = URLEncoding.default, headers: HTTPHeaders? = nil) -> DataRequest {
        return Alamofire.request(url, method: method, parameters: parameters, encoding: encoding, headers: headers)
    }

    public func responseJSON(request: DataRequest, queue: DispatchQueue? = nil, options: JSONSerialization.ReadingOptions = .allowFragments, completionHandler: @escaping (DataResponse<Any>) -> Void) {
        request.responseJSON(queue: queue, options: options, completionHandler: completionHandler)
    }
}

My request part starts looking like this:

var alamofireWrapper = AlamofireWrapper()

let request = alamofireWrapper.request(url, parameters: params, encoding: URLEncoding(destination: .queryString))
alamofireWrapper.responseJSON(request: request) { response in
    if response.result.isSuccess {
        success(response.result.value)
    } else if let error = response.result.error {
        failure(error)
    }
}

And here is my test double finaly:

// MARK: - Test doubles

class AlamofireWrapperSpy: AlamofireWrapper {

    enum RequestStatus {
        case success
        case failure
    }

    var status: RequestStatus = .success

    // MARK: Method call expectations

    var requestCalled = false
    var responseJSONCalled = false

    // MARK: Spied methods

    override func request(_ url: URLConvertible, method: HTTPMethod, parameters: Parameters?, encoding: ParameterEncoding, headers: HTTPHeaders?) -> DataRequest {
        requestCalled = true
        return  SessionManager.default.request("dummy_url")
    }

    override func responseJSON(request: DataRequest, queue: DispatchQueue?, options: JSONSerialization.ReadingOptions, completionHandler: @escaping (DataResponse<Any>) -> Void) {
        responseJSONCalled = true
        switch status {
        case .success:
            let value = ["result": "best_response_ever"]
            completionHandler(DataResponse(request: nil, response: nil, data: nil, result: Result.success(value)))
        case .failure:
            let error = NSError(domain: "", code: 500, userInfo: nil)
            completionHandler(DataResponse(request: nil, response: nil, data: nil, result: Result.failure(error)))
        }
    }
}

34 Sort array of objects with multiple optional criteria

Few days ago I faced a sorting task. I needed to sort array of object by more then one criteria that is also optional. My object was Place with rating and distance properties. rating was main criteria for sorting and distance was secondary. So, here is my workouts.


struct Place {
    var rating: Int?
    var distance: Double?
}

func sortPlacesByRatingAndDistance(_ places: [Place]) -> [Place] {
    return places.sorted { t1, t2 in
        if t1.rating == t2.rating {
            guard let distance1 = t1.distance, let distance2 = t2.distance else {
                return false
            }
            return distance1 < distance2
        }
        guard let rating1 = t1.rating, let rating2 = t2.rating else {
            return false
        }
        return rating1 > rating2
    }
}

let places = [Place(rating: 3, distance: 127), Place(rating: 4, distance: 423), Place(rating: 5, distance: nil), Place(rating: nil, distance: 100), Place(rating: nil, distance: 34), Place(rating: nil, distance: nil)]

let sortedPlaces = sortPlacesByRatingAndDistance(places) // [{rating 5, nil}, {rating 4, distance 423}, {rating 3, distance 127}, {nil, distance 34}, {nil, distance 100}, {nil, nil}]

33 Remove object from array

Super lightweight extension 🎈. How to remove particular object from array. Remove first collection element that is equal to the given object.

extension Array where Element: Equatable {
    mutating func remove(_ object: Element) {
        if let index = index(of: object) {
            remove(at: index)
        }
    }
}

32 Delegate naming

When creating custom delegate methods take into consideration the following list items:

  • You can give the name to your delegate property delegate, if it is just one of them, or put delegate at the end, when it is more than one. Here is the example, WKWebView has uiDelegate and navigationDelegate properties that can point to two different objects. 

  • You should use Apple’s standard approach to avoid verb conjugation. It says that many of your method names will use will, did, and should.
  • Start the name by identifying the class of the object that’s sending the message func tableView(_ tableView: UITableView,.... It helps in situations where there is more than one object that can send a message .

As a result, Apple delegates have a very specific naming convention:

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)

31 Run, Playground, run!

Asynchronous work in Playground.

Tell the playground it should continue running forever, otherwise it will terminate before the asynchronous work has time to hppen.

PlaygroundPage.current.needsIndefiniteExecution = true

30 DispatchGroup usage

Let’s say you’ve got several long running tasks to perform. After all of them have finished, you’d like to run some further logic. You could run each task in a sequential fashion, but that isn’t so efficient - you’d really like the former tasks to run concurrently. DispatchGroup enables you to do exactly this.

let dispatchGroup = DispatchGroup()

for i in 1...5 {
    dispatchGroup.enter()
    Alamofire.request(url, parameters: params).responseJSON { response in
        //work with response
        dispatchGroup.leave() 
    }
}

dispatchGroup.notify(queue: .main) {
    print("All requests complete")
}

In the above, all long running functions will perform concurrently, followed by the print statement, which will execute on the main thread.

Each call to enter() must be matched later on with a call to leave(), after which the group will call the closure provided to notify().

DispatchGroup has a few other tricks:

  • Instead of notify(), we can call wait(). This blocks the current thread until the group’s tasks have completed.
  • A further option is wait(timeout:). This blocks the current thread, but after the timeout specified, continues anyway. To create a timeout object of type DispatchTime, the syntax .now() + 1 will create a timeout one second from now.
  • wait(timeout:) returns an enum that can be used to determine whether the group completed, or timed out.

29 Remove duplicates

Clear way 🛣️ to return the unique list of objects based on a given key 🔑. It has the advantage of not requiring the Hashable and being able to return an unique list based on any field or combination.

extension Array {
    func unique<T:Hashable>(map: ((Element) -> (T)))  -> [Element] {
        var set: Set<T> = []
        var arrayOrdered: [Element] = []
        for value in self {
            if !set.contains(map(value)) {
                set.insert(map(value))
                arrayOrdered.append(value)
            }   
        }
        return arrayOrdered
    }
}

28 Debugging: View Debugging

Using View Debugging you’re now able to inspect an entire view hierarchy visually – right from within Xcode, instead of printing frames to the console and trying to visualize layouts in your head.

You can invoke the view debugger by choosing View UI Hierarchy from the process view options menu in the debug navigator, or by choosing Debug > View Debugging > Capture View Hierarchy.

You'll see a 3D representation of your view, which means you can look behind the layers to see what else is there. The hierarchy automatically puts some depth between each of its views, so they appear to pop off the canvas as you rotate them. If you have a complicated view layout, View Debugging > Show View Frames option will draw lines around all your views so you can see exactly where they are.

More about view debugging here

27 Debugging: Breakpoints

A breakpoint is a debugging tool that allows you to pause the execution of your program up to a certain moment. Creating pause points in your code can help you investigate your code. While your app is paused, light green arrow that shows your current execution position can be moved. Just click and drag it somewhere else to have execution pick up from there – although Xcode will warn you that it might have unexpected results, so tread carefully!

Right-click on the breakpoint (the blue arrow marker) and choose Edit Breakpoint.

In the popup that appears you can set the condition. Execution will now pause only when your condition is true. You can use conditional breakpoints to execute debugger commands automatically – the Automatically continue checkbox is perfect for making your program continue uninterrupted while breakpoints silently trigger actions. Also you can set Ignore times before stoping and actions like Debuger command, Log message, Sound, etc.

Shortcuts:

F6- Step Over. Ctrl+Cmd+Y - Continue (continue executing my program until you hit another breakpoint)

In Xcode debug console you can use po to print what you need during pause.

 

26 Debugging: Asserts

assert() is debug-only check that will force your app to crash if specific condition is false.

assert(4 == 4, "Maths error") //OK
assert(3 == 2, "Maths error") //Crash

As you can see assert() takes two parameters:

  • Something to check.
  • Message to print out of the check fails.

If the check evaluates to false, your app will be forced to crash because you know it's not in a safe state, and you'll see the error message in the debug console. If you don’t have a condition to evaluate, or don’t need to evaluate one, you can use assertionFailure() function.

precondition() is not debug-only check. It will crash your app even in release mode.

precondition(4 == 4, "Maths error") //OK
precondition(3 == 2, "Maths error") //Crash

preconditionFailure() works the same as assertionFailure(). With the same difference as above, it works for release builds.

fatalError(), like assertionFailure() and preconditionFailure() works for all optimisation levels in all build configurations.

fatalError("ERROR")

More about asserts and optimisation levels you can find here

25 Debugging: Log functions

Debuging 👨‍🔧 is one of the most importent aspects of programing👨‍💻. It should be in your skillbox anyway. It contains log functions, asserts, breakpoints and view debuging. Let's observe everyone of them just one by one. And here are log functions in the crosshairs 🔍.

We know print() as a variadic function. Function that accepts any number of parameters.

print("one", "two", "three", "four") //one two three four

But it's variadic nature becomes much more useful when you use separator and terminator, its optional extra parameters. separator gives you opportunity to provide a string that should be placed between every item. It's "space" by default.

print("one", "two", "three", "four", separator: "-") //one-two-three-four

Meanwhile terminator is what should be placed after the last item. It’s \n by default, which means "line break".

print("one", "two", "three", "four", terminator: " five") //one two three four five

24 Update UIView content with animation

Really lightweight way 🎈 How to add content changing animation to UIView and it subclasses.

extension UIView {
    func fadeTransition(_ duration: CFTimeInterval) {
        let animation = CATransition()
        animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
        animation.type = kCATransitionFade
        animation.duration = duration
        layer.add(animation, forKey: kCATransitionFade)
    }
}

Just invoke 🧙‍♂️ fadeTransition(_ duration: CFTimeInterval) by your view before you will apply a change.

label.fadeTransition(1)
label.text = "Updated test content with animation"

23 Observe MOC changes

Next code snippet 📃 I use to keep eye on changes that take place in the managed object context. Useful thing to know what's going on, what was added, updated ( what specific values were changed ) or deleted 📥📝📤

func changeNotification(_ notification: Notification) {
    guard let userInfo = notification.userInfo else { return }

    if let inserts = userInfo[NSInsertedObjectsKey] as? Set<NSManagedObject>, inserts.count > 0 {
        print("--- INSERTS ---")
        print(inserts)
        print("+++++++++++++++")
    }

    if let updates = userInfo[NSUpdatedObjectsKey] as? Set<NSManagedObject>, updates.count > 0 {
        print("--- UPDATES ---")
        for update in updates {
            print(update.changedValues())
        }
        print("+++++++++++++++")
    }

    if let deletes = userInfo[NSDeletedObjectsKey] as? Set<NSManagedObject>, deletes.count > 0 {
        print("--- DELETES ---")
        print(deletes)
        print("+++++++++++++++")
    }
}

NotificationCenter.default.addObserver(self, selector: #selector(self.changeNotification(_:)), name: .NSManagedObjectContextObjectsDidChange, object: moc)

22 Split String into words

Default ways of splitting ✂️ String don't work perfect sometimes, because of punctuation characters and other "wreckers" 🐛. Here is extension for splitting ✂️ String into words 💻🧐👌.

extension String {
    var words: [String] {
        return components(separatedBy: .punctuationCharacters)
            .joined()
            .components(separatedBy: .whitespaces)
            .filter{!$0.isEmpty}
    }
}

21 Comparing tuples

I discovered strange behavior of tuples during comparing 🤪. Comparison cares only about types and ignores labels 😦. So result can be unexpected. Be careful ⚠️.

let car = (model: "Tesla", producer: "USA")
let company = (name: "Tesla", country: "USA")
if car == company {
    print("Equal")
} else {
    print("Not equal")
}

Printed result will be: Equal

20 How to detect that user stop typing

Painless way ( NO to timers from now ⛔️ ) how to detect that user stop typing text in text field ⌨️ Could be usefull for lifetime search 🔍

class TestViewController: UIViewController {

    @objc func searchBarDidEndTyping(_ textField: UISearchBar) {
        print("User finsihed typing text in search bar")
    }

    @objc func textFieldDidEndTyping(_ textField: UITextField) {
        print("User finished typing text in text field")
    }
}

extension TestViewController: UISearchBarDelegate {
    func searchBar(_ searchBar: UISearchBar, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
        NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(searchBarDidEndTyping), object: searchBar)
        self.perform(#selector(searchBarDidEndTyping), with: searchBar, afterDelay: 0.5)
        return true
    }
}

extension TestViewController: UITextFieldDelegate {
    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(textFieldDidEndTyping), object: textField)
        self.perform(#selector(textFieldDidEndTyping), with: textField, afterDelay: 0.5)
        return true
    }
}

19 Left/rigth text offset inside UITextField

Clear way of adding left\right text offset inside UItextField 🔨🧐💻 Also, because of @IBInspectable it could be easily editable in Interface Builder’s inspector panel.

@IBDesignable
extension UITextField {

    @IBInspectable var leftPaddingWidth: CGFloat {
        get {
            return leftView!.frame.size.width
        }
        set {
            let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: newValue, height: frame.size.height))
            leftView = paddingView
            leftViewMode = .always
        }
    }

    @IBInspectable var rigthPaddingWidth: CGFloat {
        get {
            return rightView!.frame.size.width
        }
        set {
            let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: newValue, height: frame.size.height))
            rightView = paddingView
            rightViewMode = .always
        }
    }
}

18 Common elements in two arrays

I'm not huge fan of custom operators 😐 because they are intuitively obvious only to their authors, but I've created one which gives you opportunity to get common elements in two arrays whos elements implement Equatable protocol 🔨🧐💻

infix operator &
func  &<T : Equatable>(lhs: [T], rhs: [T]) -> [T] {
    return lhs.filter { rhs.contains($0) }
}

17 Apply gradient to Navigation Bar

Gradient 🏳️‍🌈 on Navigation Bar is really good looking, but not very easy to implement 🧐🔨👨‍💻 Works with iOS 11 largeTitle navigation bar too 👌

struct GradientComponents {
    var colors: [CGColor]
    var locations: [NSNumber]
    var startPoint: CGPoint
    var endPoint: CGPoint
}

extension UINavigationBar {

    func applyNavigationBarGradient(with components: GradientComponents) {

        let size = CGSize(width: UIScreen.main.bounds.size.width, height: 1)
        let gradient = CAGradientLayer()
        gradient.frame = CGRect(x: 0, y: 0, width: size.width, height: size.height)

        gradient.colors = components.colors
        gradient.locations = components.locations
        gradient.startPoint = components.startPoint
        gradient.endPoint = components.endPoint

        UIGraphicsBeginImageContext(gradient.bounds.size)
        gradient.render(in: UIGraphicsGetCurrentContext()!)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        self.barTintColor = UIColor(patternImage: image!)
    }
}

16 Get next element of array

Easy way how to get next element of array

extension Array where Element: Hashable {
    func after(item: Element) -> Element? {
        if let index = self.index(of: item), index + 1 < self.count {
            return self[index + 1]
        }
        return nil
    }
}

15 Split array by chunks of given size

Great extension to split array by chunks of given size

extension Array {
    func chunk(_ chunkSize: Int) -> [[Element]] {
        return stride(from: 0, to: self.count, by: chunkSize).map({ (startIndex) -> [Element] in
            let endIndex = (startIndex.advanced(by: chunkSize) > self.count) ? self.count-startIndex : chunkSize
            return Array(self[startIndex..<startIndex.advanced(by: endIndex)])
        })
    }
}

14 Transparent/Opaque Navigation Bar

Scene with UIImageView on top looks stylish if navigation bar is transparent. Easy way how to make navigation bar transparent or opaque.

func transparentNavigationBar() {
    self.setBackgroundImage(UIImage(), for: .default)
    self.shadowImage = UIImage()
}

func opaqueNavigationBar() {
    self.shadowImage = nil
    self.setBackgroundImage(nil, for: .default)
}

13 Group objects by property

One more useful extension 🔨💻 Gives you opportunity to group objects by property 👨‍💻🧐

extension Sequence {
    func group<GroupingType: Hashable>(by key: (Iterator.Element) -> GroupingType) -> [[Iterator.Element]] {
        var groups: [GroupingType: [Iterator.Element]] = [:]
        var groupsOrder: [GroupingType] = []
        forEach { element in
            let key = key(element)
            if case nil = groups[key]?.append(element) {
                groups[key] = [element]
                groupsOrder.append(key)
            }
        }
        return groupsOrder.map { groups[$0]! }
    }
}

Usage:

struct Person {
    var name: String
    var age: Int
}

let mike = Person(name: "Mike", age: 18)
let john = Person(name: "John", age: 18)
let bob = Person(name: "Bob", age: 56)
let jake = Person(name: "Jake", age: 56)
let roman = Person(name: "Roman", age: 25)

let persons = [mike, john, bob, jake, roman]

let groupedPersons = persons.group { $0.age }

for persons in groupedPersons {
    print(persons.map { $0.name })
}

Result:

["Mike", "John"]
["Bob", "Jake"]
["Roman"]

Also in-box alternative

12 Semicolons in Swift

Do you need semicolons in Swift ? Short answer is NO, but you can use them and it will give you interesting opportunity. Semicolons enable you to join related components into a single line.

func sum(a: Int, b: Int) -> Int {
    let sum = a + b; return sum
}

11 Fake AppDelegate

Unit testing shouldn’t have any side effects. While running tests, Xcode firstly launches app and thus having the side effect of executing any code we may have in our App Delegate and initial View Controller. Fake AppDelegate in your main.swift to prevent it.

You can find main.swift file here

10 Invoke didSet when property’s value is set inside init context

Apple's docs specify that: "Property observers are only called when the property’s value is set outside of initialization context."

defer can change situation 😊

class AA {
    var propertyAA: String! {
        didSet {
            print("Function: \(#function)")
        }
    }

    init(propertyAA: String) {
        self.propertyAA = propertyAA
    }
}

class BB {
    var propertyBB: String! {
        didSet {
            print("Function: \(#function)")
        }
    }

    init(propertyBB: String) {
        defer {
            self.propertyBB = propertyBB
        }
    }
}

let aa = AA(propertyAA: "aa")
let bb = BB(propertyBB: "bb")

Result:

Function: propertyBB

9 Change type of items in array

Two ways of changing type of items in array and obvious difference between them 🧐👨‍💻

let numbers = ["1", "2", "3", "4", "notInt"]
let mapNumbers = numbers.map { Int($0) }  // [Optional(1), Optional(2), Optional(3), Optional(4), nil]
let compactNumbers = numbers.compactMap { Int($0) } // [1, 2, 3, 4]

8 forEach and map execution order difference

Execution order is interesting difference between forEach and map: forEach is guaranteed to go through array elements in its sequence, while map is free to go in any order.

7 Testing settings

  1. Even if you don't write UI Tests, they still take considerable amount of time to run. Just skip it.
  2. Enable code coverage stats in Xcode, it helps to find which method was tested, not tested, partly tested. But don’t pay too much attention to the percentage 😊.

6 Tips for writing error messages

  1. Say what happened and why
  2. Suggest a next step
  3. Find the right tone (If it’s a stressful or serious issue, then a silly tone would be inappropriate)

Common​ ​Types​ ​of​ ​Error​ ​Messages

5 Profit to compiler

Do you know that using map gives profit to the compiler: it's now clear we want to apply some code to every item in an array, then like in for loop we could have break on halfway through.

4 Combinations of pure functions

compactMap func is effectively the combination of using map and joined in a single call, in that order. It maps items in array A into array B using a func you provide, then joins the results using concatenation.

Functions min and max could be also combinations of sorted.first and sorted.last in single call.

let colors = ["red", "blue", "black", "white"]

let min = colors.min() // black
let first = colors.sorted().first // black

let max = colors.max() // white
let last = colors.sorted().last // white

3 Enumerated iteration

Use enumerated when you iterate over the collection to return a sequence of pairs (n, c), where n - index for each element and c - its value 👨‍💻💻

for (n, c) in "Swift".enumerated() {
    print("\(n): \(c)")
}

Result:

0: S
1: w
2: i
3: f
4: t

Also be careful with this tricky thing, enumerated on collection will not provide actual indices, but monotonically increasing integer, which happens to be the same as the index for Array but not for anything else, especially slices.

2 Easy way to hide Status Bar

Ever faced the problem that u can't hide status bar because of prefersStatusBarHidden is get-only? The simplest solution is to override it 🧐👨‍💻

let vc = UIViewController()
vc.prefersStatusBarHidden = true // error
print("statusBarHidded \(vc.prefersStatusBarHidden)") // false

class TestViewController: UIViewController {
    override var prefersStatusBarHidden: Bool {
        return true
    }
}

let testVC = TestViewController()
print("statusBarHidded \(testVC.prefersStatusBarHidden)") // true

1 Safe way to return element at specified index

You can extend collections to return the element at the specified index if it is within bounds, otherwise nil.

extension Collection {
    subscript (safe index: Index) -> Element? {
        return indices.contains(index) ? self[index] : nil
    }
}

let cars = ["Lexus", "Ford", "Volvo", "Toyota", "Opel"]
let selectedCar1 = cars[safe: 3] // Toyota
let selectedCar2 = cars[safe: 6] // not crash, but nil

Download Details:

Author: Luur
Source Code: https://github.com/Luur/SwiftEchoes-Tips 
License: MIT license

#swift #tips #tricks #iosdev 

What is GEEK

Buddha Community

SwiftEchoes-Tips: Swift tips and tricks
Rupert  Beatty

Rupert Beatty

1673078940

SwiftEchoes-Tips: Swift tips and tricks

🔥 SwiftEchoes - Tips

Here's list of tips and tricks with all additional sources (playgrounds, images) that I would like to share. Also you can find them on Twitter @szubyak, where you can ask questions and respond with feedback. I will really glad to have you there! 😀

63 How to make UIStoryboard usage safer

Below I want to show you a few simple ways how to make usage of string literals connected with UIStoryboard much more safer.

Global constant string literals
At first it sounds like a good idea. You only need to define the constant once and it will be available everywhere. Then if you want to change its value, there is only one place in code to change for which the effects will cascade throughout the project. But global constants have some disadvantages. Not as much as global variables but still enought to stop using them. It's not the best practice. I will cover the topic of andvatages and disadavntages of global constants/variables usage in my next posts.

Relatable storyboard names
Your storyboards should be named after the sections of which they cover. It's a general rule. If you have a storyboard which houses view controllers are related to Profile, then the name of that storyboard’s file should be Profile.storyboard.

Uniform storyboard identifiers
When you want to use Storyboard Identifiers on your view controllers, usage of the class names as identifiers will be a good practice. For example, “ProfileViewController” would be the identifier for ProfuleViewController. Adding this to your naming convention is a good idea.

Enum
Try to consider enums as uniform, centralized global string literal identifiers for 'UIStoryboard'. You can create UIStoryboard class extension which defines all the storyboard files you have in your project. You can also add the convenience initializer or class function to add more syntactic sugar.

extension UIStoryboard {
    enum Storyboard: String {
        case profile
        case login
        
        var name: String {
            return rawValue.capitalized
        }
    }
    
    convenience init(storyboard: Storyboard, bundle: Bundle? = nil) {
        self.init(name: storyboard.name, bundle: bundle)
    }
    // let storyboard = UIStoryboard(storyboard: .profile)
    
    class func storyboard(storyboard: Storyboard, bundle: Bundle? = nil) -> UIStoryboard {
        return UIStoryboard(name: storyboard.name, bundle: bundle)
    }
    // let storyboard = UIStoryboard.storyboard(.profile)
}

I hope provided solutions will really help you to maintain your storyboards.

62 "Massive" Storyboard

I'm prety sure that almost every one has heard about "main" MVC problem known as Massive View Controller and how fashionable architectures (MVVM, VIPER, CleanArchitecture) valiantly fight it. But I want to pay attation to not less common problem - massive storyboard. Because of this many developers prefer to make layouts in code insted of storyboard. I've to admit that developers despise storyboards not without reason.

Let's list main storyboard problems:

  • Storyboard always become unwieldy and unmanageable.
  • Storyboard is slow. Extremely slow. As it grows in size, it doesn't only become unhandy for developers but also for XCode. Once you tap on a storyboard file of this size, you can leave your seat, grab a cup of coffee and come back. You’ll be lucky if XCode has loaded the storyboard.
  • Merge Conflicts in Source Control. It’s always a painful experience when multiple developers are working on a project as it almost always leads to merge conflicts in storyboard. Merging storyboards is hell because of their nature - storyboards are just hugeass XML files.

Inspite of the issues, I'm convinced there is a way to make storyboard great again 🇺🇸, resolving most of the above.

"A storyboard is meant to explain a story, not a saga."

Storyboard can be easily splitted into multiple storyboards, with each one representing an individual story. Yes, it's OK not to have one big, fat, slowloading storyboard file but have 30+ devided storyboards.

Also don't forget about Storyboard References introduced in iOS 9.

But splitting storyboard will not help with one serious problem. Storyboard usage has strong coupling with one silent killer known as String Literals. Look for the solution in 63 StoryboardIdentifiable protocol

61 XCTUnwrap assertion function

In Xcode 11 new assertion function has been added for use in Swift tests. XCTUnwrap asserts that an Optional variable’s value is not nil, returning the unwrapped value of expression for subsequent use in the test. It protects you from dealing with conditional chaining for the rest of the test. Also it removes the need to use XCTAssertNotNil(_:_:file:line:) with either unwrapping the value. It’s common to unwrap optional before checking it for a particular value so that’s really where XCTUnwrap() will come into its own.


struct Note {
    var id: Int
    var title: String
    var body: String
}

class NotesViewModel {

    static func all() -> [Note] {
        return [
            Note(id: 0, title: "first_note_title", body: "first_note_body"),
            Note(id: 1, title: "second_note_title", body: "second_note_body"),
        ]
    }
}


func testGetFirstNote() throws {
    let notes = NotesViewModel.all()
    let firstNote =  try XCTUnwrap(notes.first)
    XCTAssertEqual(firstNote.title, "first_note_title")
}

This approach is cleaner than what we might have written previously:

func testGetFirstNote() {
    let notes = NotesViewModel.all()
    if let firstNote = notes.first {
        XCTAssertEqual(firstNote.title, "first_note_title")
    } else {
        XCTFail("Failed to get first note.")
    }
}

60 UITableViewCell identifier

To register or dequeue UITableViewCell object we need to provide its string type identifier. Typing string by hand is wasting time and could couse you typos. In this case I would recomend to use extension which declares static identifier property inside UITableViewCell to avoid these problems.

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

let cell = tableView.dequeueReusableCell(withIdentifier: ExampleTableViewCell.identifier)
tableView.register(ExampleTableViewCell.self, forCellReuseIdentifier: ExampleTableViewCell.identifier)

print(ExampleTableViewCell.identifier) //ExampleTableViewCell

59 AlertPresentable protocol

In my current project I work on I present alerts almost on every view controller. To reduce lines of codes and time spent on duplicate code I created AlertPresentable layer and want to share it with you. Any ViewController which implements AlertPresentable protocol receive opportunity to present any type of alerts discribed in this layer just by one line of code.

protocol AlertPresentable {
    func presentErrorAlert(with message: String)
    func presentSuccessAlert(with message: String, action: ((UIAlertAction) -> Void)?)
    func presentConfirmationAlert(with message: String, action: ((UIAlertAction) -> Void)?)
}

extension AlertPresentable where Self: UIViewController {

    func presentErrorAlert(with message: String) {
        presentAlert(message: message, actions: [UIAlertAction(title: "OK", style: .cancel, handler: nil)])
    }

    func presentSuccessAlert(with message: String, action: ((UIAlertAction) -> Void)?) {
        presentAlert(message: message, actions: [UIAlertAction(title: "OK", style: .cancel, handler: action)])
    }

    func presentConfirmationAlert(with message: String, action: ((UIAlertAction) -> Void)?) {
        presentAlert(message: message, actions: [UIAlertAction(title: "Yes", style: .default, handler: action), UIAlertAction(title: "No", style: .cancel, handler: nil)])
    }

    private func presentAlert(title: String? = "", message: String? = nil, actions: [UIAlertAction] = []) {
        let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
        actions.forEach { (action) in
            alertController.addAction(action)
        }
        present(alertController, animated: true, completion: nil)
    }
}

Usage:

class ViewController: UIViewController, AlertPresentable {

    override func viewDidLoad() {
        super.viewDidLoad()

        presentErrorAlert(with: "User not found")

        presentSuccessAlert(with: "File downloaded") { _ in
            // use downloaded file
        }

        presentConfirmationAlert(with: "Are you sure you would like to sign out?") { _ in
            // sign out user
        }
    }
}

58 CollectionView extension for adaptive grid layout

Implementation of grid CollectionView layout is a commont task. But I found that calculation of cell width when you dont know how many cells can fit in one row of CollectionView is not a common task.

Here is my extension for calculation width of cell in grid CollectionView to make your layot adaptive.

extension UICollectionView {

    func flexibleCellWidth(minCellWidth: CGFloat, minimumInteritemSpacing: CGFloat) -> CGFloat {
        let contentWidth = frame.size.width - contentInset.left - contentInset.right
        let numberOfItemsInRow = Int((contentWidth + minimumInteritemSpacing) / (minCellWidth + minimumInteritemSpacing))
        let spacesWidth = CGFloat(numberOfItemsInRow - 1) * minimumInteritemSpacing
        let availableContentWidth = contentWidth - spacesWidth
        return availableContentWidth / CGFloat(numberOfItemsInRow)
    }
}

Based on minimal cell width and minimal interitem spacing it autocalculates number of cells in row and return cell width for the best placement.

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    let cellWidth = collectionView.flexibleCellWidth(minCellWidth: 72, minimumInteritemSpacing: 10)
    return CGSize(width: cellWidth, height: cellWidth)
}

iPhoneSE

iPhoneX

57 Render HTML within a UILabel

You can render HTML strings within a UILabel using a special initializer of NSAttributedString and passing in NSAttributedString.DocumentType.html for .documentType. But in most cases it is not enough to display it as is. If we want to add custom font or color we need to use CSS (add CSS header to our HTML string).

extension String {

    func htmlAttributedString(with fontName: String, fontSize: Int, colorHex: String) -> NSAttributedString? {
        do {
            let cssPrefix = "<style>* { font-family: \(fontName); color: #\(colorHex); font-size: \(fontSize); }</style>"
            let html = cssPrefix + self
            guard let data = html.data(using: String.Encoding.utf8) else {  return nil }
            return try NSAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding: String.Encoding.utf8.rawValue], documentAttributes: nil)
        } catch {
            return nil
        }
    }
}

Usage example

56 Custom Error by adopting LocalizedError protocol

Custom errors are an integral parts of you work. Swift-defined error types can provide localized error descriptions by adopting the new LocalizedError protocol.

enum UserError: Error {
    case credentialsNotMatch
    case invalidEmail
    case invalidName
}

extension UserError: LocalizedError {
    public var errorDescription: String? {
        switch self {
            case .credentialsNotMatch:
                return NSLocalizedString("Your username and/or password do not match", comment: "Credentials do not match")
            case .invalidEmail:
                return NSLocalizedString("Please enter email address in format: yourname@example.com", comment: "Invalid email format")
            case .invalidName:
                return NSLocalizedString("Please enter you name", comment: "Name field is blank")
        }
    }
}

Example:

func validate(email: String?, password: String?) throws {
    throw UserError.credentialsNotMatch
}

do {
    try validate(email: "email", password: "password")
} catch UserError.credentialsNotMatch {
    print(UserError.credentialsNotMatch.localizedDescription)
}

You can provide even more information. It's common decency to show user except error description also failure reasons and recovery suggestions. Sorry for ambiguous error messages :)

enum ProfileError: Error {
    case invalidSettings
}

extension ProfileError: LocalizedError {
    public var errorDescription: String? {
        switch self {
            case .invalidSettings:
                return NSLocalizedString("Your profile settings are incorrect", comment: "")
        }
    }

    public var failureReason: String? {
        switch self {
            case .invalidSettings:
                return NSLocalizedString("I don't know why", comment: "")
        }
    }
    
    public var recoverySuggestion: String? {
        switch self {
            case .invalidSettings:
                return NSLocalizedString("Please provide correct profile settings", comment: "")
        }
    }
}

let error = ProfileError.invalidSettings
print(error.errorDescription)
print(error.failureReason)
print(error.recoverySuggestion)

55 Result type without value to provide

Result type usage is really popular nowadays.

enum Result<T> {
    case success(result: T)
    case failure(error: Error)
}

func login(with credentials: Credentials, handler: @escaping (_ result: Result<User>) -> Void) {
    // Two possible options:
    handler(Result.success(result: user))
    handler(Result.failure(error: UserError.notFound))
}

login(with:) operation has user value to provide and default Result type fits perfectly here. But let’s imagine that your operation hasn’t got value to provide or you don’t care about it. Default Result type makes you to provide the result value any way.

To fix this inconvenience you need to add extension and instantiate a generic with an associated value of type Void.

func login(with credentials: Credentials, handler: @escaping (_ result: Result<Void>) -> Void)

extension Result where T == Void {
    static var success: Result {
        return .success(result: ())
    }
}

Now we can change our func login(with:) a bit, to ignore result success value if we don’t care about it.

func login(with credentials: Credentials, handler: @escaping (_ result: Result<Void>) -> Void) {
    // Two possible options:
    handler(Result.success)
    handler(Result.failure(error: UserError.notFound))
}

54 Given, When, Then

In unit testing terms, there are three phases to a unit test:

  • Given prepares the preconditions for the test. These preconditions include arguments to the function call, states of the subject, and any test doubles that are used to inspect the test subject in subsequent assertions.
  • The When phase can be as simple as making the call to the function on the test subject. In the when section an operation to be tested is performed.
  • The Then phase verifies the actual results against your expected results. These are done with assertions statements. An assertion can check if something is true or false, nil or non-nil, two objects match or not.
func testSum() {
    // Given
    let firstValue = 4
    let secondValue = 3
    // When
    let sum = sut.sum(firstValue: firstValue, secondValue: secondValue)
    // Then
    XCTAssertEqual(sum, 7)
}

Note: When comparing two values using XCTAssertEqual you should always put the actual value first before the expected value.

53 sut and test lifecycle

The subject under test is always named sut . It refers to a subject that is being tested for correct operation. It is short for "whatever thing we are testing" and is always defined from the perspective of the test. So when you look at your tests, there is no ambiguity as to what is being tested. All other objects are named after their types. Clearly identifying the subject under test in the sut variable makes it stand out from the rest.

The test lifecycle for each test is surrounded by a pair of functions - setUp() and tearDown(). The setUp() function is called before the execution of each test function in the test class. The tearDown() function is called after the execution of each test function in the test class. They provide you a place to prepare the sut ready before the test starts, and clean up the sut and any states after the test finishes. It’s important that each test starts with the desired initial state.

import XCTest

class YourClassTests: XCTestCase {

    // MARK: - Subject under test
    
    var sut: YourClass!
    
    // MARK: - Test lifecycle
    
    override func setUp() {
        super.setUp()
        sut = YourClass()
    }
    
    override func tearDown() {
        sut = nil
        super.tearDown()
    }
}

52 Point on circle perimeter

Could be useful solution for positioning nodes in your game or views in ordinary business application on the perimeter of circle.

func pointOnCircle(radius: Float, center: CGPoint) -> CGPoint {
    // Random angle between 0 and 2*pi
    let theta = Float(arc4random_uniform(UInt32.max)) / Float(UInt32.max-1) * .pi * 2.0
    // Convert polar to cartesian
    let x = radius * cos(theta)
    let y = radius * sin(theta)
    return CGPoint(x: CGFloat(x) + center.x, y: CGFloat(y) + center.y)
}

51 zip() function

Let’s imagine you have an array of LOTR heroes, and an array of weapons that match those heros. How could I create a list with the heros and weapons combined at each index? zip() function is designed to combine two sequences into a single sequence of tuples.

Note: I would recomend to wrap the output from zip() into array to make its output easier to read.

One of the useful features of zip() is that if your two arrays arent equal in size it will choose the shorter one. Yes, it will not crash your application, just ignore element which doesn’t have a match in second sequence.

50 StackView custom spacing

StackView, introduced in iOS 9, made Auto Layout usage much easier, because of reducing the amount of constraints needed to create manually for common layouts. But we faced with the problem, property spacing applies equally to the arranged subviews of StackView. Before iOS 11, there were 2 ways how to overcome this problem. One way is to create views with desired height and use them as spacer views or we could do it by nesting stack views but these two ways always seemed to be an unnecessary complication.

In iOS 11 you can create stack views with custom spacing between views. But there is no way to specify this custom spacing in Storyboard, only in code.

stackView.setCustomSpacing(32.0, after: label)

Also, in iOS 11 Apple introduced default and system spacing properties on the UIStackView class. You can use these to set or reset the custom spacing after a view.

stackview.setCustomSpacing(UIStackView.spacingUseSystem, after: label)

49 Named UIColor

XCode 9.0 gives us opportunity to create named colors. We can do it directly inside assets catalog and use it in code and storyboards. Named colors contain 3 parts: name ("FerrariRed"), color specified as range and device compatibility.

Created named color you can use like this:

view.backgroundColor = UIColor(named: "FerrariRed")

Inside storyboards you can use created named color by selecting it from the color dropdown menu.

48 Result error handling

Error handling is realy common functionality. But if you want to handle errors across asynchronous boundaries or store value/error results for later processing, then ordinary Swift error handling won’t help. The best alternative is a common pattern called a Result type: it stores a value/error “sum” type (either one or the other) and can be used between any two arbitrary execution contexts.

Example of Result type:

enum Result<T> {
    case success(result: T)
    case failure(error: String)
}

Usage of Result type:

protocol TaskStoreProtocol {
    func fetchTasks(handler: @escaping TaskStoreFetchTasksResult)
}

typealias TaskStoreFetchTasksResult = (_ result: Result<[Task]>) -> Void

class TasksStore: TasksStoreProtocol {
    
    func fetchTasks(handler: @escaping TaskStoreFetchTasksResult) {
        // Some useful code, network request or something like this goes here
        if success {
            handler(Result.success(result: fetchedTasks))
        } else {
            handler(Result.failure(error: error))
        }
    }
}

47 Generics: Type parameters

Here I want to introduce you with general info about generic type parameters and their naming style. Type parameters specify and name a placeholder type, and are written immediately after the function’s name, between a pair of angle brackets (such as <T>). Once you specify a type parameter, you can use it to define the type of a function’s parameters, or as the function’s return type, or as a type annotation within the body of the function. In each case, the type parameter is replaced with an actual type whenever the function is called.

You can provide more than one type parameter by writing multiple names within the angle brackets, separated by commas.

In most cases, type parameters have descriptive names, such as Key and Value in Dictionary<Key, Value> and Element in Array<Element>, which tells about the relationship between the type parameter and the generic type or function it’s used in. However, when there isn’t a meaningful relationship between them, it’s traditional to name them using single letters such as T, U, and V.

46 Generics: Basics

You can find usage of generics in several of my tips. So here I want to show you some basics of generics. They are one of the most powerful features of Swift. It gives you opportinity to write flexible, reusable functions. Types that can work with any type, subject to requirements that you define. You can write code that avoids duplication and expresses its intent in a clear, abstracted way.

At first, here is nongeneric function which swaps two Int values:

func swapTwoInts(_ a: inout Int, _ b: inout Int) {
    let temporaryA = a
    a = b
    b = temporaryA
}

As you can see, this func is useful but a bit limited, because you can swap only Int values. If you want to swap two String values, or two Double values, you have to write more functions.

Generic functions can work with any type. Here’s a generic representation of our function.

func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
    let temporaryA = a
    a = b
    b = temporaryA
}

The body of our two functions is the same. But the first line of is slightly different. The generic version of the function uses a placeholder type name (called T) instead of an actual type name (such as Int, String, or Double). The placeholder type name doesn’t say anything about what T must be, but it does say that both a and b must be of the same type T. The actual type to use in place of T is determined each time the function is called.

45 UserDefaults during testing

As with all dependencies, we should reduce UserDefaults to an abstract protocol, inject it into the classes that require it, and thereby decouple ourselves from implementation details and increase the testability of our code by introducing seams. But I'll be honest, the UserDefaults API is so simple and pervasive that standard dependency management feels like overkill. If so, we've probably encountered the same problem: without the ability to inject mocks, testing code that makes use of UserDefaults can be a pain. Any time we use our test device (including running tests on it!) we potentially change the state of its persisted settings.

Here is extension UserDefaults that gives your test method an opportunity to run in clean state and not jinx real persisted settings.

extension UserDefaults {
    static func blankDefaultsWhile(handler:()->Void){
        guard let name = Bundle.main.bundleIdentifier else {
            fatalError("Couldn't find bundle ID.")
        }
        let old = UserDefaults.standard.persistentDomain(forName: name)
        defer {
            UserDefaults.standard.setPersistentDomain( old ?? [:], forName: name)
        }
        UserDefaults.standard.removePersistentDomain(forName: name)
        handler()
    }
}

Usage:

class MyTests: XCTestCase {
    func testSomething() {
        // Defaults can be full of junk.
        UserDefaults.blankDefaultsWhile {
            // Some tests that expect clean defaults.
            // They can also scribble all over defaults with test-specific values.
        }
        // Defaults are back to their pre-test state.
    }
}

44 Additional Info to #38 Protocols: Optional methods

Here is one more trick how to make protocol method to be optional. Let’s remember why we would like to declare a protocol method as optional? It is because we we don’t want to write the implementation if that method will not be used. So Swift has a feature called extension that allow us to provide a default implementation for those methods that we want to be optional.

protocol CarEngineStatusDelegate {
    func engineWillStop()
    func engineDidStop()
}

extension CarEngineStatusDelegate {
    func engineWillStop() {}
}

This way the class/struct that will use our protocol will only need to implement func engineDidStop().

43 Responsible view controller for particular view

How to find the view controller that is responsible for a particular view? This is as easy as walking the responder chain looking for the first UIViewController you find.

extension UIView {
    func findViewController() -> UIViewController? {
        if let nextResponder = self.next as? UIViewController {
            return nextResponder
        } else if let nextResponder = self.next as? UIView {
            return nextResponder.findViewController()
        } else {
            return nil 
        }
    }
}

You should only use it when you really need it – if you’re able to call methods directly using a delegate or indirectly by posting notifications then do it in first place.

42 Move between textfields

Lets imagine full of texfields scene (e.g. UserProfile scene). Moving between textfields using Next/Return on on-screen keyboard is integral functionality.

func textFieldShouldReturn(_ textField: UITextField) -> Bool {
    let nextTag = textField.tag + 1
    if let nextResponder = textField.superview?.viewWithTag(nextTag) {
        nextResponder.becomeFirstResponder()
    } else {
        textField.resignFirstResponder()
    }
    return true 
}

If you need to force the first responder to resign itself and aren’t sure which textfield is in control, it’s easier to use view.endEditing(true).

41 Autogenerated allCases property for your enum (Swift 4.2)

Retrieving all cases of enum is really common task, but it's implementation is not so clean. We were forced to create static variable of array with all cases of our enum manually.

enum Cars {
    case BMW
    case Audi
    case Volkswagen
    case Mercedes

    static let allCases = [BMW, Audi, Volkswagen, Mercedes]
}

In Swift 4.2 we got really useful property allCases, which is autogenerated (all you need to do is make your enum conform to the CaseIterable protocol), so it releases us from additional steps.

enum Cars: CaseIterable {
    case BMW
    case Audi
    case Volkswagen
    case Mercedes
}

for car in Cars.allCases {
    print(car)
}

40 Protocols: Class-only
 

One more thing left to say about protocols. Sometimes protocols should be adopted only by classes. To achieve this behaviour you should define you protocol with class keyword or by inheriting from AnyObject. This is commonly happens because you have a delegate property that needs to use weak storage to avoid the risk of a strong reference cycle (formerly known as a retain cycle).

weak var delegate: CarDelegate?

The weak keyword can't be used with structs and enums, because they are value types.

39 Protocols: Inheritance and composition
 

In previous tip you can observe how to split your protocol to smaller pieces. Here I will show you how to combine your protocols to make bigger one using inheritance and composition. To merge protocols together use & for protocol composition.

typealias Codable = Decodable & Encodable

You can also use protocol inheritance to build larger protocols.

protocol CarEngineStatusDelegate { }
protocol CarMovingStatusDelegate { }
protocol CarDelegate: CarMovingStatusDelegate, CarEngineStatusDelegate { }

38 Protocols: Optional methods
 

If you implement a protocol in Swift you must implement all its requirements. Optional methods are not allowed.

Example:

protocol CarDelegate {
    func engineDidStart()
    func carShouldStartMoving() -> Bool
    func carDidStartMoving()
    func engineWillStop()
    func engineDidStop()
}

But there are a few tricks how to make some methods to be optionals:

  • You can split them in two protocols, and adopt only needed one. ```swift protocol CarMovingStatusDelegate { func carShouldStartMoving() -> Bool func carDidStartMoving() }

protocol CarEngineStatusDelegate { func engineDidStart() func engineWillStop() func engineDidStop() }

* Use the `@objc` attribute on a Swift protocol to receive opportunity to mark methods as being optional, but you may no longer use Swift structs and enums with that protocol, and you may no longer use protocol extensions.
```swift
@objc protocol CarDelegate {
    @objc optional func engineDidStart()
    func carShouldStartMoving() -> Bool
    func carDidStartMoving()
    @objc optional func engineWillStop()
    @objc optional func engineDidStop()
}

Here is the optional method usage:

delegate?.engineWillStop?()

The delegate property is optional because there might not be a delegate assigned. And in the CarDelegate protocol we made engineWillStop() an optional requirement, so even if a delegate is present it might not implement that method. As a result, we need to use optional chaining.

37 Protocols: Naming
 

If we are talking about naming the protocol itself:

  • Protocols that describe what something is should read as nouns (e.g. Collection).
  • Protocols that describe a capability should be named using the suffixes able, ible, or ing (e.g. Equatable, ProgressReporting).

Briefly talking about methods names inside protocol, you can use whatever names you want. But, if your protocol is created to represent delegates please look: #32 Delegate naming

36 Property observers, getter/setter and lazy are mutually exclusive
 

Little thing that surprisingly became a discovery for me, because it's rare case in practice, so I'm not ashamed of it 🤣. Property observers, getter/setter and lazy cant be used together, they are mutually exclusive. So you should choose what pattern makes most profit to you in particaulr situation.

35 Prepare Alamofire standalone functions to unit-testing

Methods are functions associated with a type. Mock the type, and you can intercept the method. In Swift, we prefer to do this with protocols. But a standalone function lives on its own, without any associated data. In other words, it has no self. During unit-testing we want to intercept these calls, for two main reasons:

  • To spy on the arguments a function receives.
  • To stub any return values.

In popular networking 3party library Almofire all request, upload or download functions implemented as standalone functions. So, we have dificulties with intercepting during unit testing. We can't mock associated type, because there is no type. Also responseJSON function is implemented in extension, so we can't make any changes or override it.

I defined a new class with 2 methods (request and responseJSON) with the same method signature as Alamofire's request and responseJSON methods. I called the Alamofire version of these methods inside. And then used this class as a dependency in my sut.

import Alamofire

class AlamofireWrapper {
    @discardableResult
    public func request(_ url: URLConvertible, method: HTTPMethod = .get, parameters: Parameters? = nil, encoding: ParameterEncoding = URLEncoding.default, headers: HTTPHeaders? = nil) -> DataRequest {
        return Alamofire.request(url, method: method, parameters: parameters, encoding: encoding, headers: headers)
    }

    public func responseJSON(request: DataRequest, queue: DispatchQueue? = nil, options: JSONSerialization.ReadingOptions = .allowFragments, completionHandler: @escaping (DataResponse<Any>) -> Void) {
        request.responseJSON(queue: queue, options: options, completionHandler: completionHandler)
    }
}

My request part starts looking like this:

var alamofireWrapper = AlamofireWrapper()

let request = alamofireWrapper.request(url, parameters: params, encoding: URLEncoding(destination: .queryString))
alamofireWrapper.responseJSON(request: request) { response in
    if response.result.isSuccess {
        success(response.result.value)
    } else if let error = response.result.error {
        failure(error)
    }
}

And here is my test double finaly:

// MARK: - Test doubles

class AlamofireWrapperSpy: AlamofireWrapper {

    enum RequestStatus {
        case success
        case failure
    }

    var status: RequestStatus = .success

    // MARK: Method call expectations

    var requestCalled = false
    var responseJSONCalled = false

    // MARK: Spied methods

    override func request(_ url: URLConvertible, method: HTTPMethod, parameters: Parameters?, encoding: ParameterEncoding, headers: HTTPHeaders?) -> DataRequest {
        requestCalled = true
        return  SessionManager.default.request("dummy_url")
    }

    override func responseJSON(request: DataRequest, queue: DispatchQueue?, options: JSONSerialization.ReadingOptions, completionHandler: @escaping (DataResponse<Any>) -> Void) {
        responseJSONCalled = true
        switch status {
        case .success:
            let value = ["result": "best_response_ever"]
            completionHandler(DataResponse(request: nil, response: nil, data: nil, result: Result.success(value)))
        case .failure:
            let error = NSError(domain: "", code: 500, userInfo: nil)
            completionHandler(DataResponse(request: nil, response: nil, data: nil, result: Result.failure(error)))
        }
    }
}

34 Sort array of objects with multiple optional criteria

Few days ago I faced a sorting task. I needed to sort array of object by more then one criteria that is also optional. My object was Place with rating and distance properties. rating was main criteria for sorting and distance was secondary. So, here is my workouts.


struct Place {
    var rating: Int?
    var distance: Double?
}

func sortPlacesByRatingAndDistance(_ places: [Place]) -> [Place] {
    return places.sorted { t1, t2 in
        if t1.rating == t2.rating {
            guard let distance1 = t1.distance, let distance2 = t2.distance else {
                return false
            }
            return distance1 < distance2
        }
        guard let rating1 = t1.rating, let rating2 = t2.rating else {
            return false
        }
        return rating1 > rating2
    }
}

let places = [Place(rating: 3, distance: 127), Place(rating: 4, distance: 423), Place(rating: 5, distance: nil), Place(rating: nil, distance: 100), Place(rating: nil, distance: 34), Place(rating: nil, distance: nil)]

let sortedPlaces = sortPlacesByRatingAndDistance(places) // [{rating 5, nil}, {rating 4, distance 423}, {rating 3, distance 127}, {nil, distance 34}, {nil, distance 100}, {nil, nil}]

33 Remove object from array

Super lightweight extension 🎈. How to remove particular object from array. Remove first collection element that is equal to the given object.

extension Array where Element: Equatable {
    mutating func remove(_ object: Element) {
        if let index = index(of: object) {
            remove(at: index)
        }
    }
}

32 Delegate naming

When creating custom delegate methods take into consideration the following list items:

  • You can give the name to your delegate property delegate, if it is just one of them, or put delegate at the end, when it is more than one. Here is the example, WKWebView has uiDelegate and navigationDelegate properties that can point to two different objects. 

  • You should use Apple’s standard approach to avoid verb conjugation. It says that many of your method names will use will, did, and should.
  • Start the name by identifying the class of the object that’s sending the message func tableView(_ tableView: UITableView,.... It helps in situations where there is more than one object that can send a message .

As a result, Apple delegates have a very specific naming convention:

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)

31 Run, Playground, run!

Asynchronous work in Playground.

Tell the playground it should continue running forever, otherwise it will terminate before the asynchronous work has time to hppen.

PlaygroundPage.current.needsIndefiniteExecution = true

30 DispatchGroup usage

Let’s say you’ve got several long running tasks to perform. After all of them have finished, you’d like to run some further logic. You could run each task in a sequential fashion, but that isn’t so efficient - you’d really like the former tasks to run concurrently. DispatchGroup enables you to do exactly this.

let dispatchGroup = DispatchGroup()

for i in 1...5 {
    dispatchGroup.enter()
    Alamofire.request(url, parameters: params).responseJSON { response in
        //work with response
        dispatchGroup.leave() 
    }
}

dispatchGroup.notify(queue: .main) {
    print("All requests complete")
}

In the above, all long running functions will perform concurrently, followed by the print statement, which will execute on the main thread.

Each call to enter() must be matched later on with a call to leave(), after which the group will call the closure provided to notify().

DispatchGroup has a few other tricks:

  • Instead of notify(), we can call wait(). This blocks the current thread until the group’s tasks have completed.
  • A further option is wait(timeout:). This blocks the current thread, but after the timeout specified, continues anyway. To create a timeout object of type DispatchTime, the syntax .now() + 1 will create a timeout one second from now.
  • wait(timeout:) returns an enum that can be used to determine whether the group completed, or timed out.

29 Remove duplicates

Clear way 🛣️ to return the unique list of objects based on a given key 🔑. It has the advantage of not requiring the Hashable and being able to return an unique list based on any field or combination.

extension Array {
    func unique<T:Hashable>(map: ((Element) -> (T)))  -> [Element] {
        var set: Set<T> = []
        var arrayOrdered: [Element] = []
        for value in self {
            if !set.contains(map(value)) {
                set.insert(map(value))
                arrayOrdered.append(value)
            }   
        }
        return arrayOrdered
    }
}

28 Debugging: View Debugging

Using View Debugging you’re now able to inspect an entire view hierarchy visually – right from within Xcode, instead of printing frames to the console and trying to visualize layouts in your head.

You can invoke the view debugger by choosing View UI Hierarchy from the process view options menu in the debug navigator, or by choosing Debug > View Debugging > Capture View Hierarchy.

You'll see a 3D representation of your view, which means you can look behind the layers to see what else is there. The hierarchy automatically puts some depth between each of its views, so they appear to pop off the canvas as you rotate them. If you have a complicated view layout, View Debugging > Show View Frames option will draw lines around all your views so you can see exactly where they are.

More about view debugging here

27 Debugging: Breakpoints

A breakpoint is a debugging tool that allows you to pause the execution of your program up to a certain moment. Creating pause points in your code can help you investigate your code. While your app is paused, light green arrow that shows your current execution position can be moved. Just click and drag it somewhere else to have execution pick up from there – although Xcode will warn you that it might have unexpected results, so tread carefully!

Right-click on the breakpoint (the blue arrow marker) and choose Edit Breakpoint.

In the popup that appears you can set the condition. Execution will now pause only when your condition is true. You can use conditional breakpoints to execute debugger commands automatically – the Automatically continue checkbox is perfect for making your program continue uninterrupted while breakpoints silently trigger actions. Also you can set Ignore times before stoping and actions like Debuger command, Log message, Sound, etc.

Shortcuts:

F6- Step Over. Ctrl+Cmd+Y - Continue (continue executing my program until you hit another breakpoint)

In Xcode debug console you can use po to print what you need during pause.

 

26 Debugging: Asserts

assert() is debug-only check that will force your app to crash if specific condition is false.

assert(4 == 4, "Maths error") //OK
assert(3 == 2, "Maths error") //Crash

As you can see assert() takes two parameters:

  • Something to check.
  • Message to print out of the check fails.

If the check evaluates to false, your app will be forced to crash because you know it's not in a safe state, and you'll see the error message in the debug console. If you don’t have a condition to evaluate, or don’t need to evaluate one, you can use assertionFailure() function.

precondition() is not debug-only check. It will crash your app even in release mode.

precondition(4 == 4, "Maths error") //OK
precondition(3 == 2, "Maths error") //Crash

preconditionFailure() works the same as assertionFailure(). With the same difference as above, it works for release builds.

fatalError(), like assertionFailure() and preconditionFailure() works for all optimisation levels in all build configurations.

fatalError("ERROR")

More about asserts and optimisation levels you can find here

25 Debugging: Log functions

Debuging 👨‍🔧 is one of the most importent aspects of programing👨‍💻. It should be in your skillbox anyway. It contains log functions, asserts, breakpoints and view debuging. Let's observe everyone of them just one by one. And here are log functions in the crosshairs 🔍.

We know print() as a variadic function. Function that accepts any number of parameters.

print("one", "two", "three", "four") //one two three four

But it's variadic nature becomes much more useful when you use separator and terminator, its optional extra parameters. separator gives you opportunity to provide a string that should be placed between every item. It's "space" by default.

print("one", "two", "three", "four", separator: "-") //one-two-three-four

Meanwhile terminator is what should be placed after the last item. It’s \n by default, which means "line break".

print("one", "two", "three", "four", terminator: " five") //one two three four five

24 Update UIView content with animation

Really lightweight way 🎈 How to add content changing animation to UIView and it subclasses.

extension UIView {
    func fadeTransition(_ duration: CFTimeInterval) {
        let animation = CATransition()
        animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
        animation.type = kCATransitionFade
        animation.duration = duration
        layer.add(animation, forKey: kCATransitionFade)
    }
}

Just invoke 🧙‍♂️ fadeTransition(_ duration: CFTimeInterval) by your view before you will apply a change.

label.fadeTransition(1)
label.text = "Updated test content with animation"

23 Observe MOC changes

Next code snippet 📃 I use to keep eye on changes that take place in the managed object context. Useful thing to know what's going on, what was added, updated ( what specific values were changed ) or deleted 📥📝📤

func changeNotification(_ notification: Notification) {
    guard let userInfo = notification.userInfo else { return }

    if let inserts = userInfo[NSInsertedObjectsKey] as? Set<NSManagedObject>, inserts.count > 0 {
        print("--- INSERTS ---")
        print(inserts)
        print("+++++++++++++++")
    }

    if let updates = userInfo[NSUpdatedObjectsKey] as? Set<NSManagedObject>, updates.count > 0 {
        print("--- UPDATES ---")
        for update in updates {
            print(update.changedValues())
        }
        print("+++++++++++++++")
    }

    if let deletes = userInfo[NSDeletedObjectsKey] as? Set<NSManagedObject>, deletes.count > 0 {
        print("--- DELETES ---")
        print(deletes)
        print("+++++++++++++++")
    }
}

NotificationCenter.default.addObserver(self, selector: #selector(self.changeNotification(_:)), name: .NSManagedObjectContextObjectsDidChange, object: moc)

22 Split String into words

Default ways of splitting ✂️ String don't work perfect sometimes, because of punctuation characters and other "wreckers" 🐛. Here is extension for splitting ✂️ String into words 💻🧐👌.

extension String {
    var words: [String] {
        return components(separatedBy: .punctuationCharacters)
            .joined()
            .components(separatedBy: .whitespaces)
            .filter{!$0.isEmpty}
    }
}

21 Comparing tuples

I discovered strange behavior of tuples during comparing 🤪. Comparison cares only about types and ignores labels 😦. So result can be unexpected. Be careful ⚠️.

let car = (model: "Tesla", producer: "USA")
let company = (name: "Tesla", country: "USA")
if car == company {
    print("Equal")
} else {
    print("Not equal")
}

Printed result will be: Equal

20 How to detect that user stop typing

Painless way ( NO to timers from now ⛔️ ) how to detect that user stop typing text in text field ⌨️ Could be usefull for lifetime search 🔍

class TestViewController: UIViewController {

    @objc func searchBarDidEndTyping(_ textField: UISearchBar) {
        print("User finsihed typing text in search bar")
    }

    @objc func textFieldDidEndTyping(_ textField: UITextField) {
        print("User finished typing text in text field")
    }
}

extension TestViewController: UISearchBarDelegate {
    func searchBar(_ searchBar: UISearchBar, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
        NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(searchBarDidEndTyping), object: searchBar)
        self.perform(#selector(searchBarDidEndTyping), with: searchBar, afterDelay: 0.5)
        return true
    }
}

extension TestViewController: UITextFieldDelegate {
    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(textFieldDidEndTyping), object: textField)
        self.perform(#selector(textFieldDidEndTyping), with: textField, afterDelay: 0.5)
        return true
    }
}

19 Left/rigth text offset inside UITextField

Clear way of adding left\right text offset inside UItextField 🔨🧐💻 Also, because of @IBInspectable it could be easily editable in Interface Builder’s inspector panel.

@IBDesignable
extension UITextField {

    @IBInspectable var leftPaddingWidth: CGFloat {
        get {
            return leftView!.frame.size.width
        }
        set {
            let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: newValue, height: frame.size.height))
            leftView = paddingView
            leftViewMode = .always
        }
    }

    @IBInspectable var rigthPaddingWidth: CGFloat {
        get {
            return rightView!.frame.size.width
        }
        set {
            let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: newValue, height: frame.size.height))
            rightView = paddingView
            rightViewMode = .always
        }
    }
}

18 Common elements in two arrays

I'm not huge fan of custom operators 😐 because they are intuitively obvious only to their authors, but I've created one which gives you opportunity to get common elements in two arrays whos elements implement Equatable protocol 🔨🧐💻

infix operator &
func  &<T : Equatable>(lhs: [T], rhs: [T]) -> [T] {
    return lhs.filter { rhs.contains($0) }
}

17 Apply gradient to Navigation Bar

Gradient 🏳️‍🌈 on Navigation Bar is really good looking, but not very easy to implement 🧐🔨👨‍💻 Works with iOS 11 largeTitle navigation bar too 👌

struct GradientComponents {
    var colors: [CGColor]
    var locations: [NSNumber]
    var startPoint: CGPoint
    var endPoint: CGPoint
}

extension UINavigationBar {

    func applyNavigationBarGradient(with components: GradientComponents) {

        let size = CGSize(width: UIScreen.main.bounds.size.width, height: 1)
        let gradient = CAGradientLayer()
        gradient.frame = CGRect(x: 0, y: 0, width: size.width, height: size.height)

        gradient.colors = components.colors
        gradient.locations = components.locations
        gradient.startPoint = components.startPoint
        gradient.endPoint = components.endPoint

        UIGraphicsBeginImageContext(gradient.bounds.size)
        gradient.render(in: UIGraphicsGetCurrentContext()!)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        self.barTintColor = UIColor(patternImage: image!)
    }
}

16 Get next element of array

Easy way how to get next element of array

extension Array where Element: Hashable {
    func after(item: Element) -> Element? {
        if let index = self.index(of: item), index + 1 < self.count {
            return self[index + 1]
        }
        return nil
    }
}

15 Split array by chunks of given size

Great extension to split array by chunks of given size

extension Array {
    func chunk(_ chunkSize: Int) -> [[Element]] {
        return stride(from: 0, to: self.count, by: chunkSize).map({ (startIndex) -> [Element] in
            let endIndex = (startIndex.advanced(by: chunkSize) > self.count) ? self.count-startIndex : chunkSize
            return Array(self[startIndex..<startIndex.advanced(by: endIndex)])
        })
    }
}

14 Transparent/Opaque Navigation Bar

Scene with UIImageView on top looks stylish if navigation bar is transparent. Easy way how to make navigation bar transparent or opaque.

func transparentNavigationBar() {
    self.setBackgroundImage(UIImage(), for: .default)
    self.shadowImage = UIImage()
}

func opaqueNavigationBar() {
    self.shadowImage = nil
    self.setBackgroundImage(nil, for: .default)
}

13 Group objects by property

One more useful extension 🔨💻 Gives you opportunity to group objects by property 👨‍💻🧐

extension Sequence {
    func group<GroupingType: Hashable>(by key: (Iterator.Element) -> GroupingType) -> [[Iterator.Element]] {
        var groups: [GroupingType: [Iterator.Element]] = [:]
        var groupsOrder: [GroupingType] = []
        forEach { element in
            let key = key(element)
            if case nil = groups[key]?.append(element) {
                groups[key] = [element]
                groupsOrder.append(key)
            }
        }
        return groupsOrder.map { groups[$0]! }
    }
}

Usage:

struct Person {
    var name: String
    var age: Int
}

let mike = Person(name: "Mike", age: 18)
let john = Person(name: "John", age: 18)
let bob = Person(name: "Bob", age: 56)
let jake = Person(name: "Jake", age: 56)
let roman = Person(name: "Roman", age: 25)

let persons = [mike, john, bob, jake, roman]

let groupedPersons = persons.group { $0.age }

for persons in groupedPersons {
    print(persons.map { $0.name })
}

Result:

["Mike", "John"]
["Bob", "Jake"]
["Roman"]

Also in-box alternative

12 Semicolons in Swift

Do you need semicolons in Swift ? Short answer is NO, but you can use them and it will give you interesting opportunity. Semicolons enable you to join related components into a single line.

func sum(a: Int, b: Int) -> Int {
    let sum = a + b; return sum
}

11 Fake AppDelegate

Unit testing shouldn’t have any side effects. While running tests, Xcode firstly launches app and thus having the side effect of executing any code we may have in our App Delegate and initial View Controller. Fake AppDelegate in your main.swift to prevent it.

You can find main.swift file here

10 Invoke didSet when property’s value is set inside init context

Apple's docs specify that: "Property observers are only called when the property’s value is set outside of initialization context."

defer can change situation 😊

class AA {
    var propertyAA: String! {
        didSet {
            print("Function: \(#function)")
        }
    }

    init(propertyAA: String) {
        self.propertyAA = propertyAA
    }
}

class BB {
    var propertyBB: String! {
        didSet {
            print("Function: \(#function)")
        }
    }

    init(propertyBB: String) {
        defer {
            self.propertyBB = propertyBB
        }
    }
}

let aa = AA(propertyAA: "aa")
let bb = BB(propertyBB: "bb")

Result:

Function: propertyBB

9 Change type of items in array

Two ways of changing type of items in array and obvious difference between them 🧐👨‍💻

let numbers = ["1", "2", "3", "4", "notInt"]
let mapNumbers = numbers.map { Int($0) }  // [Optional(1), Optional(2), Optional(3), Optional(4), nil]
let compactNumbers = numbers.compactMap { Int($0) } // [1, 2, 3, 4]

8 forEach and map execution order difference

Execution order is interesting difference between forEach and map: forEach is guaranteed to go through array elements in its sequence, while map is free to go in any order.

7 Testing settings

  1. Even if you don't write UI Tests, they still take considerable amount of time to run. Just skip it.
  2. Enable code coverage stats in Xcode, it helps to find which method was tested, not tested, partly tested. But don’t pay too much attention to the percentage 😊.

6 Tips for writing error messages

  1. Say what happened and why
  2. Suggest a next step
  3. Find the right tone (If it’s a stressful or serious issue, then a silly tone would be inappropriate)

Common​ ​Types​ ​of​ ​Error​ ​Messages

5 Profit to compiler

Do you know that using map gives profit to the compiler: it's now clear we want to apply some code to every item in an array, then like in for loop we could have break on halfway through.

4 Combinations of pure functions

compactMap func is effectively the combination of using map and joined in a single call, in that order. It maps items in array A into array B using a func you provide, then joins the results using concatenation.

Functions min and max could be also combinations of sorted.first and sorted.last in single call.

let colors = ["red", "blue", "black", "white"]

let min = colors.min() // black
let first = colors.sorted().first // black

let max = colors.max() // white
let last = colors.sorted().last // white

3 Enumerated iteration

Use enumerated when you iterate over the collection to return a sequence of pairs (n, c), where n - index for each element and c - its value 👨‍💻💻

for (n, c) in "Swift".enumerated() {
    print("\(n): \(c)")
}

Result:

0: S
1: w
2: i
3: f
4: t

Also be careful with this tricky thing, enumerated on collection will not provide actual indices, but monotonically increasing integer, which happens to be the same as the index for Array but not for anything else, especially slices.

2 Easy way to hide Status Bar

Ever faced the problem that u can't hide status bar because of prefersStatusBarHidden is get-only? The simplest solution is to override it 🧐👨‍💻

let vc = UIViewController()
vc.prefersStatusBarHidden = true // error
print("statusBarHidded \(vc.prefersStatusBarHidden)") // false

class TestViewController: UIViewController {
    override var prefersStatusBarHidden: Bool {
        return true
    }
}

let testVC = TestViewController()
print("statusBarHidded \(testVC.prefersStatusBarHidden)") // true

1 Safe way to return element at specified index

You can extend collections to return the element at the specified index if it is within bounds, otherwise nil.

extension Collection {
    subscript (safe index: Index) -> Element? {
        return indices.contains(index) ? self[index] : nil
    }
}

let cars = ["Lexus", "Ford", "Volvo", "Toyota", "Opel"]
let selectedCar1 = cars[safe: 3] // Toyota
let selectedCar2 = cars[safe: 6] // not crash, but nil

Download Details:

Author: Luur
Source Code: https://github.com/Luur/SwiftEchoes-Tips 
License: MIT license

#swift #tips #tricks #iosdev 

Houston  Sipes

Houston Sipes

1600430400

10 Free Online Resources To Learn Swift Language

Swift is a fast and efficient general-purpose programming language that provides real-time feedback and can be seamlessly incorporated into existing Objective-C code. This is why developers are able to write safer, more reliable code while saving time. It aims to be the best language that can be used for various purposes ranging from systems programming to mobile as well as desktop apps and scaling up to cloud services.

Below here, we list down the 10 best online resources to learn Swift language.

(The list is in no particular order)

#developers corner #free online resources to learn swift language #learn swift #learn swift free #learn swift online free #resources to learn swift #swift language #swift programming

Top Swift Development Companies | Top Swift Developers - TopDevelopers.co

A thoroughly researched list of top Swift developers with ratings & reviews to help find the best Swift development companies around the world.

#swift development service providers #best swift development companies #top swift development companies #swift development solutions #top swift developers #swift

Hire Dedicated Swift Developers

Want to create a native iOS application for your Startup?

Hire Dedicated Swift Developers for end-to-end services like development, migration, upgrade, testing, and support & maintenance. Trust HourlyDeveloper.io our Swift development team for iOS device apps that are high on performance and security.

Consult with experts:- https://bit.ly/2C5M6cz

#hire dedicated swift developers #swift developers #swift development company #swift development services #swift development #swift

Rupert  Beatty

Rupert Beatty

1666245660

A Collection Of Swift Tips & Tricks That I've Shared on Twitter

Swift tips & tricks ⚡️

One of the things I really love about Swift is how I keep finding interesting ways to use it in various situations, and when I do - I usually share them on Twitter. Here's a collection of all the tips & tricks that I've shared so far. Each entry has a link to the original tweet, if you want to respond with some feedback or question, which is always super welcome! 🚀

⚠️ This list is no longer being updated. For my latest Swift tips, checkout the "Tips" section on Swift by Sundell.

Also make sure to check out all of my other Swift content:

102 Making async tests faster and more stable

🚀 Here are some quick tips to make async tests faster & more stable:

  • 😴 Avoid sleep() - use expectations instead
  • ⏱ Use generous timeouts to avoid flakiness on CI
  • 🧐 Put all assertions at the end of each test, not inside closures
// BEFORE:

class MentionDetectorTests: XCTestCase {
    func testDetectingMention() {
        let detector = MentionDetector()
        let string = "This test was written by @johnsundell."

        detector.detectMentions(in: string) { mentions in
            XCTAssertEqual(mentions, ["johnsundell"])
        }
        
        sleep(2)
    }
}

// AFTER:

class MentionDetectorTests: XCTestCase {
    func testDetectingMention() {
        let detector = MentionDetector()
        let string = "This test was written by @johnsundell."

        var mentions: [String]?
        let expectation = self.expectation(description: #function)

        detector.detectMentions(in: string) {
            mentions = $0
            expectation.fulfill()
        }

        waitForExpectations(timeout: 10)
        XCTAssertEqual(mentions, ["johnsundell"])
    }
}

For more on async testing, check out "Unit testing asynchronous Swift code".

101 Adding support for Apple Pencil double-taps

✍️ Adding support for the new Apple Pencil double-tap feature is super easy! All you have to do is to create a UIPencilInteraction, add it to a view, and implement one delegate method. Hopefully all pencil-compatible apps will soon adopt this.

let interaction = UIPencilInteraction()
interaction.delegate = self
view.addInteraction(interaction)

extension ViewController: UIPencilInteractionDelegate {
    func pencilInteractionDidTap(_ interaction: UIPencilInteraction) {
        // Handle pencil double-tap
    }
}

For more on using this and other iPad Pro features, check out "Building iPad Pro features in Swift".

100 Combining values with functions

😎 Here's a cool function that combines a value with a function to return a closure that captures that value, so that it can be called without any arguments. Super useful when working with closure-based APIs and we want to use some of our properties without having to capture self.

func combine<A, B>(_ value: A, with closure: @escaping (A) -> B) -> () -> B {
    return { closure(value) }
}

// BEFORE:

class ProductViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        buyButton.handler = { [weak self] in
            guard let self = self else {
                return
            }
            
            self.productManager.startCheckout(for: self.product)
        }
    }
}

// AFTER:

class ProductViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        buyButton.handler = combine(product, with: productManager.startCheckout)
    }
}

99 Dependency injection using functions

💉 When I'm only using a single function from a dependency, I love to inject that function as a closure, instead of having to create a protocol and inject the whole object. Makes dependency injection & testing super simple.

final class ArticleLoader {
    typealias Networking = (Endpoint) -> Future<Data>
    
    private let networking: Networking
    
    init(networking: @escaping Networking = URLSession.shared.load) {
        self.networking = networking
    }
    
    func loadLatest() -> Future<[Article]> {
        return networking(.latestArticles).decode()
    }
}

For more on this technique, check out "Simple Swift dependency injection with functions".

98 Using a custom exception handler

💥 It's cool that you can easily assign a closure as a custom NSException handler. This is super useful when building things in Playgrounds - since you can't use breakpoints - so instead of just signal SIGABRT, you'll get the full exception description if something goes wrong.

NSSetUncaughtExceptionHandler { exception in
    print(exception)
}

97 Using type aliases to give semantic meaning to primitives

❤️ I love that in Swift, we can use the type system to make our code so much more self-documenting - one way of doing so is to use type aliases to give the primitive types that we use a more semantic meaning.

extension List.Item {
    // Using type aliases, we can give semantic meaning to the
    // primitive types that we use, without having to introduce
    // wrapper types.
    typealias Index = Int
}

extension List {
    enum Mutation {
        // Our enum cases now become a lot more self-documenting,
        // without having to add additional parameter labels to
        // explain them.
        case add(Item, Item.Index)
        case update(Item, Item.Index)
        case remove(Item.Index)
    }
}

For more on self-documenting code, check out "Writing self-documenting Swift code".

96 Specializing protocols using constraints

🤯 A little late night prototyping session reveals that protocol constraints can not only be applied to extensions - they can also be added to protocol definitions!

This is awesome, since it lets us easily define specialized protocols based on more generic ones.

protocol Component {
    associatedtype Container
    func add(to container: Container)
}

// Protocols that inherit from other protocols can include
// constraints to further specialize them.
protocol ViewComponent: Component where Container == UIView {
    associatedtype View: UIView
    var view: View { get }
}

extension ViewComponent {
    func add(to container: UIView) {
        container.addSubview(view)
    }
}

For more on specializing protocols, check out "Specializing protocols in Swift".

95 Unwrapping an optional or throwing an error

📦 Here's a super handy extension on Swift's Optional type, which gives us a really nice API for easily unwrapping an optional, or throwing an error in case the value turned out to be nil:

extension Optional {
    func orThrow(_ errorExpression: @autoclosure () -> Error) throws -> Wrapped {
        switch self {
        case .some(let value):
            return value
        case .none:
            throw errorExpression()
        }
    }
}

let file = try loadFile(at: path).orThrow(MissingFileError())

For more ways that optionals can be extended, check out "Extending optionals in Swift".

94 Testing code that uses static APIs

👩‍🔬 Testing code that uses static APIs can be really tricky, but there's a way that it can often be done - using Swift's first class function capabilities!

Instead of accessing that static API directly, we can inject the function we want to use, which enables us to mock it!

// BEFORE

class FriendsLoader {
    func loadFriends(then handler: @escaping (Result<[Friend]>) -> Void) {
        Networking.loadData(from: .friends) { result in
            ...
        }
    }
}

// AFTER

class FriendsLoader {
    typealias Handler<T> = (Result<T>) -> Void
    typealias DataLoadingFunction = (Endpoint, @escaping Handler<Data>) -> Void

    func loadFriends(using dataLoading: DataLoadingFunction = Networking.loadData,
                     then handler: @escaping Handler<[Friend]>) {
        dataLoading(.friends) { result in
            ...
        }
    }
}

// MOCKING IN TESTS

let dataLoading: FriendsLoader.DataLoadingFunction = { _, handler in
    handler(.success(mockData))
}

friendsLoader.loadFriends(using: dataLoading) { result in
    ...
}

93 Matching multiple enum cases with associated values

🐾 Swift's pattern matching capabilities are so powerful! Two enum cases with associated values can even be matched and handled by the same switch case - which is super useful when handling state changes with similar data.

enum DownloadState {
    case inProgress(progress: Double)
    case paused(progress: Double)
    case cancelled
    case finished(Data)
}

func downloadStateDidChange(to state: DownloadState) {
    switch state {
    case .inProgress(let progress), .paused(let progress):
        updateProgressView(with: progress)
    case .cancelled:
        showCancelledMessage()
    case .finished(let data):
        process(data)
    }
}

92 Multiline string literals

🅰 One really nice benefit of Swift multiline string literals - even for single lines of text - is that they don't require quotes to be escaped. Perfect when working with things like HTML, or creating a custom description for an object.

let html = highlighter.highlight("Array<String>")

XCTAssertEqual(html, """
<span class="type">Array</span>&lt;<span class="type">String</span>&gt;
""")

91 Reducing sequences

💎 While it's very common in functional programming, the reduce function might be a bit of a hidden gem in Swift. It provides a super useful way to transform a sequence into a single value.

extension Sequence where Element: Equatable {
    func numberOfOccurrences(of target: Element) -> Int {
        return reduce(0) { result, element in
            guard element == target else {
                return result
            }

            return result + 1
        }
    }
}

You can read more about transforming collections in "Transforming collections in Swift".

90 Avoiding manual Codable implementations

📦 When I use Codable in Swift, I want to avoid manual implementations as much as possible, even when there's a mismatch between my code structure and the JSON I'm decoding.

One way that can often be achieved is to use private data containers combined with computed properties.

struct User: Codable {
    let name: String
    let age: Int

    var homeTown: String { return originPlace.name }

    private let originPlace: Place
}

private extension User {
    struct Place: Codable {
        let name: String
    }
}

extension User {
    struct Container: Codable {
        let user: User
    }
}

89 Using feature flags instead of feature branches

🚢 Instead of using feature branches, I merge almost all of my code directly into master - and then I use feature flags to conditionally enable features when they're ready. That way I can avoid merge conflicts and keep shipping!

extension ListViewController {
    func addSearchIfNeeded() {
        // Rather than having to keep maintaining a separate
        // feature branch for a new feature, we can use a flag
        // to conditionally turn it on.
        guard FeatureFlags.searchEnabled else {
            return
        }

        let resultsVC = SearchResultsViewController()
        let searchVC = UISearchController(
            searchResultsController: resultsVC
        )

        searchVC.searchResultsUpdater = resultsVC
        navigationItem.searchController = searchVC
    }
}

You can read more about feature flags in "Feature flags in Swift".

88 Lightweight data hierarchies using tuples

💾 Here I'm using tuples to create a lightweight hierarchy for my data, giving me a nice structure without having to introduce any additional types.

struct CodeSegment {
    var tokens: (
        previous: String?,
        current: String
    )

    var delimiters: (
        previous: Character?
        next: Character?
    )
}

handle(segment.tokens.current)

You can read more about tuples in "Using tuples as lightweight types in Swift"

87 The rule of threes

3️⃣ Whenever I have 3 properties or local variables that share the same prefix, I usually try to extract them into their own method or type. That way I can avoid massive types & methods, and also increase readability, without falling into a "premature optimization" trap.

Before

public func generate() throws {
    let contentFolder = try folder.subfolder(named: "content")

    let articleFolder = try contentFolder.subfolder(named: "posts")
    let articleProcessor = ContentProcessor(folder: articleFolder)
    let articles = try articleProcessor.process()

    ...
}

After

public func generate() throws {
    let contentFolder = try folder.subfolder(named: "content")
    let articles = try processArticles(in: contentFolder)
    ...
}

private func processArticles(in folder: Folder) throws -> [ContentItem] {
    let folder = try folder.subfolder(named: "posts")
    let processor = ContentProcessor(folder: folder)
    return try processor.process()
}

86 Useful Codable extensions

👨‍🔧 Here's two extensions that I always add to the Encodable & Decodable protocols, which for me really make the Codable API nicer to use. By using type inference for decoding, a lot of boilerplate can be removed when the compiler is already able to infer the resulting type.

extension Encodable {
    func encoded() throws -> Data {
        return try JSONEncoder().encode(self)
    }
}

extension Data {
    func decoded<T: Decodable>() throws -> T {
        return try JSONDecoder().decode(T.self, from: self)
    }
}

let data = try user.encoded()

// By using a generic type in the decoded() method, the
// compiler can often infer the type we want to decode
// from the current context.
try userDidLogin(data.decoded())

// And if not, we can always supply the type, still making
// the call site read very nicely.
let otherUser = try data.decoded() as User

85 Using shared UserDefaults suites

📦 UserDefaults is a lot more powerful than what it first might seem like. Not only can it store more complex values (like dates & dictionaries) and parse command line arguments - it also enables easy sharing of settings & lightweight data between apps in the same App Group.

let sharedDefaults = UserDefaults(suiteName: "my-app-group")!
let useDarkMode = sharedDefaults.bool(forKey: "dark-mode")

// This value is put into the shared suite.
sharedDefaults.set(true, forKey: "dark-mode")

// If you want to treat the shared settings as read-only (and add
// local overrides on top of them), you can simply add the shared
// suite to the standard UserDefaults.
let combinedDefaults = UserDefaults.standard
combinedDefaults.addSuite(named: "my-app-group")

// This value is a local override, not added to the shared suite.
combinedDefaults.set(true, forKey: "app-specific-override")

84 Custom UIView backing layers

🎨 By overriding layerClass you can tell UIKit what CALayer class to use for a UIView's backing layer. That way you can reduce the amount of layers, and don't have to do any manual layout.

final class GradientView: UIView {
    override class var layerClass: AnyClass { return CAGradientLayer.self }

    var colors: (start: UIColor, end: UIColor)? {
        didSet { updateLayer() }
    }

    private func updateLayer() {
        let layer = self.layer as! CAGradientLayer
        layer.colors = colors.map { [$0.start.cgColor, $0.end.cgColor] }
    }
}

83 Auto-Equatable enums with associated values

✅ That the compiler now automatically synthesizes Equatable conformances is such a huge upgrade for Swift! And the cool thing is that it works for all kinds of types - even for enums with associated values! Especially useful when using enums for verification in unit tests.

struct Article: Equatable {
    let title: String
    let text: String
}

struct User: Equatable {
    let name: String
    let age: Int
}

extension Navigator {
    enum Destination: Equatable {
        case profile(User)
        case article(Article)
    }
}

func testNavigatingToArticle() {
    let article = Article(title: "Title", text: "Text")
    controller.select(article)
    XCTAssertEqual(navigator.destinations, [.article(article)])
}

82 Defaults for associated types

🤝 Associated types can have defaults in Swift - which is super useful for types that are not easily inferred (for example when they're not used for a specific instance method or property).

protocol Identifiable {
    associatedtype RawIdentifier: Codable = String

    var id: Identifier<Self> { get }
}

struct User: Identifiable {
    let id: Identifier<User>
    let name: String
}

struct Group: Identifiable {
    typealias RawIdentifier = Int

    let id: Identifier<Group>
    let name: String
}

81 Creating a dedicated identifier type

🆔 If you want to avoid using plain strings as identifiers (which can increase both type safety & readability), it's really easy to create a custom Identifier type that feels just like a native Swift type, thanks to protocols!

More on this topic in "Type-safe identifiers in Swift".

struct Identifier: Hashable {
    let string: String
}

extension Identifier: ExpressibleByStringLiteral {
    init(stringLiteral value: String) {
        string = value
    }
}

extension Identifier: CustomStringConvertible {
    var description: String {
        return string
    }
}

extension Identifier: Codable {
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        string = try container.decode(String.self)
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        try container.encode(string)
    }
}

struct Article: Codable {
    let id: Identifier
    let title: String
}

let article = Article(id: "my-article", title: "Hello world!")

80 Assigning optional tuple members to variables

🙌 A really cool thing about using tuples to model the internal state of a Swift type, is that you can unwrap an optional tuple's members directly into local variables.

Very useful in order to group multiple optional values together for easy unwrapping & handling.

class ImageTransformer {
    private var queue = [(image: UIImage, transform: Transform)]()

    private func processNext() {
        // When unwrapping an optional tuple, you can assign the members
        // directly to local variables.
        guard let (image, transform) = queue.first else {
            return
        }

        let context = Context()
        context.draw(image)
        context.apply(transform)
        ...
    }
}

79 Struct convenience initializers

❤️ I love to structure my code using extensions in Swift. One big benefit of doing so when it comes to struct initializers, is that defining a convenience initializer doesn't remove the default one the compiler generates - best of both worlds!

struct Article {
    let date: Date
    var title: String
    var text: String
    var comments: [Comment]
}

extension Article {
    init(title: String, text: String) {
        self.init(date: Date(), title: title, text: text, comments: [])
    }
}

let articleA = Article(title: "Best Cupcake Recipe", text: "...")

let articleB = Article(
    date: Date(),
    title: "Best Cupcake Recipe",
    text: "...",
    comments: [
        Comment(user: currentUser, text: "Yep, can confirm!")
    ]
)

78 Usages of throwing functions

🏈 A big benefit of using throwing functions for synchronous Swift APIs is that the caller can decide whether they want to treat the return value as optional (try?) or required (try).

func loadFile(named name: String) throws -> File {
    guard let url = urlForFile(named: name) else {
        throw File.Error.missing
    }

    do {
        let data = try Data(contentsOf: url)
        return File(url: url, data: data)
    } catch {
        throw File.Error.invalidData(error)
    }
}

let requiredFile = try loadFile(named: "AppConfig.json")

let optionalFile = try? loadFile(named: "UserSettings.json")

77 Nested generic types

🐝 Types that are nested in generics automatically inherit their parent's generic types - which is super useful when defining accessory types (for things like states or outcomes).

struct Task<Input, Output> {
    typealias Closure = (Input) throws -> Output

    let closure: Closure
}

extension Task {
    enum Result {
        case success(Output)
        case failure(Error)
    }
}

76 Equatable & Hashable structures

🤖 Now that the Swift compiler automatically synthesizes Equatable & Hashable conformances for value types, it's easier than ever to setup model structures with nested types that are all Equatable/Hashable!

typealias Value = Hashable & Codable

struct User: Value {
    var name: String
    var age: Int
    var lastLoginDate: Date?
    var settings: Settings
}

extension User {
    struct Settings: Value {
        var itemsPerPage: Int
        var theme: Theme
    }
}

extension User.Settings {
    enum Theme: String, Value {
        case light
        case dark
    }
}

You can read more about using nested types in Swift here.

75 Conditional conformances

🎉 Swift 4.1 is here! One of the key features it brings is conditional conformances, which lets you have a type only conform to a protocol under certain constraints.

protocol UnboxTransformable {
    associatedtype RawValue

    static func transform(_ value: RawValue) throws -> Self?
}

extension Array: UnboxTransformable where Element: UnboxTransformable {
    typealias RawValue = [Element.RawValue]

    static func transform(_ value: RawValue) throws -> [Element]? {
        return try value.compactMap(Element.transform)
    }
}

I also have an article with lots of more info on conditional conformances here. Paul Hudson also has a great overview of all Swift 4.1 features here.

74 Generic type aliases

🕵️‍♀️ A cool thing about Swift type aliases is that they can be generic! Combine that with tuples and you can easily define simple generic types.

typealias Pair<T> = (T, T)

extension Game {
    func calculateScore(for players: Pair<Player>) -> Int {
        ...
    }
}

You can read more about using tuples as lightweight types here.

73 Parsing command line arguments using UserDefaults

☑️ A really cool "hidden" feature of UserDefaults is that it contains any arguments that were passed to the app at launch!

Super useful both in Swift command line tools & scripts, but also to temporarily override a value when debugging iOS apps.

let defaults = UserDefaults.standard
let query = defaults.string(forKey: "query")
let resultCount = defaults.integer(forKey: "results")

72 Using the & operator

👏 Swift's & operator is awesome! Not only can you use it to compose protocols, you can compose other types too! Very useful if you want to hide concrete types & implementation details.

protocol LoadableFromURL {
    func load(from url: URL)
}

class ContentViewController: UIViewController, LoadableFromURL {
    func load(from url: URL) {
        ...
    }
}

class ViewControllerFactory {
    func makeContentViewController() -> UIViewController & LoadableFromURL {
        return ContentViewController()
    }
}

71 Capturing multiple values in mocks

🤗 When capturing values in mocks, using an array (instead of just a single value) makes it easy to verify that only a certain number of values were passed.

Perfect for protecting against "over-calling" something.

class UserManagerTests: XCTestCase {
    func testObserversCalledWhenUserFirstLogsIn() {
        let manager = UserManager()

        let observer = ObserverMock()
        manager.addObserver(observer)

        // First login, observers should be notified
        let user = User(id: 123, name: "John")
        manager.userDidLogin(user)
        XCTAssertEqual(observer.users, [user])

        // If the same user logs in again, observers shouldn't be notified
        manager.userDidLogin(user)
        XCTAssertEqual(observer.users, [user])
    }
}

private extension UserManagerTests {
    class ObserverMock: UserManagerObserver {
        private(set) var users = [User]()

        func userDidChange(to user: User) {
            users.append(user)
        }
    }
}

70 Reducing the need for mocks

👋 When writing tests, you don't always need to create mocks - you can create stubs using real instances of things like errors, URLs & UserDefaults.

Here's how to do that for some common tasks/object types in Swift:

// Create errors using NSError (#function can be used to reference the name of the test)
let error = NSError(domain: #function, code: 1, userInfo: nil)

// Create non-optional URLs using file paths
let url = URL(fileURLWithPath: "Some/URL")

// Reference the test bundle using Bundle(for:)
let bundle = Bundle(for: type(of: self))

// Create an explicit UserDefaults object (instead of having to use a mock)
let userDefaults = UserDefaults(suiteName: #function)

// Create queues to control/await concurrent operations
let queue = DispatchQueue(label: #function)

For when you actually do need mocking, check out "Mocking in Swift".

69 Using "then" as an external parameter label for closures

⏱ I've started using "then" as an external parameter label for completion handlers. Makes the call site read really nicely (Because I do ❤️ conversational API design) regardless of whether trailing closure syntax is used or not.

protocol DataLoader {
    // Adding type aliases to protocols can be a great way to
    // reduce verbosity for parameter types.
    typealias Handler = (Result<Data>) -> Void
    associatedtype Endpoint

    func loadData(from endpoint: Endpoint, then handler: @escaping Handler)
}

loader.loadData(from: .messages) { result in
    ...
}

loader.loadData(from: .messages, then: { result in
    ...
})

68 Combining lazily evaluated sequences with the builder pattern

😴 Combining lazily evaluated sequences with builder pattern-like properties can lead to some pretty sweet APIs for configurable sequences in Swift.

Also useful for queries & other things you "build up" and then execute.

// Extension adding builder pattern-like properties that return
// a new sequence value with the given configuration applied
extension FileSequence {
    var recursive: FileSequence {
        var sequence = self
        sequence.isRecursive = true
        return sequence
    }

    var includingHidden: FileSequence {
        var sequence = self
        sequence.includeHidden = true
        return sequence
    }
}

// BEFORE

let files = folder.makeFileSequence(recursive: true, includeHidden: true)

// AFTER

let files = folder.files.recursive.includingHidden

Want an intro to lazy sequences? Check out "Swift sequences: The art of being lazy".

67 Faster & more stable UI tests

My top 3 tips for faster & more stable UI tests:

📱 Reset the app's state at the beginning of every test.

🆔 Use accessibility identifiers instead of UI strings.

⏱ Use expectations instead of waiting time.

func testOpeningArticle() {
    // Launch the app with an argument that tells it to reset its state
    let app = XCUIApplication()
    app.launchArguments.append("--uitesting")
    app.launch()
    
    // Check that the app is displaying an activity indicator
    let activityIndicator = app.activityIndicator.element
    XCTAssertTrue(activityIndicator.exists)
    
    // Wait for the loading indicator to disappear = content is ready
    expectation(for: NSPredicate(format: "exists == 0"),
                evaluatedWith: activityIndicator)
                
    // Use a generous timeout in case the network is slow
    waitForExpectations(timeout: 10)
    
    // Tap the cell for the first article
    app.tables.cells["Article.0"].tap()
    
    // Assert that a label with the accessibility identifier "Article.Title" exists
    let label = app.staticTexts["Article.Title"]
    XCTAssertTrue(label.exists)
}

66 Accessing the clipboard from a Swift script

📋 It's super easy to access the contents of the clipboard from a Swift script. A big benefit of Swift scripting is being able to use Cocoa's powerful APIs for Mac apps.

import Cocoa

let clipboard = NSPasteboard.general.string(forType: .string)

65 Using tuples for view state

🎯 Using Swift tuples for view state can be a super nice way to group multiple properties together and render them reactively using the layout system.

By using a tuple we don't have to either introduce a new type or make our view model-aware.

class TextView: UIView {
    var state: (title: String?, text: String?) {
        // By telling UIKit that our view needs layout and binding our
        // state in layoutSubviews, we can react to state changes without
        // doing unnecessary layout work.
        didSet { setNeedsLayout() }
    }

    private let titleLabel = UILabel()
    private let textLabel = UILabel()

    override func layoutSubviews() {
        super.layoutSubviews()

        titleLabel.text = state.title
        textLabel.text = state.text

        ...
    }
}

64 Throwing tests and LocalizedError

⚾️ Swift tests can throw, which is super useful in order to avoid complicated logic or force unwrapping. By making errors conform to LocalizedError, you can also get a nice error message in Xcode if there's a failure.

class ImageCacheTests: XCTestCase {
    func testCachingAndLoadingImage() throws {
        let bundle = Bundle(for: type(of: self))
        let cache = ImageCache(bundle: bundle)
        
        // Bonus tip: You can easily load images from your test
        // bundle using this UIImage initializer
        let image = try require(UIImage(named: "sample", in: bundle, compatibleWith: nil))
        try cache.cache(image, forKey: "key")
        
        let cachedImage = try cache.image(forKey: "key")
        XCTAssertEqual(image, cachedImage)
    }
}

enum ImageCacheError {
    case emptyKey
    case dataConversionFailed
}

// When using throwing tests, making your errors conform to
// LocalizedError will render a much nicer error message in
// Xcode (per default only the error code is shown).
extension ImageCacheError: LocalizedError {
    var errorDescription: String? {
        switch self {
        case .emptyKey:
            return "An empty key was given"
        case .dataConversionFailed:
            return "Failed to convert the given image to Data"
        }
    }
}

For more information, and the implementation of the require method used above, check out "Avoiding force unwrapping in Swift unit tests".

63 The difference between static and class properties

✍️ Unlike static properties, class properties can be overridden by subclasses (however, they can't be stored, only computed).

class TableViewCell: UITableViewCell {
    class var preferredHeight: CGFloat { return 60 }
}

class TallTableViewCell: TableViewCell {
    override class var preferredHeight: CGFloat { return 100 }
}

62 Creating extensions with static factory methods

👨‍🎨 Creating extensions with static factory methods can be a great alternative to subclassing in Swift, especially for things like setting up UIViews, CALayers or other kinds of styling.

It also lets you remove a lot of styling & setup from your view controllers.

extension UILabel {
    static func makeForTitle() -> UILabel {
        let label = UILabel()
        label.font = .boldSystemFont(ofSize: 24)
        label.textColor = .darkGray
        label.adjustsFontSizeToFitWidth = true
        label.minimumScaleFactor = 0.75
        return label
    }

    static func makeForText() -> UILabel {
        let label = UILabel()
        label.font = .systemFont(ofSize: 16)
        label.textColor = .black
        label.numberOfLines = 0
        return label
    }
}

class ArticleViewController: UIViewController {
    lazy var titleLabel = UILabel.makeForTitle()
    lazy var textLabel = UILabel.makeForText()
}

61 Child view controller auto-resizing

🧒 An awesome thing about child view controllers is that they're automatically resized to match their parent, making them a super nice solution for things like loading & error views.

class ListViewController: UIViewController {
    func loadItems() {
        let loadingViewController = LoadingViewController()
        add(loadingViewController)

        dataLoader.loadItems { [weak self] result in
            loadingViewController.remove()
            self?.handle(result)
        }
    }
}

For more about child view controller (including the add and remove methods used above), check out "Using child view controllers as plugins in Swift".

60 Using zip

🤐 Using the zip function in Swift you can easily combine two sequences. Super useful when using two sequences to do some work, since zip takes care of all the bounds-checking.

func render(titles: [String]) {
    for (label, text) in zip(titleLabels, titles) {
        print(text)
        label.text = text
    }
}

59 Defining custom option sets

🎛 The awesome thing about option sets in Swift is that they can automatically either be passed as a single member or as a set. Even cooler is that you can easily define your own option sets as well, perfect for options and other non-exclusive values.

// Option sets are awesome, because you can easily pass them
// both using dot syntax and array literal syntax, like when
// using the UIView animation API:
UIView.animate(withDuration: 0.3,
               delay: 0,
               options: .allowUserInteraction,
               animations: animations)

UIView.animate(withDuration: 0.3,
               delay: 0,
               options: [.allowUserInteraction, .layoutSubviews],
               animations: animations)

// The cool thing is that you can easily define your own option
// sets as well, by defining a struct that has an Int rawValue,
// that will be used as a bit mask.
extension Cache {
    struct Options: OptionSet {
        static let saveToDisk = Options(rawValue: 1)
        static let clearOnMemoryWarning = Options(rawValue: 1 << 1)
        static let clearDaily = Options(rawValue: 1 << 2)

        let rawValue: Int
    }
}

// We can now use Cache.Options just like UIViewAnimationOptions:
Cache(options: .saveToDisk)
Cache(options: [.saveToDisk, .clearDaily])

58 Using the where clause with associated types

🙌 Using the where clause when designing protocol-oriented APIs in Swift can let your implementations (or others' if it's open source) have a lot more freedom, especially when it comes to collections.

See "Using generic type constraints in Swift 4" for more info.

public protocol PathFinderMap {
    associatedtype Node
    // Using the 'where' clause for associated types, we can
    // ensure that a type meets certain requirements (in this
    // case that it's a sequence with Node elements).
    associatedtype NodeSequence: Sequence where NodeSequence.Element == Node

    // Instead of using a concrete type (like [Node]) here, we
    // give implementors of this protocol more freedom while
    // still meeting our requirements. For example, one
    // implementation might use Set<Node>.
    func neighbors(of node: Node) -> NodeSequence
}

57 Using first class functions when iterating over a dictionary

👨‍🍳 Combine first class functions in Swift with the fact that Dictionary elements are (Key, Value) tuples and you can build yourself some pretty awesome functional chains when iterating over a Dictionary.

func makeActor(at coordinate: Coordinate, for building: Building) -> Actor {
    let actor = Actor()
    actor.position = coordinate.point
    actor.animation = building.animation
    return actor
}

func render(_ buildings: [Coordinate : Building]) {
    buildings.map(makeActor).forEach(add)
}

56 Calling instance methods as static functions

😎 In Swift, you can call any instance method as a static function and it will return a closure representing that method. This is how running tests using SPM on Linux works.

More about this topic in my blog post "First class functions in Swift".

// This produces a '() -> Void' closure which is a reference to the
// given view's 'removeFromSuperview' method.
let closure = UIView.removeFromSuperview(view)

// We can now call it just like we would any other closure, and it
// will run 'view.removeFromSuperview()'
closure()

// This is how running tests using the Swift Package Manager on Linux
// works, you return your test functions as closures:
extension UserManagerTests {
    static var allTests = [
        ("testLoggingIn", testLoggingIn),
        ("testLoggingOut", testLoggingOut),
        ("testUserPermissions", testUserPermissions)
    ]
}

55 Dropping suffixes from method names to support multiple arguments

👏 One really nice benefit of dropping suffixes from method names (and just using verbs, when possible) is that it becomes super easy to support both single and multiple arguments, and it works really well semantically.

extension UIView {
    func add(_ subviews: UIView...) {
        subviews.forEach(addSubview)
    }
}

view.add(button)
view.add(label)

// By dropping the "Subview" suffix from the method name, both
// single and multiple arguments work really well semantically.
view.add(button, label)

54 Constraining protocols to classes to ensure mutability

👽 Using the AnyObject (or class) constraint on protocols is not only useful when defining delegates (or other weak references), but also when you always want instances to be mutable without copying.

// By constraining a protocol with 'AnyObject' it can only be adopted
// by classes, which means all instances will always be mutable, and
// that it's the original instance (not a copy) that will be mutated.
protocol DataContainer: AnyObject {
    var data: Data? { get set }
}

class UserSettingsManager {
    private var settings: Settings
    private let dataContainer: DataContainer

    // Since DataContainer is a protocol, we an easily mock it in
    // tests if we use dependency injection
    init(settings: Settings, dataContainer: DataContainer) {
        self.settings = settings
        self.dataContainer = dataContainer
    }

    func saveSettings() throws {
        let data = try settings.serialize()

        // We can now assign properties on an instance of our protocol
        // because the compiler knows it's always going to be a class
        dataContainer.data = data
    }
}

53 String-based enums in string interpolation

🍣 Even if you define a custom raw value for a string-based enum in Swift, the full case name will be used in string interpolation.

Super useful when using separate raw values for JSON, while still wanting to use the full case name in other contexts.

extension Building {
    // This enum has custom raw values that are used when decoding
    // a value, for example from JSON.
    enum Kind: String {
        case castle = "C"
        case town = "T"
        case barracks = "B"
        case goldMine = "G"
        case camp = "CA"
        case blacksmith = "BL"
    }

    var animation: Animation {
        return Animation(
            // When used in string interpolation, the full case name is still used.
            // For 'castle' this will be 'buildings/castle'.
            name: "buildings/\(kind)",
            frameCount: frameCount,
            frameDuration: frameDuration
        )
    }
}

52 Expressively comparing a value with a list of candidates

👨‍🔬 Continuing to experiment with expressive ways of comparing a value with a list of candidates in Swift. Adding an extension on Equatable is probably my favorite approach so far.

extension Equatable {
    func isAny(of candidates: Self...) -> Bool {
        return candidates.contains(self)
    }
}

let isHorizontal = direction.isAny(of: .left, .right)

See tip 35 for my previous experiment.

51 UIView bounds and transforms

📐 A really interesting side-effect of a UIView's bounds being its rect within its own coordinate system is that transforms don't affect it at all. That's why it's usually a better fit than frame when doing layout calculations of subviews.

let view = UIView()
view.frame.size = CGSize(width: 100, height: 100)
view.transform = CGAffineTransform(scaleX: 2, y: 2)

print(view.frame) // (-50.0, -50.0, 200.0, 200.0)
print(view.bounds) // (0.0, 0.0, 100.0, 100.0)

50 UIKit default arguments

👏 It's awesome that many UIKit APIs with completion handlers and other optional parameters import into Swift with default arguments (even though they are written in Objective-C). Getting rid of all those nil arguments is so nice!

// BEFORE: All parameters are specified, just like in Objective-C

viewController.present(modalViewController, animated: true, completion: nil)

modalViewController.dismiss(animated: true, completion: nil)

viewController.transition(from: loadingViewController,
                          to: contentViewController,
                          duration: 0.3,
                          options: [],
                          animations: animations,
                          completion: nil)

// AFTER: Since many UIKit APIs with completion handlers and other
// optional parameters import into Swift with default arguments,
// we can make our calls shorter

viewController.present(modalViewController, animated: true)

modalViewController.dismiss(animated: true)

viewController.transition(from: loadingViewController,
                          to: contentViewController,
                          duration: 0.3,
                          animations: animations)

49 Avoiding Massive View Controllers

✂️ Avoiding Massive View Controllers is all about finding the right levels of abstraction and splitting things up.

My personal rule of thumb is that as soon as I have 3 methods or properties that have the same prefix, I break them out into their own type.

// BEFORE

class LoginViewController: UIViewController {
    private lazy var signUpLabel = UILabel()
    private lazy var signUpImageView = UIImageView()
    private lazy var signUpButton = UIButton()
}

// AFTER

class LoginViewController: UIViewController {
    private lazy var signUpView = SignUpView()
}

class SignUpView: UIView {
    private lazy var label = UILabel()
    private lazy var imageView = UIImageView()
    private lazy var button = UIButton()
}

48 Extending optionals

❤️ I love the fact that optionals are enums in Swift - it makes it so easy to extend them with convenience APIs for certain types. Especially useful when doing things like data validation on optional values.

func validateTextFields() -> Bool {
    guard !usernameTextField.text.isNilOrEmpty else {
        return false
    }

    ...

    return true
}

// Since all optionals are actual enum values in Swift, we can easily
// extend them for certain types, to add our own convenience APIs

extension Optional where Wrapped == String {
    var isNilOrEmpty: Bool {
        switch self {
        case let string?:
            return string.isEmpty
        case nil:
            return true
        }
    }
}

// Since strings are now Collections in Swift 4, you can even
// add this property to all optional collections:

extension Optional where Wrapped: Collection {
    var isNilOrEmpty: Bool {
        switch self {
        case let collection?:
            return collection.isEmpty
        case nil:
            return true
        }
    }
}

47 Using where with for-loops

🗺 Using the where keyword can be a super nice way to quickly apply a filter in a for-loop in Swift. You can of course use map, filter and forEach, or guard, but for simple loops I think this is very expressive and nice.

func archiveMarkedPosts() {
    for post in posts where post.isMarked {
        archive(post)
    }
}

func healAllies() {
    for player in players where player.isAllied(to: currentPlayer) {
        player.heal()
    }
}

46 Variable shadowing

👻 Variable shadowing can be super useful in Swift, especially when you want to create a local copy of a parameter value in order to use it as state within a closure.

init(repeatMode: RepeatMode, closure: @escaping () -> UpdateOutcome) {
    // Shadow the argument with a local, mutable copy
    var repeatMode = repeatMode
    
    self.closure = {
        // With shadowing, there's no risk of accidentially
        // referring to the immutable version
        switch repeatMode {
        case .forever:
            break
        case .times(let count):
            guard count > 0 else {
                return .finished
            }
            
            // We can now capture the mutable version and use
            // it for state in a closure
            repeatMode = .times(count - 1)
        }
        
        return closure()
    }
}

45 Using dot syntax for static properties and initializers

✒️ Dot syntax is one of my favorite features of Swift. What's really cool is that it's not only for enums, any static method or property can be used with dot syntax - even initializers! Perfect for convenience APIs and default parameters.

public enum RepeatMode {
    case times(Int)
    case forever
}

public extension RepeatMode {
    static var never: RepeatMode {
        return .times(0)
    }

    static var once: RepeatMode {
        return .times(1)
    }
}

view.perform(animation, repeated: .once)

// To make default parameters more compact, you can even use init with dot syntax

class ImageLoader {
    init(cache: Cache = .init(), decoder: ImageDecoder = .init()) {
        ...
    }
}

44 Calling functions as closures with a tuple as parameters

🚀 One really cool aspect of Swift having first class functions is that you can pass any function (or even initializer) as a closure, and even call it with a tuple containing its parameters!

// This function lets us treat any "normal" function or method as
// a closure and run it with a tuple that contains its parameters
func call<Input, Output>(_ function: (Input) -> Output, with input: Input) -> Output {
    return function(input)
}

class ViewFactory {
    func makeHeaderView() -> HeaderView {
        // We can now pass an initializer as a closure, and a tuple
        // containing its parameters
        return call(HeaderView.init, with: loadTextStyles())
    }
    
    private func loadTextStyles() -> (font: UIFont, color: UIColor) {
        return (theme.font, theme.textColor)
    }
}

class HeaderView {
    init(font: UIFont, textColor: UIColor) {
        ...
    }
}

43 Enabling static dependency injection

💉 If you've been struggling to test code that uses static APIs, here's a technique you can use to enable static dependency injection without having to modify any call sites:

// Before: Almost impossible to test due to the use of singletons

class Analytics {
    static func log(_ event: Event) {
        Database.shared.save(event)
        
        let dictionary = event.serialize()
        NetworkManager.shared.post(dictionary, to: eventURL)
    }
}

// After: Much easier to test, since we can inject mocks as arguments

class Analytics {
    static func log(_ event: Event,
                    database: Database = .shared,
                    networkManager: NetworkManager = .shared) {
        database.save(event)
        
        let dictionary = event.serialize()
        networkManager.post(dictionary, to: eventURL)
    }
}

42 Type inference for lazy properties in Swift 4

🎉 In Swift 4, type inference works for lazy properties and you don't need to explicitly refer to self!

// Swift 3

class PurchaseView: UIView {
    private lazy var buyButton: UIButton = self.makeBuyButton()
    
    private func makeBuyButton() -> UIButton {
        let button = UIButton()
        button.setTitle("Buy", for: .normal)
        button.setTitleColor(.blue, for: .normal)
        return button
    }
}

// Swift 4

class PurchaseView: UIView {
    private lazy var buyButton = makeBuyButton()
    
    private func makeBuyButton() -> UIButton {
        let button = UIButton()
        button.setTitle("Buy", for: .normal)
        button.setTitleColor(.blue, for: .normal)
        return button
    }
}

41 Converting Swift errors to NSError

😎 You can turn any Swift Error into an NSError, which is super useful when pattern matching with a code 👍. Also, switching on optionals is pretty cool!

let task = urlSession.dataTask(with: url) { data, _, error in
    switch error {
    case .some(let error as NSError) where error.code == NSURLErrorNotConnectedToInternet:
        presenter.showOfflineView()
    case .some(let error):
        presenter.showGenericErrorView()
    case .none:
        presenter.renderContent(from: data)
    }
}

task.resume()

Also make sure to check out Kostas Kremizas' tip about how you can pattern match directly against a member of URLError.

40 Making UIImage macOS compatible

🖥 Here's an easy way to make iOS model code that uses UIImage macOS compatible - like me and Gui Rambo discussed on the Swift by Sundell Podcast.

// Either put this in a separate file that you only include in your macOS target or wrap the code in #if os(macOS) / #endif

import Cocoa

// Step 1: Typealias UIImage to NSImage
typealias UIImage = NSImage

// Step 2: You might want to add these APIs that UIImage has but NSImage doesn't.
extension NSImage {
    var cgImage: CGImage? {
        var proposedRect = CGRect(origin: .zero, size: size)

        return cgImage(forProposedRect: &proposedRect,
                       context: nil,
                       hints: nil)
    }

    convenience init?(named name: String) {
        self.init(named: Name(name))
    }
}

// Step 3: Profit - you can now make your model code that uses UIImage cross-platform!
struct User {
    let name: String
    let profileImage: UIImage
}

39 Internally mutable protocol-oriented APIs

🤖 You can easily define a protocol-oriented API that can only be mutated internally, by using an internal protocol that extends a public one.

// Declare a public protocol that acts as your immutable API
public protocol ModelHolder {
    associatedtype Model
    var model: Model { get }
}

// Declare an extended, internal protocol that provides a mutable API
internal protocol MutableModelHolder: ModelHolder {
    var model: Model { get set }
}

// You can now implement the requirements using 'public internal(set)'
public class UserHolder: MutableModelHolder {
    public internal(set) var model: User

    internal init(model: User) {
        self.model = model
    }
}

38 Switching on a set

🎛 You can switch on a set using array literals as cases in Swift! Can be really useful to avoid many if/else if statements.

class RoadTile: Tile {
    var connectedDirections = Set<Direction>()

    func render() {
        switch connectedDirections {
        case [.up, .down]:
            image = UIImage(named: "road-vertical")
        case [.left, .right]:
            image = UIImage(named: "road-horizontal")
        default:
            image = UIImage(named: "road")
        }
    }
}

37 Adding the current locale to cache keys

🌍 When caching localized content in an app, it's a good idea to add the current locale to all keys, to prevent bugs when switching languages.

func cache(_ content: Content, forKey key: String) throws {
    let data = try wrap(content) as Data
    let key = localize(key: key)
    try storage.store(data, forKey: key)
}

func loadCachedContent(forKey key: String) -> Content? {
    let key = localize(key: key)
    let data = storage.loadData(forKey: key)
    return data.flatMap { try? unbox(data: $0) }
}

private func localize(key: String) -> String {
    return key + "-" + Bundle.main.preferredLocalizations[0]
}

36 Setting up tests to avoid retain cycles with weak references

🚳 Here's an easy way to setup a test to avoid accidental retain cycles with object relationships (like weak delegates & observers) in Swift:

func testDelegateNotRetained() {
    // Assign the delegate (weak) and also retain it using a local var
    var delegate: Delegate? = DelegateMock()
    controller.delegate = delegate
    XCTAssertNotNil(controller.delegate)
    
    // Release the local var, which should also release the weak reference
    delegate = nil
    XCTAssertNil(controller.delegate)
}

35 Expressively matching a value against a list of candidates

👨‍🔬 Playing around with an expressive way to check if a value matches any of a list of candidates in Swift:

// Instead of multiple conditions like this:

if string == "One" || string == "Two" || string == "Three" {

}

// You can now do:

if string == any(of: "One", "Two", "Three") {

}

You can find a gist with the implementation here.

34 Organizing code using extensions

👪 APIs in a Swift extension automatically inherit its access control level, making it a neat way to organize public, internal & private APIs.

public extension Animation {
    init(textureNamed textureName: String) {
        frames = [Texture(name: textureName)]
    }
    
    init(texturesNamed textureNames: [String], frameDuration: TimeInterval = 1) {
        frames = textureNames.map(Texture.init)
        self.frameDuration = frameDuration
    }
    
    init(image: Image) {
        frames = [Texture(image: image)]
    }
}

internal extension Animation {
    func loadFrameImages() -> [Image] {
        return frames.map { $0.loadImageIfNeeded() }
    }
}

33 Using map to transform an optional into a Result type

🗺 Using map you can transform an optional value into an optional Result type by simply passing in the enum case.

enum Result<Value> {
    case value(Value)
    case error(Error)
}

class Promise<Value> {
    private var result: Result<Value>?
    
    init(value: Value? = nil) {
        result = value.map(Result.value)
    }
}

32 Assigning to self in struct initializers

👌 It's so nice that you can assign directly to self in struct initializers in Swift. Very useful when adding conformance to protocols.

extension Bool: AnswerConvertible {
    public init(input: String) throws {
        switch input.lowercased() {
        case "y", "yes", "👍":
            self = true
        default:
            self = false
        }
    }
}

31 Recursively calling closures as inline functions

☎️ Defining Swift closures as inline functions enables you to recursively call them, which is super useful in things like custom sequences.

class Database {
    func records(matching query: Query) -> AnySequence<Record> {
        var recordIterator = loadRecords().makeIterator()
        
        func iterate() -> Record? {
            guard let nextRecord = recordIterator.next() else {
                return nil
            }
            
            guard nextRecord.matches(query) else {
                // Since the closure is an inline function, it can be recursively called,
                // in this case in order to advance to the next item.
                return iterate()
            }
            
            return nextRecord
        }
        
        // AnySequence/AnyIterator are part of the standard library and provide an easy way
        // to define custom sequences using closures.
        return AnySequence { AnyIterator(iterate) }
    }
}

Rob Napier points out that using the above might cause crashes if used on a large databaset, since Swift has no guaranteed Tail Call Optimization (TCO).

Slava Pestov also points out that another benefit of inline functions vs closures is that they can have their own generic parameter list.

30 Passing self to required Objective-C dependencies

🏖 Using lazy properties in Swift, you can pass self to required Objective-C dependencies without having to use force-unwrapped optionals.

class DataLoader: NSObject {
    lazy var urlSession: URLSession = self.makeURLSession()
    
    private func makeURLSession() -> URLSession {
        return URLSession(configuration: .default, delegate: self, delegateQueue: .main)
    }
}

class Renderer {
    lazy var displayLink: CADisplayLink = self.makeDisplayLink()
    
    private func makeDisplayLink() -> CADisplayLink {
        return CADisplayLink(target: self, selector: #selector(screenDidRefresh))
    }
}

29 Making weak or lazy properties readonly

👓 If you have a property in Swift that needs to be weak or lazy, you can still make it readonly by using private(set).

class Node {
    private(set) weak var parent: Node?
    private(set) lazy var children = [Node]()

    func add(child: Node) {
        children.append(child)
        child.parent = self
    }
}

28 Defining static URLs using string literals

🌏 Tired of using URL(string: "url")! for static URLs? Make URL conform to ExpressibleByStringLiteral and you can now simply use "url" instead.

extension URL: ExpressibleByStringLiteral {
    // By using 'StaticString' we disable string interpolation, for safety
    public init(stringLiteral value: StaticString) {
        self = URL(string: "\(value)").require(hint: "Invalid URL string literal: \(value)")
    }
}

// We can now define URLs using static string literals 🎉
let url: URL = "https://www.swiftbysundell.com"
let task = URLSession.shared.dataTask(with: "https://www.swiftbysundell.com")

// In Swift 3 or earlier, you also have to implement 2 additional initializers
extension URL {
    public init(extendedGraphemeClusterLiteral value: StaticString) {
        self.init(stringLiteral: value)
    }

    public init(unicodeScalarLiteral value: StaticString) {
        self.init(stringLiteral: value)
    }
}

To find the extension that adds the require() method on Optional that I use above, check out Require.

27 Manipulating points, sizes and frames using math operators

✚ I'm always careful with operator overloading, but for manipulating things like sizes, points & frames I find them super useful.

extension CGSize {
    static func *(lhs: CGSize, rhs: CGFloat) -> CGSize {
        return CGSize(width: lhs.width * rhs, height: lhs.height * rhs)
    }
}

button.frame.size = image.size * 2

If you like the above idea, check out CGOperators, which contains math operator overloads for all Core Graphics' vector types.

26 Using closure types in generic constraints

🔗 You can use closure types in generic constraints in Swift. Enables nice APIs for handling sequences of closures.

extension Sequence where Element == () -> Void {
    func callAll() {
        forEach { $0() }
    }
}

extension Sequence where Element == () -> String {
    func joinedResults(separator: String) -> String {
        return map { $0() }.joined(separator: separator)
    }
}

callbacks.callAll()
let names = nameProviders.joinedResults(separator: ", ")

(If you're using Swift 3, you have to change Element to Iterator.Element)

25 Using associated enum values to avoid state-specific optionals

🎉 Using associated enum values is a super nice way to encapsulate mutually exclusive state info (and avoiding state-specific optionals).

// BEFORE: Lots of state-specific, optional properties

class Player {
    var isWaitingForMatchMaking: Bool
    var invitingUser: User?
    var numberOfLives: Int
    var playerDefeatedBy: Player?
    var roundDefeatedIn: Int?
}

// AFTER: All state-specific information is encapsulated in enum cases

class Player {
    enum State {
        case waitingForMatchMaking
        case waitingForInviteResponse(from: User)
        case active(numberOfLives: Int)
        case defeated(by: Player, roundNumber: Int)
    }
    
    var state: State
}

24 Using enums for async result types

👍 I really like using enums for all async result types, even boolean ones. Self-documenting, and makes the call site a lot nicer to read too!

protocol PushNotificationService {
    // Before
    func enablePushNotifications(completionHandler: @escaping (Bool) -> Void)
    
    // After
    func enablePushNotifications(completionHandler: @escaping (PushNotificationStatus) -> Void)
}

enum PushNotificationStatus {
    case enabled
    case disabled
}

service.enablePushNotifications { status in
    if status == .enabled {
        enableNotificationsButton.removeFromSuperview()
    }
}

23 Working on async code in a playground

🏃 Want to work on your async code in a Swift Playground? Just set needsIndefiniteExecution to true to keep it running:

import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
    let greeting = "Hello after 3 seconds"
    print(greeting)
}

To stop the playground from executing, simply call PlaygroundPage.current.finishExecution().

22 Overriding self with a weak reference

💦 Avoid memory leaks when accidentially refering to self in closures by overriding it locally with a weak reference:

Swift >= 4.2

dataLoader.loadData(from: url) { [weak self] result in
    guard let self = self else { 
        return 
    }

    self.cache(result)
    
    ...

Swift < 4.2

dataLoader.loadData(from: url) { [weak self] result in
    guard let `self` = self else {
        return
    }

    self.cache(result)
    
    ...

Note that the reason the above currently works is because of a compiler bug (which I hope gets turned into a properly supported feature soon).

21 Using DispatchWorkItem

🕓 Using dispatch work items you can easily cancel a delayed asynchronous GCD task if you no longer need it:

let workItem = DispatchWorkItem {
    // Your async code goes in here
}

// Execute the work item after 1 second
DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: workItem)

// You can cancel the work item if you no longer need it
workItem.cancel()

20 Combining a sequence of functions

➕ While working on a new Swift developer tool (to be open sourced soon 😉), I came up with a pretty neat way of organizing its sequence of operations, by combining their functions into a closure:

internal func +<A, B, C>(lhs: @escaping (A) throws -> B,
                         rhs: @escaping (B) throws -> C) -> (A) throws -> C {
    return { try rhs(lhs($0)) }
}

public func run() throws {
    try (determineTarget + build + analyze + output)()
}

If you're familiar with the functional programming world, you might know the above technique as the pipe operator (thanks to Alexey Demedreckiy for pointing this out!)

19 Chaining optionals with map() and flatMap()

🗺 Using map() and flatMap() on optionals you can chain multiple operations without having to use lengthy if lets or guards:

// BEFORE

guard let string = argument(at: 1) else {
    return
}

guard let url = URL(string: string) else {
    return
}

handle(url)

// AFTER

argument(at: 1).flatMap(URL.init).map(handle)

18 Using self-executing closures for lazy properties

🚀 Using self-executing closures is a great way to encapsulate lazy property initialization:

class StoreViewController: UIViewController {
    private lazy var collectionView: UICollectionView = {
        let layout = UICollectionViewFlowLayout()
        let view = UICollectionView(frame: self.view.bounds, collectionViewLayout: layout)
        view.delegate = self
        view.dataSource = self
        return view
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.addSubview(collectionView)
    }
}

17 Speeding up Swift package tests

⚡️ You can speed up your Swift package tests using the --parallel flag. For Marathon, the tests execute 3 times faster that way!

swift test --parallel

16 Avoiding mocking UserDefaults

🛠 Struggling with mocking UserDefaults in a test? The good news is: you don't need mocking - just create a real instance:

class LoginTests: XCTestCase {
    private var userDefaults: UserDefaults!
    private var manager: LoginManager!
    
    override func setUp() {
        super.setup()
        
        userDefaults = UserDefaults(suiteName: #file)
        userDefaults.removePersistentDomain(forName: #file)
        
        manager = LoginManager(userDefaults: userDefaults)
    }
}

15 Using variadic parameters

👍 Using variadic parameters in Swift, you can create some really nice APIs that take a list of objects without having to use an array:

extension Canvas {
    func add(_ shapes: Shape...) {
        shapes.forEach(add)
    }
}

let circle = Circle(center: CGPoint(x: 5, y: 5), radius: 5)
let lineA = Line(start: .zero, end: CGPoint(x: 10, y: 10))
let lineB = Line(start: CGPoint(x: 0, y: 10), end: CGPoint(x: 10, y: 0))

let canvas = Canvas()
canvas.add(circle, lineA, lineB)
canvas.render()

14 Referring to enum cases with associated values as closures

😮 Just like you can refer to a Swift function as a closure, you can do the same thing with enum cases with associated values:

enum UnboxPath {
    case key(String)
    case keyPath(String)
}

struct UserSchema {
    static let name = key("name")
    static let age = key("age")
    static let posts = key("posts")
    
    private static let key = UnboxPath.key
}

13 Using the === operator to compare objects by instance

📈 The === operator lets you check if two objects are the same instance. Very useful when verifying that an array contains an instance in a test:

protocol InstanceEquatable: class, Equatable {}

extension InstanceEquatable {
    static func ==(lhs: Self, rhs: Self) -> Bool {
        return lhs === rhs
    }
}

extension Enemy: InstanceEquatable {}

func testDestroyingEnemy() {
    player.attack(enemy)
    XCTAssertTrue(player.destroyedEnemies.contains(enemy))
}

12 Calling initializers with dot syntax and passing them as closures

😎 Cool thing about Swift initializers: you can call them using dot syntax and pass them as closures! Perfect for mocking dates in tests.

class Logger {
    private let storage: LogStorage
    private let dateProvider: () -> Date
    
    init(storage: LogStorage = .init(), dateProvider: @escaping () -> Date = Date.init) {
        self.storage = storage
        self.dateProvider = dateProvider
    }
    
    func log(event: Event) {
        storage.store(event: event, date: dateProvider())
    }
}

11 Structuring UI tests as extensions on XCUIApplication

📱 Most of my UI testing logic is now categories on XCUIApplication. Makes the test cases really easy to read:

func testLoggingInAndOut() {
    XCTAssertFalse(app.userIsLoggedIn)
    
    app.launch()
    app.login()
    XCTAssertTrue(app.userIsLoggedIn)
    
    app.logout()
    XCTAssertFalse(app.userIsLoggedIn)
}

func testDisplayingCategories() {
    XCTAssertFalse(app.isDisplayingCategories)
    
    app.launch()
    app.login()
    app.goToCategories()
    XCTAssertTrue(app.isDisplayingCategories)
}

10 Avoiding default cases in switch statements

🙂 It’s a good idea to avoid “default” cases when switching on Swift enums - it’ll “force you” to update your logic when a new case is added:

enum State {
    case loggedIn
    case loggedOut
    case onboarding
}

func handle(_ state: State) {
    switch state {
    case .loggedIn:
        showMainUI()
    case .loggedOut:
        showLoginUI()
    // Compiler error: Switch must be exhaustive
    }
}

9 Using the guard statement in many different scopes

💂 It's really cool that you can use Swift's 'guard' statement to exit out of pretty much any scope, not only return from functions:

// You can use the 'guard' statement to...

for string in strings {
    // ...continue an iteration
    guard shouldProcess(string) else {
        continue
    }
    
    // ...or break it
    guard !shouldBreak(for: string) else {
        break
    }
    
    // ...or return
    guard !shouldReturn(for: string) else {
        return
    }
    
    // ..or throw an error
    guard string.isValid else {
        throw StringError.invalid(string)
    }
    
    // ...or exit the program
    guard !shouldExit(for: string) else {
        exit(1)
    }
}

8 Passing functions & operators as closures

❤️ Love how you can pass functions & operators as closures in Swift. For example, it makes the syntax for sorting arrays really nice!

let array = [3, 9, 1, 4, 6, 2]
let sorted = array.sorted(by: <)

7 Using #function for UserDefaults key consistency

🗝 Here's a neat little trick I use to get UserDefault key consistency in Swift (#function expands to the property name in getters/setters). Just remember to write a good suite of tests that'll guard you against bugs when changing property names.

extension UserDefaults {
    var onboardingCompleted: Bool {
        get { return bool(forKey: #function) }
        set { set(newValue, forKey: #function) }
    }
}

6 Using a name already taken by the standard library

📛 Want to use a name already taken by the standard library for a nested type? No problem - just use Swift. to disambiguate:

extension Command {
    enum Error: Swift.Error {
        case missing
        case invalid(String)
    }
}

5 Using Wrap to implement Equatable

📦 Playing around with using Wrap to implement Equatable for any type, primarily for testing:

protocol AutoEquatable: Equatable {}

extension AutoEquatable {
    static func ==(lhs: Self, rhs: Self) -> Bool {
        let lhsData = try! wrap(lhs) as Data
        let rhsData = try! wrap(rhs) as Data
        return lhsData == rhsData
    }
}

4 Using typealiases to reduce the length of method signatures

📏 One thing that I find really useful in Swift is to use typealiases to reduce the length of method signatures in generic types:

public class PathFinder<Object: PathFinderObject> {
    public typealias Map = Object.Map
    public typealias Node = Map.Node
    public typealias Path = PathFinderPath<Object>
    
    public static func possiblePaths(for object: Object, at rootNode: Node, on map: Map) -> Path.Sequence {
        return .init(object: object, rootNode: rootNode, map: map)
    }
}

3 Referencing either external or internal parameter name when writing docs

📖 You can reference either the external or internal parameter label when writing Swift docs - and they get parsed the same:

// EITHER:

class Foo {
    /**
    *   - parameter string: A string
    */
    func bar(with string: String) {}
}

// OR:

class Foo {
    /**
    *   - parameter with: A string
    */
    func bar(with string: String) {}
}

2 Using auto closures

👍 Finding more and more uses for auto closures in Swift. Can enable some pretty nice APIs:

extension Dictionary {
    mutating func value(for key: Key, orAdd valueClosure: @autoclosure () -> Value) -> Value {
        if let value = self[key] {
            return value
        }
        
        let value = valueClosure()
        self[key] = value
        return value
    }
}

1 Namespacing with nested types

🚀 I’ve started to become a really big fan of nested types in Swift. Love the additional namespacing it gives you!

public struct Map {
    public struct Model {
        public let size: Size
        public let theme: Theme
        public var terrain: [Position : Terrain.Model]
        public var units: [Position : Unit.Model]
        public var buildings: [Position : Building.Model]
    }
    
    public enum Direction {
        case up
        case right
        case down
        case left
    }
    
    public struct Position {
        public var x: Int
        public var y: Int
    }
    
    public enum Size: String {
        case small = "S"
        case medium = "M"
        case large = "L"
        case extraLarge = "XL"
    }
}

Download Details:

Author: JohnSundell
Source Code: https://github.com/JohnSundell/SwiftTips 
License: MIT license

#swift #tips #tricks