iOS
clase final 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) } } clase final 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()
¿Hay más macros en el proyecto además de la capa de red?
¿Si implementas una nueva pantalla o una nueva función, escribes inmediatamente en SwiftUI, o hay discusiones sobre la elección entre UIKit y SwiftUI?
Cuéntame sobre el uso de SwiftUI en el proyecto: ¿cuál es la política, cuántas pantallas están escritas en SwiftUI?
¿Por qué elegiste un diccionario como almacenamiento en caché?
¿Qué tan útiles son las herramientas de IA (Cursor, Proxyman, etc.) para ayudarte a ser más productivo en el trabajo?
Cuéntame sobre los Property Wrapper útiles que usaste en el proyecto
import UIKit protocolo CacheProtocol { tipo asociado func setValue(_ value: Valor?, forKey key: String) func value(forKey key: String) -> Valor? func fetchValue(forKey key: String, completion: @escaping (Valor?) -> Void) }
// Escribe `data` en un archivo llamado `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) } // Elimina el archivo llamado `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) } // Lee `Data` de un archivo llamado `file`. Si el archivo no existe, devuelve `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) { } }
¿Cómo entiendes el término concurrencia estructurada en Swift? ¿Task.detached se ajusta a este concepto?
Explica la diferencia entre una caché en memoria y una caché en disco.
¿Por qué creaste métodos separados load y getLastCities, y qué debería suceder si se llama a getLastCities antes de que los datos hayan sido cargados?
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) } } }
¿Cómo priorizaría los problemas identificados si el proyecto debe salir a producción en una hora?
¿Por qué usar una clase final en lugar de una clase para la implementación de la caché?
// Servicio que envía solicitudes (get/post), url, formación de la solicitud según la semántica (decode/encode)
enum HTTPMethod: String { case post = "POST" } enum Errors: Error { case invalidUrl case invalidQuery case invalidResponse case httpStatus(Int) } protocol MyDecoder { func decode<T: Decodable>(_: _ type: T.Type, from data: Data) throws -> T } final class NetworkService { private let session: URLSession private let encoder = JSONEncoder() private let decoder: MyDecoder init( session: URLSession = .shared, decoder: MyDecoder ) { self.session = session self.decoder = decoder } func request<Response: Decodable, Body: Encodable>( urlString: String, method: HTTPMethod, body: Body? = nil, headers: [String: String] = [:], query: [String: String] = [:] ) async throws -> Response { guard var components = URLComponents(string: urlString) else { throw Errors.invalidUrl } if !query.isEmpty { components.queryItems = query.map { URLQueryItem(name: $0.key, value: $0.value) } } guard let url = components.url else { throw Errors.invalidQuery } var request = URLRequest(url: url)