Создать менеджер для объединения треков из нескольких альбомов

DispatchGroup позволяет дождаться всех альбомов, а отдельные слоты по индексу сохраняют порядок albumIds независимо от времени ответов.

Короткий ответ

Что ответить на собеседовании

DispatchGroup позволяет дождаться всех альбомов, а отдельные слоты по индексу сохраняют порядок albumIds независимо от времени ответов.

Подробный разбор

Ответ с пояснениями

Условие

Необходимо реализовать менеджер, который собирает списки треков из разных альбомов и выводит объединённый результат.

struct Song: CustomStringConvertible {
    let title: String
    var description: String { title }
}

protocol AlbumProvider {
    func loadSongs(albumId: String, completion: @escaping ([Song]) -> Void)
}

final class AlbumService: AlbumProvider {
    func loadSongs(albumId: String, completion: @escaping ([Song]) -> Void) {
        DispatchQueue.global().async {
            let songs: [Song]
            switch albumId {
            case "1":
                songs = [
                    Song(title: "One, 1"),
                    Song(title: "Two, 1"),
                    Song(title: "Three, 1"),
                    Song(title: "Four, 1")
                ]
            case "2":
                songs = [
                    Song(title: "One, 2"),
                    Song(title: "Two, 2"),
                    Song(title: "Three, 2")
                ]
            case "3":
                songs = [
                    Song(title: "One, 3"),
                    Song(title: "Two, 3")
                ]
            default:
                songs = []
            }

            DispatchQueue.main.asyncAfter(deadline: .now() + Double.random(in: 0.1...0.4)) {
                completion(songs)
            }
        }
    }
}

final class SongsManager {
    let albumService = AlbumService()

    func loadSongs(albumIds: [String], completion: @escaping ([Song]) -> Void) {
        // Реализуйте загрузку треков из всех переданных альбомов и верните единый массив
        // Написать код
    }
}

let manager = SongsManager()
let albumIds: [String] = ["1", "2", "3"]

manager.loadSongs(albumIds: albumIds) { songs in
    // вывести объединённый список треков
    print(songs)
}

RunLoop.main.run(until: Date(timeIntervalSinceNow: 3))

Ответ

DispatchGroup позволяет дождаться всех альбомов, а отдельные слоты по индексу сохраняют порядок albumIds независимо от времени ответов.

Тело метода можно реализовать так:

let group = DispatchGroup()
let lock = NSLock()
var results = Array(repeating: [Song](), count: albumIds.count)
for (index, albumId) in albumIds.enumerated() {
    group.enter()
    albumService.loadSongs(albumId: albumId) { songs in
        lock.lock()
        results[index] = songs
        lock.unlock()
        group.leave()
    }
}
group.notify(queue: .main) {
    lock.lock()
    let songs = results.flatMap { $0 }
    lock.unlock()
    completion(songs)
}

Контракт провайдера должен гарантировать ровно один callback на запрос, включая пустой результат. Пустой список альбомов вернёт пустой массив. Это callback-вариант для традиционного режима Swift; при строгой Sendable-проверке общий массив нужно оформить как явно синхронизированное хранилище. Протокол не передаёт ошибки и отмену — их следует добавить для реальной сети.

ИИ-помощник для собеседований

Хочешь уверенно проходить собеседования?

Попробуй ИИ-помощник для собеседований: слышит вас и собеседника, анализирует экран, подсказывает ответы в реальном времени, работает без VPN и не попадает в захват экрана.

Подробнее