Separation of concerns is a core principle when it comes to designing architectures and systems that are easy to maintain. It's the idea that each object or type should only know enough about its surroundings to do its work, and no more.

However, even though it's a principle most programmers learn about early in their career, it's not always very easy to apply in practice. This week, let's take a look at how to more easily separate the concerns of various types in Swift using protocols.

In previous posts, we've taken a look at a few different ways to use dependency injection to achieve a more decoupled and testable architecture in Swift apps. So far, most of my posts and examples have used initializer-based dependency injection. However, just like with most programming techniques, there are multiple "flavors" of dependency injection - each with its own pros & cons. This week, let's take a look at three such flavors and how they can be used in Swift.

UI testing can be a really useful tool in order to assure that the key user interactions of your app work as expected. Maybe even more importantly though, it's a great way to add an extra level of protection against regressions as you are continuously changing and refactoring your code base.

This week, let's take a look at how to add UI tests to verify an app's analytics code, and how it can be a great way to quickly gain a broad UI testing coverage.

Gathering some form of analytics from your users is super important when continuously building, iterating on and improving a product. Learning how your users use your app in real life situations can sometimes be really surprising and take its development in new directions or act as inspiration for new features.

This week, let's take a look at how an analytics system can be architected and implemented, based on one of my favorite Swift features - enums!

Managing memory and avoiding leaks is a crucial part of building any kind of program. Thankfully Swift makes this relatively easy in most situations - thanks to Automatic Reference Counting (or ARC for short). However, there are still times when it can be quite easy to make a mistake that causes memory to be leaked.

This week, let's take a look at how we can set up unit tests to both help us identify memory leaks, and also make it easier to avoid common mistakes that could end up causing leaks in the future.

A very common problem when building apps for Apple's platforms is where to put common functionality that is used by many different view controllers. On one hand we want to avoid code duplication as much as possible, and on the other hand we want to have a nice separation of concerns to avoid the dreaded Massive View Controller.

Rather than using a BaseViewController or relying on inheritance in some other way, let's take a look at how we can structure common functionality as child view controllers that can be used as plugins.

While force unwrapping is an important Swift feature that would be hard to work without (especially when interacting with Objective-C APIs), it also circumvents some of the other features that make Swift so great.

However, dealing with optionals and unknown types in a safe way can require quite a lot of code, so the question is whether we want to do all that additional work when writing tests as well? That is what we'll take a look at this week - let's dive in!

Dependency injection is an essential tool when it comes to making code more testable. Instead of having objects either create their own dependencies or access them as singletons, it's the idea that everything an object needs in order to do its work should be passed in from the outside.

This week, let's take a look at a dependency injection technique that lets us enable testability without forcing us to write massive initializers or complicated dependency management code.

Handling asynchronous code in a way that is predictable, readable and easy to debug can be really challenging. It's also something that is very common, almost all modern codebases have parts that are highly asynchronous - whether that's loading data over the network, processing big datasets locally or any other computationally intensive operations.

This week, let's take a look at a technique that can make managing asynchronous calls a bit simpler and less error prone - using tokens.

Ever since blocks were introduced into Objective-C as part of iOS 4 they have been an important part of most modern APIs for Apple's platforms. The convention of using blocks also carried over to Swift with closures, which is a language feature that most of us use every single day.

But even though closures are very widely used, there's a lot of behaviors and caveats to keep in mind when using them. This week, let's take a closer look at closures, how capturing works and some techniques that can make handling them easier.

We all use open source code every day. Whether it’s by directly pulling in a framework in one of our apps, or indirectly in almost all of the apps, tools and utilities that we all use to get our work done as developers. So open source is incredibly important for our industry, and it's big reason why I love the Swift community so much.

This week I thought I’d try to sum up most of my tips & tricks on how to publish a new Swift open source project.

"I know singletons are bad, but...", is something that developers often say when discussing code. There seems to almost be a consensus in the community that singletons are "bad", but at the same time both Apple and third party Swift developers keep using them both internally within apps and in shared frameworks.

This week, let's take a look at exactly what the problems are with using singletons and explore some techniques that can be used to avoid them.

Using generic type constraints, you are able to only add certain APIs and behaviors to implementors of a generic type that match a certain set of constraints. This week, let's take a look at some techniques and patterns that are made possible because of type constraints, and how they can be used in practice - focusing on some of the new capabilities that were recently introduced with Swift 3.1 & 4.

While switch statements are hardly something that was invented as part of Swift, they are made a lot more powerful when combined with Swift's type system.  This week - let's go further beyond switching on single enum values and take a look at more of the powerful capabilities that switch statements offer in Swift.

When creating collections of objects or values in Swift, we usually use data structures provided by the standard library - such as Array, Dictionary and Set. While those three cover most use cases, sometimes creating a custom wrapper collection can enable you to make your code more predictable and less prone to errors.

This week, let's take a look at how we as app developers can define such custom collections in Swift!

Assertions are not only an essential tool when writing tests - they're also super useful in order to write more predictable and easier to debug code.

This week, let's look deeper into assertions, how they work and how we can implement our own assert() functions for performing various checks.

Shared state is a really common source of bugs in most apps. It's what happens when you (accidentally or by design) have multiple parts of a system that rely on the same mutable state.

This week, let's take a look at how shared state can be avoided in many situations, by using the factory pattern to create clearly separated instances that each manage their own state.

If you have ever pushed a pixel onto the screen of an Apple device, you have used Core Animation - either directly or indirectly. 

In this new (non-consecutive) series of posts - "Core Animation Gems" - we'll take a closer look at some less widely known features and APIs, and how they can be used to solve problems related to animation and rendering in a nice way. This week, let's kick it off with the first one - CAReplicatorLayer.

An important part of maintaining any app, framework or system is dealing with legacy code. No matter how well architected a system is, legacy will always be built up over time - it can be because of changes in an underlying SDK, because of an expanded feature set, or simply because no one on the team really knows how a particular part works.

This week, let's take a look at a technique that I usually use when dealing with legacy code - that lets you replace a problematic system class by class, rather than having to do it all at once.