Convenience Property Wrappers vs Custom Data Access Layer in SwiftUI

Mohammad Azam
3 min readDec 6, 2023
Photo by Ugne Vasyliute on Unsplash

Yesterday, I had the opportunity to speak at WomenWhoCode Mobile event. It was a remote event and well attended. I spoke about SwiftUI architecture best practices.

When I was covering Core Data I mentioned that you should use @FetchRequest property wrappers as they are optimized to work with SwiftUI. Same is true for @Query property wrapper in SwiftData.

During this time an interesting question was raised. An attendee asked what if you want to change the data access layer in the future. Currently our views are tightly coupled with either Core Data or SwiftData but what happens if we want to use Realm or GRDB.

I have thought about this question for a very long time. This is one of those questions, which does not have a straight forward answer or a single answer to be exact. The answer is opinion based so let’s take a look at both sides of the equation. I will use SwiftData in my examples.

If you like this article and want to support my work then check out my courses on Teachable.

Property Wrappers

Consider a scenario that we are building a simple TodoList application. We add our model TodoItem, which consists of a single property title. We add a TodoListView, which uses the @Query property wrapper to fetch and also monitor the changes in the model context of the application. In just a few lines of code we are able to display todo items on our screen. The code is listed below:

But what if few weeks later you decided that you want to replace SwiftData with Realm. And we are also assuming that you are interested in using Realm SwiftUI property wrappers in your application. This means all those @Query property wrappers will not work. Realm uses @ObservedResults property wrapper instead of @Query property wrapper. Also Realm models are not decorated with @Model macro so you need to remove that too.

If your intention is to utilize Realm, it’s crucial to note that all of your models will necessitate updating, irrespective of the approach you choose.

The TodoItemView also needs to be updated as Realm uses @ObservedRealmObject or similar instead of [TodoItem].

Ultimately, it becomes apparent that altering the data persistence framework will have a ripple effect throughout a significant portion of our application.

Custom Data Access Layer

Now, let’s see the what happens when we implement our own custom data access layer.

We will start with implementing the protocol.

Next, we will implement the concrete implementation of our data access service:

And finally, you can use the SwiftDataTodoService in your application.

This is definitely a lot more code as compared to our previous approach. We pass the TodoService as a dependency to our ContentView and then uses the saveTodoItem and getTodoItems functions to perform appropriate actions. By not using the built-in property wrappers like @FetchRequest or @Query we lost the ability of tracking changes but gained the flexibility of swapping out the data access layers when and if needed.

Final Words

When determining which option is superior, the answer is it depends. If convenience is your priority, then SwiftUI property wrappers are recommended. However, if you value flexibility to accommodate future changes, implementing a custom data access layer would be more suitable.

I pose a question for you to contemplate: When was the last instance in which you entirely swapped out your data access layers?

If you like this article and want to support my work then check out my courses on Teachable.

--

--

Mohammad Azam

Lead instructor at a coding bootcamp. Top iOS mobile instructor on Udemy. Author of multiple books and international speaker. azamsharp.school