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-проверке общий массив нужно оформить как явно синхронизированное хранилище. Протокол не передаёт ошибки и отмену — их следует добавить для реальной сети.