iOS Development

Writing code that makes errors more durable – Donny Wals

Spread the love


As we work on tasks, we often add extra code than we take away. A minimum of that’s how issues are initially of our undertaking. Whereas our undertaking grows, the wants of the codebase change, and we begin refactoring issues. One factor that’s typically fairly exhausting to get precisely proper when coding is the sorts of abstractions and design patterns we truly want. On this publish, I want to discover a mechanism that I wish to leverage to ensure my code is strong with out truly worrying an excessive amount of about abstractions and design patterns within the first place.

We’ll begin off by sketching a number of situations by which you would possibly end up questioning what to do. And even worse, situations the place you begin noticing that some issues go improper typically, on some screens. After that, we’ll have a look at how we are able to leverage Swift’s sort system and entry management to forestall ourselves from writing code that’s susceptible to containing errors.

Widespread errors in codebases

If you have a look at codebases which have grown over time with out making use of the rules that I’d like to stipulate on this publish, you’ll typically see that the codebase accommodates code duplication, plenty of if statements, some change statements right here and there, and an entire bunch of mutable values.

None of those are errors on their very own, I’d by no means, ever argue that the existence of an if assertion, change, and even code duplication is a mistake that ought to instantly be rectified.

What I am saying is that these are sometimes signs of a codebase the place it turns into simpler and simpler over time to make errors. There’s an enormous distinction there. The code itself won’t be the error; the code permits you as a developer to make errors extra simply when it’s not structured and designed to forestall errors.

Let’s check out some examples of how errors could be made too straightforward by way of code.

Errors because of code duplication

For instance, think about having a SwiftUI view that appears as follows:

struct MyView {
  @ObservedObject var viewModel: MyViewModel

  var physique: some View {
    Textual content("(viewModel.person.givenName) (viewModel.person.familyName) ((viewModel.person.electronic mail))")
  }
}

By itself, this doesn’t look too unhealthy. We simply have a view, and a view mannequin, and to current one thing to the person we seize a number of view mannequin properties and we format them properly for our person.

As soon as the app that accommodates this view grows, we would must seize the identical knowledge from a (totally different) view mannequin, and format it an identical to the way it’s formatted in different views.

Initially some copying and pasting will reduce it however sooner or later you’ll often discover that issues get out of sync. One view presents knowledge a method, and one other view presents knowledge in one other means.

You may replace this view and examine mannequin as follows to repair the potential for errors:

class MyViewModel: ObservableObject {
  // ...

  var formattedUsername: String {
    return "(person.givenName) (person.familyName) ((person.electronic mail))"
  }
}
struct MyView {
  @ObservedObject var viewModel: MyViewModel

  var physique: some View {
    Textual content(viewModel.formattedUsername)
  }
}

With this code in place, we are able to use this view mannequin in a number of locations and reuse the formatted title.

It will be even higher if we moved the formatted title onto our Consumer object:

extension Consumer {
  // ...

  var formattedUsername: String {
    return "(givenName) (familyName) ((electronic mail))"
  }
}

struct MyView {
  @ObservedObject var viewModel: MyViewModel

  var physique: some View {
    Textual content(viewModel.person.formattedUsername)
  }
}

Whereas this code permits us to simply get a formatted username wherever we’ve got entry to a person, we’re violating a precept known as the Legislation of Demeter. I’ve written about this earlier than in a publish the place I discuss free coupling so I gained’t go too in depth for now however the important thing level to recollect is that our view explicitly relies on MyViewModel which is okay. Nonetheless, by accessing person.formattedUsername on this view mannequin, our view additionally has an implicit dependency on Consumer. And never simply that, it additionally relies on view mannequin gaining access to a person object.

I’d desire to make yet one more change to this code and make it work as follows:

extension Consumer {
  // ...

  var formattedUsername: String {
    return "(givenName) (familyName) ((electronic mail))"
  }
}

class MyViewModel: ObservableObject {
  // ...

  var formattedUsername: String {
    return person.formattedUsername
  }
}
struct MyView {
  @ObservedObject var viewModel: MyViewModel

  var physique: some View {
    Textual content(viewModel.formattedUsername)
  }
}

This would possibly really feel a bit of redundant at first however when you begin taking note of maintaining your implicit dependencies in examine and also you attempt to solely entry properties on the item you rely upon with out chaining a number of accesses you’ll discover that making adjustments to your code abruptly requires much less work than it does when you’ve got implicit dependencies in all places.

