Comprensión durante la lectura
¿Por qué se recomienda comenzar la migración con un fragmento de código aislado?
Entre menos dependencias tenga un fragmento de código más fácil es hacer la migración. Que haya dependencias significa que aparecerán casos donde, luego de arreglar 50 errores, aparecen 80 más.
¿Qué ventaja ofrece actualizar las dependencias de terceros antes de habilitar el "strict concurrency checking"?
Puede ser que el código que planeo migrar dependa de otras bibliotecas de terceros. En este caso, vale la pena primero actualizar dichas dependencias, en caso de que estas estén al día con el uso de Swift Concurrency.
De hecho, si las dependencias de terceros ya introdujeron cambios para estar al día con strict concurrency checking, muy probablemente se le exigirá a mi código que introduzca algunos cambios incluso antes de habilitar el "strict concurrency checking".
¿Vale la pena usar @MainActor por defecto?
Si se está trabajando en una aplicación móvil, es buena idea activar @MainActor por defecto. Esto va a resolver muchas advertencias y errores.
En un proyecto de iOS buscar "Default Actor Isolation". En un paquete de SPM:
.target(
name: "DefaultActorIsolationPackage",
swiftSettings: [
.defaultIsolation(MainActor.self)
]
)
¿Qué diferencia hay entre los niveles Minimal, Targeted y Complete de strict concurrency checking?
-
Minimal Solo revisa código que tenga palabras clave de concurrencia explícita:
async/await,@Sendable,@MainActor. Todo lo demás se ignora. -
Targeted revisa todo tipo que conforma
Sendable, aunque no seasyncdirectamente. - Complete revisa absolutamente todo el código base, incluso si no toca concurrencia. Este nivel pregunta: "¿Podría este código, algún día, cruzar un isolation domain".
Consideremos los siguientes escenarios:
class UserCache {
var name: String = ""
var age: Int = 0
}
func loadUser() {
let cache = UserCache()
cache.name = "Ana"
}
-
Minimal: No hay warning porque no hay
async,@Sendableni@MainActor. -
Targeted: No hay warning porque no conforma
Sendableni usa concurrencia. -
Complete: warning -
UserCachese unclasscon propiedades mutables y no esSendable.
class UserCache {
var name: String = ""
}
func doWork(completion: @escaping @Sendable (UserCache) -> Void) {
let cache = UserCache()
completion(cache) // captura UserCache dentro de @Sendable
}
-
Minimal: warning - El parámetro es
@Sendable, peroUserCacheno conformaSendable. - Targeted: warning - Igual que minimal.
-
Complete: error - Convierte en error el warning de
Sendable.
struct Config: Sendable {
var timeout: Int
var logger: Logger // Logger es una class, no Sendable
}
class Logger {
var prefix: String = ""
}
func run(config: Config) {
// código síncrono, sin async
}
-
Minimal: nada -
runes síncrono, así que no valida nada. -
Targeted: error -
ConfigesSendable, pero su propiedadloggerno. -
Complete: error - Igual que Targeted. Además, advierte que
Loggerno esSendabley se puede usar en cualquier otro lado.
En SPM se activa así:
.target(
name: "CoreExtensions",
dependencies: ["Logging"],
path: "CoreExtensions/Sources",
swiftSettings: [
/// Used to be like this in Xcode 14:
SwiftSetting.unsafeFlags(["-Xfrontend", "-strict-concurrency=complete"]),
/// Xcode 15, 16, 26, and up. Remove `=targeted` to use the default `complete`. Potentially isolate to a platform to further reduce scope.
.enableExperimentalFeature("StrictConcurrency=targeted", .when(platforms: [.macOS]))
]
)
¿En qué situación tendría sentido quedarse en un paso intermedio como agregar alternativas async en lugar de migrar por completo?
Puede ser que el fragmento de código aislado sobre el que estemos trabajando sea una dependencia de algún otro módulo. En este caso, se puede proveer una alternativa async/await del API que permita a otro desarrollador migrar los clientes a Swift Concurrency.
Al proveer una alternativa async/await se puede agregar un "Async wrapper" desde el menú "Refactor" y poner la siguiente etiqueta en el código que recibe closures:
@available(*, deprecated, renamed: "fetchImage(urlRequest:)", message: "Consider using the async/await alternative.")
La etiqueta anterior va a levantar una advertencia en todos los puntos donde se use la versión con closures, lo que permitirá identificar con rapidez donde se debe reemplazar con la versión que usa async/await.
Usar un "Async wrapper" puede ser útil porque permite a los otros desarrolladores avanzar en la migración mientras yo me enfoco en construir la solución final de Swift Concurrency por detrás.
¿Por qué es útil conformar Sendable en los tipos de datos de un paquete?
Un paquete puede ser ampliamente usado por algún cliente. En este caso, conviene conformar Sendable para que se pueda usar con tranquilidad en lugares donde sí hay cambios de dominio de aislamiento.
¿Qué relación existe entre el swift-tools-version y el modo de lenguaje Swift 6?
swift-tools-version permite elegir la versión del lenguaje de un paquete (de SPM). Al elegir // swift-tools-version: 6.0 luego se puede poner en versión 5 a algunas dependencias específicas con .swiftLanguageMode(.v5).
.target(
name: "CoreNetworking",
swiftSettings: [
.swiftLanguageMode(.v5)
]
)
Top comments (0)