final class ProfileViewModel1 { enum State { ... } var state: State = .idle let service: UserService init(service: UserService) { ... } func onBackPress() { // view deinit } func loadUser() { state = .loading Task { [weak self] guard let self else { return } do { let user = try await self.service.fetchUser() self.state = .loaded(user) } catch { self.state = .failed(error.localizedDescription) } } } }
Были ли проблемы с конкурентностью (Sendable, unchecked Sendable, переход между акторами) в модулях, с которыми напрямую работал?
Расскажи подробнее про свой последний проект — модуль фотогалереи в Авито, за который ты отвечал (краткосрочная аренда, форма бронирования и т.д.)
Как ты понимаешь термин structured concurrency в Swift? Task.detached подходит под это понятие?
Какая версия языка Swift используется на проекте — выше 6 или ниже?
Насколько ИИ-инструменты (Cursor, Proxyman и т.д.) помогают тебе быть продуктивнее в работе?
Briefly introduce yourself and describe your current work.
Расскажи про использование SwiftUI на проекте — какая политика, сколько экранов написано на SwiftUI
import UIKit protocol CacheProtocol { associatedtype Value func setValue(_ value: Value?, forKey key: String) func getValue(forKey key: String) -> Value? func fetchValue(forKey key: String, completion: @escaping (Value?) -> Void) } final class Cache<T>: CacheProtocol { private var storage: [String: T] = [:] private let queue = DispatchQueue(label: "my.queue", attributes: .concurrent) func setValue(_ value: T?, forKey key: String) { queue.sync(flags: .barrier) { storage[key] = value } } func getValue(forKey key: String) -> T? { queue.sync { return storage[key] } } func fetchValue(forKey key: String, completion: @escaping (T?) -> Void) { queue.async { let value = self.storage[key] completion(value) } } }
final class Cache<T>: CacheProtocol { func getValue(forKey key: String) -> T? { return storage[key] } func fetchValue(forKey key: String, completion: @escaping (T?) -> Void) { let value = storage[key] completion(value) } } final class CacheTests: XCTestCase { override func setUp() { super.setUp() } func testSetAndGetValue() { let cache = Cache<String>() let key: String = "key" let value: String = "value" cache.setValue(value, forKey: key) XCTAssertEqual(cache.getValue(forKey: key), value) cache.setValue(nil, forKey: key) XCTAssertTrue(cache.getValue(forKey: key) == nil) } func testCompletion() { } } CacheTests.defaultTestSuite.run()
import UIKit protocol CacheProtocol { associatedtype Value func setValue(_ value: Value?, forKey key: String) func value(forKey key: String) -> Value? func fetchValue(forKey key: String, completion: @escaping (Value?) -> Void) }
What happens when nil is passed to set?
Why did you choose a dictionary as the cache storage?
// Writes `data` to a file named `file`. func writeData(_ data: Data, to file: String) { let documentDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let fileURL = documentDirectoryURL.appendingPathComponent(file) try? data.write(to: fileURL) } // Removes file named `file`. func remove(file: String) { let documentDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let fileURL = documentDirectoryURL.appendingPathComponent(file) try? FileManager.default.removeItem(at: fileURL) } // Reads `Data` from a file named `file`. If the file doesn't exists returns `nil`. func readData(from file: String) -> Data? { let documentDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let fileURL = documentDirectoryURL.appendingPathComponent(file) return try? Data(contentsOf: fileURL) } final class DiskCache<T>: CacheProtocol { func setValue(_ value: T?, forKey key: String) { } func getValue(forKey key: String) -> T? { } func fetchValue(forKey key: String, completion: @escaping (T?) -> Void) { } }
protocol CacheProtocol { associatedtype Value func setValue(_ value: Value?, forKey key: String) func getValue(forKey key: String) -> Value? func fetchValue(forKey key: String, completion: @escaping (Value?) -> Void) } final class Cache<T>: CacheProtocol { private var storage: [String: T] = [:] func setValue(_ value: T?, forKey key: String) { storage[key] = value } func getValue(forKey key: String) -> T? { return storage[key] } func fetchValue(forKey key: String, completion: @escaping (T?) -> Void) { let value = storage[key] completion(value) } } import XCTest final class CacheTests: XCTestCase { override func setUp() { super.setUp() } func testSetAndGetValue() { let cache = Cache<String>() let key: String = "key" let value: String = "value" cache.setValue(value, forKey: key) XCTAssertEqual(cache.getValue(forKey: key), value) cache.setValue(nil, forKey: key) XCTAssertTrue(cache.getValue(forKey: key) == nil) } } CacheTests.defaultTestSuite.run()
Explain the difference between an in-memory cache and an on-disk cache.
Why did the UI render correctly before adding an explicit main-thread dispatch?
Why did you create separate load and getLastCities methods, and what should happen if getLastCities is called before data has been loaded?
How would you prioritize the identified issues if the project must go to production in one hour?
Why use final class instead of class for the cache implementation?