One other type of code duplication can occur whenever you’re styling UI parts. For instance, you may need written some code that types a button in a selected means.

If there’s multiple place that ought to current this button, I may copy and paste it and issues might be high quality.

Nonetheless, a number of months later we would must make the button labels daring as an alternative of normal font weight and it is going to be means too straightforward to overlook one or two buttons that we forgot about. We may do a full undertaking seek for Button however that may most probably yield far more outcomes than simply the buttons that we wish to change. This makes it far too straightforward to miss a number of buttons that we must be updating.

Duplicating code or logic a few times often isn’t an enormous deal. In actual fact, typically generalizing or inserting the duplicated code someplace is extra tedious and sophisticated than it’s value. Nonetheless, when you begin to duplicate increasingly more, or whenever you’re duplicating issues which might be important to maintain in sync, you must take into account making a small and light-weight abstraction or wrapper to forestall errors.

Stopping errors associated to code duplication

At any time when you end up reaching for cmd+c in your keyboard, you must ask your self whether or not you’re about to repeat one thing that may have to be copied typically. Since none of us have the flexibility to reliably predict the longer term, this can at all times be considerably of a guess. As you acquire extra expertise within the discipline you’ll develop a way for when issues are susceptible to duplication and a great candidate to summary.

Particularly when an abstraction could be added in a easy method you shouldn’t have a really excessive tolerance for copying and pasting code.

Contemplate the view mannequin instance from earlier. We have been in a position to resolve our downside by ensuring that we thought of the suitable degree of inserting our person’s formatted title. Initially we put it on the view mannequin, however then we modified this by giving the person itself a formatted title. Permitting anywhere that has entry to our person object to seize a formatted title.

An additional advantage right here is we maintain our view mannequin as skinny as potential, and we’ve made our person object extra versatile.

Within the case of a button that should seem in a number of locations it is smart to wrap the button in a customized view. It may additionally make sense to put in writing a customized button model if that higher matches your use case.

Errors because of complicated state

Managing state is difficult. I don’t belief anyone that may argue in any other case.

It’s not unusual for code to slowly however certainly flip into a posh state machine that makes use of a handful of boolean values and a few strings to find out what the app’s present state actually is. Typically the result’s that when as soon as boolean is true, a few others should be false as a result of this system could be in a foul state in any other case.

My favourite instance of a scenario the place we’ve got a number of bits of state together with some guidelines about when this state is or isn’t legitimate is URLSession‘s callback for an information activity:

URLSession.shared.dataTask(with: url) { knowledge, response, error in
  guard error == nil else {
    // one thing went improper, deal with error
    return
  }

  guard let knowledge, let response else {
    // one thing went VERY improper
    // we've got no error, no knowledge, and no response
    return
  }

  // use knowledge and response
}

If our request fails and comes again as an error, we all know that the response and knowledge arguments should be nil and vice-versa. This can be a easy instance however I’ve seen a lot worse in code I’ve labored on. And the issue was by no means launched knowingly. It’s at all times the results of slowly however certainly rising the app and altering the necessities.

Once we design our code, we are able to repair these sorts of issues earlier than they happen. If you discover you can categorical an unattainable state in your app resulting from a progress in variables which might be supposed to work together collectively, take into account leveraging enums to characterize the states your app could be in.

That means, you considerably decrease your possibilities of writing incorrect state into your app, which your customers will take pleasure in.

For instance, Apple may have improved their URLSession instance with the Outcome sort for callbacks. Fortunately, with async / await unhealthy state can’t be represented anymore as a result of a knowledge name now returns a non-optional Information and URLResponse or throws an Error.

Errors because of not realizing the magical incantation

One final instance that I’d like to focus on is when codebases require you to name a sequence of strategies in a selected order to be sure that every part works appropriately, and all bookkeeping is carried out appropriately.

That is often the results of API design that’s considerably missing in its usability.

One instance of that is the API for including and eradicating youngster view controllers in UIKit.

If you add a baby view controller you write code that appears a bit of like this:

addChild(childViewController)
// ... some setup code ...
childViewController.didMove(toParent: self)

That doesn’t appear too unhealthy, proper.

The syntax for eradicating a baby view controller appears as follows:

childViewController.willMove(toParent: nil)
// ... some setup code ...
childViewController.removeFromParent()

