top of page

MVVM, SwiftUI, Async/Await, and Combine: The Ultimate API Call Dream Team 🚀

Updated: Mar 25, 2023

Welcome, future SwiftUI geniuses! Today, we’re going to explore the magical world of MVVM, SwiftUI, Async/Await, and Combine to create a stunning and efficient API call.


It’s like a superhero team-up, where each one has its unique power to make our lives easier. So buckle up, and let’s dive into the fun with some light humor, and of course, a great learning experience! 😄





The MVVM Dream Team: Model, View, ViewModel 🏅


If you’re new to the world of iOS development, you might not know what MVVM (Model-View-ViewModel) is. Think of it as a fantastic trio, much like Harry, Ron, and Hermione, where each one plays a specific role to make the magic happen.

  • Model: It’s the data part of the app. In our case, it will be the data received from the API call.

  • View: It’s what the user sees and interacts with. SwiftUI, you beauty! 😍

  • ViewModel: It’s the bridge between the Model and the View. The mastermind behind the scenes.


SwiftUI: The Rising Star ⭐


SwiftUI, Apple’s declarative UI framework, has taken the iOS world by storm. It’s like your favorite TV show that gets better with every season. Let’s use this rising star to create a simple View to display the data fetched from our API.

import SwiftUI

struct ContentView: View {
    @StateObject private var viewModel = ContentViewModel()

    var body: some View {
        VStack {
            if let data = viewModel.data {
                Text(data.title)
                    .font(.largeTitle)
            } else {
                ProgressView("Loading data…")
            }
        }
        .task {
            await viewModel.fetchData()
        }
    }
}

Here, we create a ContentView with a @StateObject that initializes our ContentViewModel. Depending on whether we have data or not, we either display the data or show a loading indicator. The .task modifier is where the real magic happens: it triggers our API call when the view appears.







Async/Await: The Magic Wand ✨


Async/Await is the new shiny toy in the Swift world. It makes working with asynchronous code easier than ever. It’s like a spell that can turn complex callback hells into simple, linear code. Let’s add this spell to our ViewModel.

import Combine
import Foundation

struct DataType: Codable {
    let id: Int
    let userId: Int
    let title: String
    let body: String
}

class ContentViewModel: ObservableObject {
    @Published var data: DataType?

    private var cancellable: AnyCancellable?

    func fetchData() async {
        do {
            let url = URL(string: "https://jsonplaceholder.typicode.com/posts/1")!
            let (data, _) = try await URLSession.shared.data(from: url)
            let decodedData = try JSONDecoder().decode(DataType.self, from: data)
            DispatchQueue.main.async {
                self.data = decodedData
            }
        } catch {
            print("Error fetching data: \(error)")
        }
    }
}

We define an async fetchData function that performs our API call. We use URLSession.shared.data(from:), which now supports async/await, making our code cleaner than a Nimbus 2000.


If successful, we decode the data and update our @Published property : @Published var data: DataType?




Combine: The Unstoppable Force 🌪️


Combine allows us to chain, transform, and react to asynchronous events easily. We’ll use it to create a publisher that fetches and decodes our data.

First, let’s import Combine:

import Combine

Now, we’ll modify our fetchData function to use Combine. We'll create a dataTaskPublisher(for:) that automatically decodes our data using the decode operator. Lastly, we'll use sink to handle the result and update our @Publishedproperty.

func fetchData() {
    guard let url = URL(string: "https://jsonplaceholder.typicode.com/posts/1") else {
        print("Invalid URL")
        return
    }

    cancellable = URLSession.shared.dataTaskPublisher(for: url)
        .map(\.data)
        .decode(type: DataType.self, decoder: JSONDecoder())
        .receive(on: DispatchQueue.main)
        .sink { completion in
            switch completion {
            case .failure(let error):
                print("Error fetching data: \(error)")
            case .finished:
                print("Data fetched successfully")
            }
        } receiveValue: { data in
            self.data = data
        }
}

And with that, we’ve successfully integrated Combine into our ViewModel! 🎉


In Conclusion: The Dream Team in Action 🌟


We’ve successfully built a SwiftUI app using MVVM, SwiftUI, Async/Await, and Combine to make an efficient and clean API call. It’s like we’ve assembled a dream team of programming concepts that work seamlessly together, making our code more readable, maintainable, and enjoyable to write.

So, give yourself a pat on the back, and go ahead, spread the word about this ultimate combination. Together, let’s create more fantastic apps and have some fun along the way! 🚀

Happy coding! 😄

12 views0 comments

Comments


bottom of page