Performance Optimization

Jun 20 2024 · Swift 5.9, iOS 17.4, Xcode 15.3

Lesson 03: Thread Optimization & Memory Management

Demo

Episode complete

Play next episode

Next

Heads up... You’re accessing parts of this content for free, with some sections shown as obfuscated text.

Heads up... You’re accessing parts of this content for free, with some sections shown as obfuscated text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

In this demo, you’ll use the Cinematica app from the second lesson, where you’ve already made efficiency enhancements and tackled Network and Data Optimization. Now, it’s time to dive into Thread Optimization. You’ll aim to boost app responsiveness and performance, ultimately delivering a smoother user experience.

Thread Optimization

Multi threading

Building upon what you learned in the previous lesson about fetching upcoming movies, you will now also fetch top rated and popular movies concurrently thanks to multi threading to improve the information available to your app users without compromising performance.

static let topRatedPath = moviePath + "/top_rated"
static let popularPath = moviePath + "/popular"
case fetchTopRated(page: Int)
case fetchPopular(page: Int)
case .fetchTopRated:
  return APIsPaths.topRatedPath
case .fetchPopular:
  return APIsPaths.popularPath
case .fetchUpcoming(let page), .fetchTopRated(let page), .fetchPopular(let page):
  return ["page": "\(page)", "sort_by": "popularity.desc"]
var upcomingMovies: [Movie] = []
var topRatedMovies: [Movie] = []
var popularMovies: [Movie] = []
func fetchUpcomingMovies() async throws {
  let moviePaginatedResponse: MoviePaginatedResponse = try await
  requestManager.perform(MoviesRequests.fetchUpcoming(page: currentPage))
  self.upcomingMovies.append(contentsOf: moviePaginatedResponse.results ?? [])
}

func fetchTopRatedMovies() async throws {
  let moviePaginatedResponse: MoviePaginatedResponse = try await
  requestManager.perform(MoviesRequests.fetchTopRated(page: currentPage))
  self.topRatedMovies.append(contentsOf: moviePaginatedResponse.results ?? [])
}

func fetchPopularMovies() async throws {
  let moviePaginatedResponse: MoviePaginatedResponse = try await
  requestManager.perform(MoviesRequests.fetchPopular(page: currentPage))
  self.popularMovies.append(contentsOf: moviePaginatedResponse.results ?? [])
}
import SwiftUI

struct MovieSectionView: View {
  let movies: [Movie]
  let title: String

  var body: some View {
    VStack(alignment: .leading, spacing: 5) {
      Text(title)
        .font(.title)
        .padding(.top, 15)
        .padding(.leading, 15)

      ScrollView(.horizontal, showsIndicators: false) {
        LazyHStack(spacing: 10) {
          ForEach(movies, id: \.id) { movie in
            MovieCellView(movie: movie)
              .frame(width: 150, height: 200)
          }
        }
      }
    }
    .background(Color(.secondarySystemBackground))
  }
}
VStack(spacing: 20) {
  MovieSectionView(movies: movieListViewModel.upcomingMovies, title: "Upcoming")
  MovieSectionView(movies: movieListViewModel.topRatedMovies, title: "Top Rated")
  MovieSectionView(movies: movieListViewModel.popularMovies, title: "Popular")
}
.padding()
if movieListViewModel.errorManager.errorMessage != nil
func fetchAllMovies() async {
  isLoading = true
  do {
    await withThrowingTaskGroup(of: Void.self) { group in
      group.addTask {
        do {
          try await self.fetchUpcomingMovies()
        } catch {
          self.errorManager.handleError(error)
        }
      }
      group.addTask {
        do {
          try await self.fetchTopRatedMovies()
        } catch {
          self.errorManager.handleError(error)
        }
      }
      group.addTask {
        do {
          try await self.fetchPopularMovies()
        } catch {
          self.errorManager.handleError(error)
        }
      }
    }
  }
  isLoading = false
}
Task {
  await movieListViewModel.fetchAllMovies()
}

Thread hangs

The code you previously added involves the app retrieving three movie lists in a background thread and subsequently refreshing the UI on the main thread using the view model properties with the help of @Observable. This approach ensures a seamless user interface experience without blocking the UI.

@MainActor // 1
public func perform(_ request: RequestProtocol) async throws -> Data {
  sleep(5) // 2
  // ...
See forum comments
Cinema mode Download course materials from Github
Previous: Instruction Next: Conclusion