The distinction right here is whether or not we name willMove or didMove on our childViewController. Not calling these strategies appropriately may end up in too few or too many view controller lifecycle occasions being despatched to your youngster view controller. Personally, I at all times overlook whether or not I must name didMove or willMove after I work with youngster view controllers as a result of I do it too occasionally to recollect.

To repair this, the API design could possibly be improved to routinely name the proper technique whenever you make a name to addChild or removeFromParent.

In your individual API design, you’ll wish to look out for conditions the place your program solely works appropriately whenever you name the suitable strategies in the suitable order. Particularly when the tactic calls ought to at all times be grouped intently collectively.

That stated, typically there’s a good cause why an API was designed the way in which it was. I believe that is the case for Apple’s view controller containment APIs for instance. We’re speculated to arrange the kid view controller’s view between the calls we’re speculated to make. However nonetheless… the API may certainly be reworked to make making errors more durable.

Designing code that helps stopping errors

If you’re writing code you must at all times be looking out for anti-patterns like copy-pasting code lots, having plenty of complicated state that permits for incorrect states to be represented, or whenever you’re writing code that has very particular necessities concerning the way it’s used.

As time goes on and also you acquire increasingly more coding expertise, you’ll discover that it will get simpler and simpler to identify potential pitfalls, and you can begin getting forward of them by fixing issues earlier than they exist.

Often because of this you spent lots of time fascinated about the way you wish to name sure bits of code.

At any time when I’m engaged on a brand new characteristic, I have a tendency to put in writing my “name website” fist. The decision website means the half the place I work together with the characteristic code that I’m about to put in writing.

For instance, if I’m constructing a SwiftUI view that’s speculated to render a listing of things which might be fetched from varied sources I’ll most likely write one thing like:

Listing(itemSource.allItems) { merchandise in 
  // ...
}

In fact, that code won’t work but however I’ll know what to goal for. Regardless of what number of knowledge sources I find yourself with, I need my Listing to be straightforward to make use of.

This technique of writing code by figuring out how I wish to use it first could be utilized to each layer of your codebase. Generally it’s going to work rather well, different occasions you’ll discover that you want to deviate out of your “ultimate” name website however it helps give attention to what issues; ensuring the code is simple to make use of.

At any time when I’m designing APIs I take into consideration this publish from Dave DeLong.

Particularly, this quote at all times stands out to me:

An amazing API is variety to all builders who work with it.

Each technique you write and each class you design has an API. And it’s a good suggestion to be sure that this API is pleasant to make use of. This contains ensuring that it’s exhausting (or ideally, unattainable) to misuse that API in addition to having good error messages and failure modes.

Transferring on from API design, in the event you’re modeling state that principally revolves round a number of booleans, take into account enums as an alternative. Even in the event you’re modeling one thing like whether or not or not a view ought to animate, an enum can assist you make your code extra readable and maintainable in the long term.

Greater than something, in the event you assume {that a} sure little bit of code feels “off”, “too complicated” or “not fairly proper”, there’s a great likelihood your instinct is appropriate. Our code ought to be as simple to grasp as potential. So each time we really feel like we’re doing the alternative, we must always appropriate that.

That’s to not say that each one complicated code is unhealthy. Or that each one repetition is unhealthy. And even that each little bit of complicated state ought to grow to be an enum. These are all simply flags that ought to stand out to you as one thing that you must take note of. Any time you may change your code a bit with a view to make it unattainable to characterize an unattainable state, or if you may make some adjustments to your code that guarantee you may’t cross unhealthy arguments to a way, that’s a win.

In Abstract

Writing good code could be actually exhausting. On this publish, I outlined a few examples of code that permits builders to make errors. There are various ways in which code can open a developer as much as errors, and these often contain code that has advanced over time, which may imply that blind spots have crept into the codebase with out the developer noticing.

By expertise, we are able to be taught to establish our blind spots early and we are able to defensively write code that anticipates change in a means that ensures our code stays secure and simple to make use of.

Total, state is the toughest factor to handle in my expertise. Modeling state in a means that permits us to characterize complicated states in a secure method is extraordinarily helpful. Subsequent time you are contemplating writing an ‘if’ assertion that compares two or extra values to find out what ought to occur, take into account writing an enum with a descriptive title and related values as an alternative.

What are some frequent coding errors that you’ve realized to establish alongside the way in which? I’d love in the event you informed me all about them on X or Threads.

Leave a Reply

Your email address will not be published. Required fields are marked *