DEV Community

Anis Ali Khan
Anis Ali Khan

Posted on

Tutorial 17: Multithreading with Grand Central Dispatch (GCD) and Async/Await in Swift

Introduction

Multithreading is a key technique in iOS development for ensuring smooth user experiences. Swift provides two powerful tools for managing concurrent tasks: Grand Central Dispatch (GCD) and Async/Await.

  • GCD is a low-level API for managing background tasks efficiently.
  • Async/Await is a modern Swift concurrency feature that simplifies asynchronous programming with structured concurrency.

In this tutorial, we will learn how to use these concepts by building a Voice Recorder app that runs in the background.

Section 1: Understanding GCD and Async/Await

What is Grand Central Dispatch (GCD)?

GCD is Apple's technology for managing threads and task execution asynchronously. It provides:

  • Dispatch Queues (Serial & Concurrent)
  • Main and Background Queues
  • Work Items (sync vs async execution)

Example: Running tasks with GCD

DispatchQueue.global(qos: .background).async {
    print("This is running in the background")
}
Enter fullscreen mode Exit fullscreen mode

What is Async/Await?

Async/Await simplifies writing asynchronous code. It allows developers to work with concurrency in a more readable manner.

Example: Using Async/Await in Swift

func fetchData() async throws -> String {
    let result = try await someAsyncFunction()
    return result
}
Enter fullscreen mode Exit fullscreen mode

Section 2: Setting Up the Voice Recorder App

Creating the Xcode Project

  1. Open Xcode and create a new iOS project.
  2. Select App, choose SwiftUI as the interface, and name it "VoiceRecorder".
  3. Enable background mode for Audio in the project settings.

Section 3: Implementing Audio Recording with AVAudioRecorder

Import AVFoundation

import AVFoundation
Enter fullscreen mode Exit fullscreen mode

Request Microphone Permissions

class AudioRecorder: NSObject, ObservableObject {
    private var audioRecorder: AVAudioRecorder?
    private var recordingSession: AVAudioSession = AVAudioSession.sharedInstance()

    override init() {
        super.init()
        requestPermissions()
    }

    private func requestPermissions() {
        recordingSession.requestRecordPermission { allowed in
            DispatchQueue.main.async {
                if allowed {
                    print("Microphone access granted")
                } else {
                    print("Microphone access denied")
                }
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Section 4: Recording and Saving Audio

Start and Stop Recording

func startRecording() {
    let audioFilename = getDocumentsDirectory().appendingPathComponent("recording.m4a")

    let settings = [
        AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
        AVSampleRateKey: 12000,
        AVNumberOfChannelsKey: 1,
        AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
    ]

    do {
        audioRecorder = try AVAudioRecorder(url: audioFilename, settings: settings)
        audioRecorder?.record()
        print("Recording started")
    } catch {
        print("Recording failed: \(error.localizedDescription)")
    }
}

func stopRecording() {
    audioRecorder?.stop()
    print("Recording stopped")
}
Enter fullscreen mode Exit fullscreen mode

Helper Function to Get Directory

func getDocumentsDirectory() -> URL {
    FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
}
Enter fullscreen mode Exit fullscreen mode

Section 5: Running Tasks in the Background

Enable Background Audio

  1. Open Signing & Capabilities.
  2. Add Background Modes and enable Audio, AirPlay, and Picture in Picture.

Keep the Recording Running in the Background

func configureAudioSession() {
    do {
        try recordingSession.setCategory(.playAndRecord, mode: .default, options: .defaultToSpeaker)
        try recordingSession.setActive(true)
    } catch {
        print("Failed to configure audio session: \(error.localizedDescription)")
    }
}
Enter fullscreen mode Exit fullscreen mode

Section 6: Integrating Async/Await for Smooth UI

Using Async/Await for File Saving

func saveRecording() async {
    let audioFile = getDocumentsDirectory().appendingPathComponent("recording.m4a")

    do {
        try await Task.sleep(nanoseconds: 2_000_000_000) // Simulate background work
        print("File saved at: \(audioFile)")
    } catch {
        print("Error saving file: \(error.localizedDescription)")
    }
}
Enter fullscreen mode Exit fullscreen mode

Handling UI Updates in the Main Thread

DispatchQueue.main.async {
    self.isRecording = false
}
Enter fullscreen mode Exit fullscreen mode

Section 7: Building the SwiftUI Interface

Designing the UI

import SwiftUI

struct ContentView: View {
    @StateObject private var audioRecorder = AudioRecorder()

    var body: some View {
        VStack {
            Button(action: {
                audioRecorder.startRecording()
            }) {
                Text("Start Recording")
                    .padding()
                    .background(Color.red)
                    .foregroundColor(.white)
                    .clipShape(Capsule())
            }

            Button(action: {
                audioRecorder.stopRecording()
            }) {
                Text("Stop Recording")
                    .padding()
                    .background(Color.blue)
                    .foregroundColor(.white)
                    .clipShape(Capsule())
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Section 8: Testing and Debugging

Running the App on a Real Device

  • Use a real device for testing as the simulator does not support microphone input.
  • Check logs using print() statements.

Debugging Common Issues

Issue Solution
No microphone permission Ensure NSMicrophoneUsageDescription is in Info.plist
No audio recorded Check audio session settings and file permissions
App stops in background Enable background audio mode

Conclusion

In this tutorial, we learned:

  • Basics of GCD and Async/Await
  • How to record audio in iOS
  • Running tasks in the background using GCD
  • Using Async/Await for structured concurrency
  • Building a simple SwiftUI Voice Recorder App

This knowledge enables you to build feature-rich apps that handle concurrency efficiently in Swift.

Next Steps

  • Add UI feedback (e.g., waveforms, timers)
  • Implement file playback functionality
  • Upload recordings to the cloud using URLSession and Async/Await

Happy coding! ๐ŸŽ™๏ธ

Top comments (0)