Learn Swift and iOS Development
Master iOS development through in-depth tutorials and comprehensive courses on Swift, SwiftUI, UIKit, Core Data, and more.
Master iOS development through in-depth tutorials and comprehensive courses on Swift, SwiftUI, UIKit, Core Data, and more.
Six months after your AI-assisted migration, the app is stable. The crashes are fixed. Production is humming along. But your development velocity has slowed to a crawl. Every new feature takes longer to implement than it should. Your team complains that the code "feels wrong."
in Programming
The app launches perfectly. The test suite shows all green. Your code review finds nothing alarming. Three days after shipping to production, users start reporting that their carefully typed messages are getting truncated at seemingly random points. This is the invisible danger of AI-assisted migrations: the code compiles and runs successfully most of the time, but bugs surface with specific data that exposes semantic mismatches between how Objective-C and Swift handle runtime behavior.
in Programming
Picture this scenario: a freshly migrated app is in production, users are happy, and then the crash reports start rolling in. Not sporadically. Consistently. Every user who tries to view their profile is welcomed with a crash.
in Programming
6:53
In the previous episode, we used the adapter pattern to make the integration of the project with the Google SDK testable. In this episode, we put the theory to the test. We write unit tests for the GoogleAnalyticsService class to validate the integration of the project with the Google SDK.
in Programming
8:23
In the previous episodes, we unit tested the analytics library we built. The integration with the Google SDK is an aspect of the implementation we haven't talked about in detail. We shouldn't unit test the Google SDK itself, but can we unit test the integration with the project? That is the question we focus on in this episode.
in Programming
5:30
In the previous episodes, we used code coverage to help us write unit tests for the analytics library we built. This worked fine, but code coverage isn't perfect. Code coverage inspects which lines of code are executed when the test suite is run. The result may be misleading or incorrect at times. In this episode, we take a look at an important shortcoming of code coverage.
in Programming
9:39
In the previous episode, we wrote the first unit test for the analytics library we built. In this episode, we write a few more unit tests and take advantage of the compiler to find gaps in the test suite.
in Programming
9:49
The analytics library we built in this series is easy to use thanks to its intuitive API. The call site is no longer littered with string literals and the API the analytics library exposes is type safe. We don't stop here, though. I promised you that the analytics library would be testable. Let's focus on that aspect in the next few episodes.
in Programming
9:50
The analytics library we are building is tightly coupled to the GoogleAnalyticsClient class. Tight coupling negatively impacts testability and it is often an indication that there is room for improvement. In this episode, we decouple the analytics library from the GoogleAnalyticsClient class by taking a protocol-oriented approach. Let me show you how that works.
in Programming
5:30
We drastically improved the analytics code of the NotesViewModel class in the past few episodes, but we still use a few string literals at the call site. That is something we address in this episode.
in Programming
While I don't typically write reviews on Cocoacasts, the Logitech MX Master 3S deserves one as it has been a loyal companion of mine for many years. Like most programmers, I'm picky about the equipment I work with. Ideally, the tools I work with should be invisible, allowing me to focus on getting work done. In this post, I hope I can convince you that the Logitech MX Master 3S is the best mouse for programmers like myself.
in Programming
6:22
We still use string literals to define the properties of an event. That is something I would like to change in this episode. The Journey enum defines the list of events the API supports. We take a similar approach for the properties of an event.
in Programming
8:21
In this episode, we focus on the analytics API itself. We define a single entry point for analytics and remove some of the string literals at the call site. The resulting API is surprisingly small, intuitive to use, and easy to extend.
in Programming
6:31
Swift has a lot to offer, but it isn't always obvious how to get the most out of the language. I often see developers compromise the APIs they build by using stringly typed code while Swift provides features that make stringly typed code unnecessary. In this series, I show you how a handful of simple patterns and techniques can help you build APIs that are type-safe, elegant, and intuitive to use.
in Programming
8:02
The API the image service exposes no longer accepts a completion handler. It returns a publisher instead. This is an improvement, but the image service still uses completion handlers under the hood. In this episode, we replace the completion handlers the image service uses internally with publishers.
in Programming
9:13
In the previous episode, you learned about futures and promises. In this episode, I show you how to use them in a project.
in Programming
11:04
Earlier in this series, you learned how a subject can bridge the gap between imperative programming and reactive programming. While subjects are convenient in many ways, they are not always the best option. The Combine framework provides another option. In this episode, we zoom in on futures and promises to bridge the gap between imperative programming and reactive programming.
in Programming
9:30
Infinite loops are every developer's nightmare, especially if they have disastrous consequences. You need to be mindful of infinite loops when working with Combine or any other reactive framework. The difficulty is that it isn't always obvious that you created an infinite loop. As a matter of fact, we introduced an infinite loop earlier in this series. You receive a bonus point if you can spot it.
in Programming
7:35
Cloudy shows the user an error if it isn't able to fetch weather data from the weather API. We map any errors that are thrown to WeatherDataError in the RootViewModel class. This solution works fine and it is a fitting implementation for the weather application we are building. In this episode, I show you three other options to handle errors.
in Programming
6:48
Even though we successfully used the Combine framework to fetch data from the weather API, the implementation is incomplete. We ignored error handling up until now and it is time to take a closer look at errors and how to handle them.
in Programming
7:25
Earlier in this series, you learned that reactive programming can be defined as working with asynchronous streams of data. A network request is an asynchronous operation and that makes reactive programming an excellent match for networking.
in Programming
11:08
In this episode, we fix the settings view using the Combine framework. We no longer rely on the delegation pattern to propagate settings changes. In the previous episode, we created a view model for the settings view controller. The settings view model exposes a publisher for each setting. Objects interested in settings changes can subscribe to these publishers. Let me show you how that works.
in Programming
12:39
Earlier in this series, we broke the settings view. The root view controller acts as the delegate of the settings view controller, but nothing happens when the user updates a setting in the settings view. In this and the next episode, we fix what we broke and reactify the application's settings view.
in Programming
8:53
In 2019, Apple introduced another powerful API alongside the Combine framework, diffable data sources. Diffable data sources make it almost trivial to build performant table and collection views. In this episode, I show you why diffable data sources work well with the Combine framework. I won't cover diffable data sources in detail in this series, but the API is straightforward and easy to pick up.
in Programming
8:32
In this and the next episode, we shift focus to the week view controller. Populating the week view requires a different approach. It contains a table view and the week view model manages an array of WeahterDayData objects. Even though the approach is different, the patterns we apply are similar.
in Programming
11:07
The Combine framework defines a range of operators to combine publishers. Combining publishers is a common pattern in reactive programming. In this episode, we improve the efficiency of the RootViewModel class by combining multiple publishers using the zip and combineLatest operators.
in Programming
8:35
We ran into several issues in the previous episode. The assign(to:on) and sink(receiveValue:) methods are convenient, but they don't always cut it. We need a solution that is robust and scales with the complexity of the project.
in Programming
7:07
In this episode, we optimize the implementation of the day view controller and the day view model. There are a few details we need to take care of. First, the day view controller should display an error message when its view model emits an error. Second, the day view controller should only display its activity indicator view if it has no weather data to display. Let's tackle these problems one by one.
in Programming
8:50
There are a few user interface issues we need to address in this episode. The day view controller no longer shows its activity indicator view while it is waiting for weather data and the user interface elements that show the weather data should only be shown when there is weather data to display. Let's find out how we can resolve these issues.
in Programming
7:42
In the previous episode, we put the foundation in place to reactify the DayViewController class. In this episode, you learn how to use publishers to drive the user interface of an application.
in Programming
8:36
The root view controller displays the weather data the publisher emits. Every time the publisher emits weather data, the child view controllers of the root view controller receive the weather data and display it to the user. This works fine, but we can make it more reactive.
in Programming
6:27
We made good progress in the previous episodes, but we need to make some changes to the RootViewController and RootViewModel classes. We address three issues in this episode. First, the RootViewController class shouldn't be aware of CLLocation objects. Second, the RootViewModel class should expose a publisher that emits weather data. Third, we need to restore the pull-to-refresh feature. Let's get started.
in Programming
7:33
The Published property wrapper lowers the barrier to start integrating the Combine framework into a project. There are a few details you need to take into account, though. Remember from the previous episode that we need to address two issues. Let's look at the first issue.
in Programming
8:01
The Cocoa frameworks use a range of asynchronous interfaces, including the target-action pattern, key-value observing, notifications, and callbacks. We can leverage the Combine framework to create a single, unified interface for asynchronous programming. This opens up a number of compelling advantages.
in Programming
6:45
You learned in the previous episode that a publisher sends zero or more values. A publisher emits an error if something goes wrong and, if the publisher is finite, it can send a completion event. In this episode, we continue exploring the relationship between publishers and subscribers. We zoom in on the life cycle of a subscription.
in Programming
8:17
Streams of values are at the heart of reactive programming. These values are delivered by publishers. In this episode, we zoom in on the relationship between publishers and subscribers.
in Programming
5:48
We haven't simplified or improved the implementation of the setupNotificationHandling() method of the RootViewModel class by adding Combine to the mix. In fact, Combine introduced additional complexity and we now need to manage the subscriptions we create. It is true that Combine comes with a bit of overhead. The traditional Cocoa API seems to be the better choice. Right?
in Programming
4:24
In the previous episode, you learned about publishers and subscribers. Combine creates a subscription when a subscriber is attached to a publisher. In the setupNotificationHandling() method of the RootViewModel class, the subscription is returned to the view model as an AnyCancellable instance. The view model holds on to the AnyCancellable instance to prevent the subscription from terminating early. We covered this in the previous episode.
in Programming
7:51
It is time to show you what Combine feels like and what it can do for you. What do you gain by using the Combine framework in your projects? Let's start with some good news. The Combine framework has a relatively small vocabulary. You don't need to spend days or weeks familiarizing yourself with a plethora of types, protocols, and terminology.
in Programming
4:17
Earlier in this series, we defined reactive programming as working with asynchronous streams of data. Now that you know what asynchronous streams of data are, it is clear why asynchronous programming is such a powerful concept.
in Programming
6:51
In the previous episode, you learned about function types and what it means for Swift to have first-class functions. With these concepts in mind, we continue exploring the functional features of the Swift programming language. In this episode, we explore pure functions and higher-order functions. We start with pure functions.
in Programming
6:49
Swift isn't a functional programming language, but it does have a number of functional features. The Combine framework relies on these functional features so it is important that you understand the basics of functional programming. That is the focus of this and the next episode.
in Programming
9:53
Combine is sometimes referred to as a functional reactive programming framework, but that isn't correct. It is more accurate to describe Combine as a reactive programming framework that uses functional programming techniques.
in Programming
3:12
Welcome to Building Reactive Applications With Combine. As the name suggests, this series zooms in on Apple's Combine framework. We cover the the ins and outs of the framework and you learn everything you need to know to make your projects reactive with the Combine framework.
in Programming
7:51
The past episodes have illustrated that creating a compositional layout isn't difficult or complex. The API of the UICollectionViewCompositionalLayout class is intuitive and flexible. In this episode, we revisit the feed view controller one more time. The plan is to display a title at the top of the feed view controller's collection view using a supplementary view.
in Programming
9:09
The previous episode focused on the basics of compositional layouts. A key feature of compositional layouts is the simple and readable API. The code we wrote in the previous episode isn't complex or difficult to understand. Another nice bonus is that we didn't need to subclass the UICollectionViewCompositionalLayout class.
in Programming
16:22
Building modern user interfaces can be challenging if you solely rely on traditional Cocoa APIs. Apple is aware of this limitation and introduced a number of brand new APIs to build modern, performant user interfaces.
in Programming
11:35
In this episode, we use the MockClient class to unit test the FeedViewModel class. The unit tests for the FeedViewModel class will change as the project evolves. The primary goal of this episode is to show how the MockClient class helps us write unit tests for the FeedViewModel class.
in Programming
7:09
In the previous episode, we added the ability to define how the MockClient class responds. This added flexibility makes the MockClient class much more useful. In the next episode, I show you how we can use the MockClient class to unit test the FeedViewModel class.
in Programming
6:01
The MockClient class fetches its mock data from the application bundle. This works fine, but there are a few limitations. The most important limitation is the lack of flexibility. With the current setup, we can only test the happy path, that is, the application showing the user a list of episodes.
in Programming
9:12
Using a local server is a convenient solution during development because it makes it straightforward to set up a development environment. It also allows for flexibility, making it easy to tweak the implementation of the backend as you develop the client.
in Programming
15:27
The collection view of the feed view controller now displays episode images. We are almost ready to build the user interface of the collection view and style the EpisodeCollectionViewCell class. Before we start with that task, I want to create a framework for styling the application. That is the focus of this episode.
in Programming
14:41
The Cocoacasts API returns a remote image for each episode. The problem we face is that the images are in the SVG format. SVG images are ideal for web, but the UIKit framework doesn't know how to handle the SVG format. The solution we implement in this episode uses Cloudinary, a service that manipulates and transforms media, including images. We also add Kingfisher to the mix. Kingfisher is a popular image library to download and cache remote images.
in Programming
6:34
Before we continue populating the user interface of the feed view controller, we need to resolve an issue we introduced in the previous episode. The feed view controller asks its view model for an Episode object every time it needs to configure an episode collection view cell. The project adopts the Model-View-ViewModel pattern, which means that the view controller should not have direct access to model objects. In this episode, I show you a simple solution to hide the Episode object from the feed view controller.
in Programming
14:49
We added quite a bit of code to the project in the past episodes and yet the user interface hasn't changed. It's time to use the data from the Cocoacasts API to populate the feed view controller's view.
in Programming
9:44
The playground has been useful to create a prototype of the APIClient class. It's time to integrate the Episode struct and the APIClient class into the project. That is the focus of this episode.
in Programming
9:16
We successfully fetched a list of episodes from the Cocoacasts API and converted the data into model objects. The code we wrote works, but it is far from finished. In this episode, we create a dedicated object that manages the communication with the Cocoacasts API.
in Programming
10:24
The first episodes of this series have almost exclusively focused on creating a foundation for the project. While this may seem tedious or even premature, it allows anyone working on the project to focus on writing code. Automation is an important part of modern software development and it pays to set aside some time to create a foundation for the project you're working on.
in Programming
13:15
In this episode, we adopt the coordinator pattern. I won't cover the details of the coordinator pattern in this episode, though. Coordinators are covered in detail in Mastering Navigation With Coordinators.
in Programming
7:54
In the previous episode, I showed you the setup I use to log messages with CocoaLumberjack. There's a lot more CocoaLumberjack has to offer. In this episode, you learn how to forward log messages to a remote server. I show you how to integrate CocoaLumberjack and PaperTrail, a popular solution for managing logs. We also take a look at an alternative approach, that is, how to send log messages to Fabric. Logs can be very helpful in combination with crash reporting.
in Programming
10:39
Logging is an integral part of my debugging workflow and it's therefore something I invest time in to get right. Swift's print(_:separator:terminator:) function works fine, but it isn't ideal for debugging issues. To use logging effectively in a debugging workflow, you need a powerful and flexible solution.
in Programming
12:43
Every project I work on makes use of view controller containment. This isn't surprising since several UIKit components take advantage of this pattern, including navigation controllers, tab bar controllers, and split view controllers. But I'm not only referring to the components UIKit offers out of the box.
in Programming
15:54
Every Xcode project starts out with two build configurations, Debug and Release. The names of these build configurations speak for themselves. The Debug configuration is used to create debug or development builds. Builds destined for TestFlight and App Store are built using the Release configuration. Having a Debug and a Release configuration is sufficient for some projects. Most setups require more flexibility, though.
in Programming
13:26
Most developers don't take pleasure in resolving code signing issues and that includes me. While managing certificates and provisioning profiles has become easier over the years, it can still be challenging. If you're working in a team or use a build server to create and distribute builds, then code signing can become complex.
in Programming
10:30
Even though Xcode makes it simple to set up a project, there's more to it than choosing a template and clicking a few buttons. The workflow I use to set up a new project is straightforward and easy to follow. In this episode, I show you what it looks like.
in Programming
6:53
From Zero to App Store won't focus on running a business on Apple's platforms, but it touches on a few ideas and concepts that relate to business. This episode focuses on defining the first version of the product. In the startup community, this is often referred to as defining the minimum viable product or MVP for short.
in Programming
3:31
From Zero to App Store is a series that journals the development of Cocoacasts for iOS and tvOS. With this series, I aim to offer a look inside the workflows and processes I use to build and ship software. This series aims to touch every aspect of mobile software development, including source control, continuous integration, release management, and App Store deployment. It goes well beyond writing code.
in Programming
As an employee, freelancer, or consultant, you inevitably end up with a foreign codebase on your plate at some point in your career. In a way, inheriting a software project is much like receiving the keys to a house or car you don't know anything about. You hold your breath, afraid for what is about to come. But you are also a little excited, curious to find out what you will be working on for the next weeks, months, or longer.
in Programming
In this post, I would like to zoom in on pure functions and impure functions in Swift. These concepts may sound exotic, especially if you are unfamiliar with functional programming. I promise you that these concepts aren't as daunting as they seem.
in Programming