anyAppleOS en Swift 6.4: comprobaciones de disponibilidad más limpias
Arturo Rivas Arias
Swift permite compartir cada vez más código entre iOS, macOS, watchOS, tvOS y visionOS. Sin embargo, hasta ahora incluso una API disponible desde la misma versión en todos los sistemas obligaba a repetir cada plataforma dentro de @available y #available. Swift 6.4 incorpora anyAppleOS para expresar ese caso mediante una única condición.
La novedad aprovecha un cambio que Apple introdujo en 2025: la unificación de la numeración de sus sistemas operativos. Desde la generación 26, iOS, macOS, watchOS, tvOS y visionOS avanzan con el mismo número de versión. El compilador puede utilizar esa correspondencia para entender que anyAppleOS 26.0 representa el mismo límite de disponibilidad en toda la familia.
El problema de repetir todas las plataformas
Imaginemos un paquete con una API compartida para guardar el progreso de lectura. Si esa funcionalidad necesita la versión 26 de todos los sistemas de Apple, antes de Swift 6.4 había que escribir lo siguiente:
@available(
macOS 26.0,
iOS 26.0,
watchOS 26.0,
tvOS 26.0,
visionOS 26.0,
*
)
public struct ReadingProgress {
public let bookID: String
public let completedPercentage: Double
}
El código es correcto, pero el atributo ocupa más espacio que la propia declaración. También es fácil olvidar una plataforma, asignar una versión distinta por error o tener que modificar muchas listas casi idénticas cuando evoluciona una librería multiplataforma.
Con Swift 6.4, la misma intención se expresa así:
@available(anyAppleOS 26.0, *)
public struct ReadingProgress {
public let bookID: String
public let completedPercentage: Double
}
anyAppleOS no es una macro que expanda el texto a cinco atributos. El compilador lo modela como una especio de dominio de disponibilidad situado por encima de macOS, iOS, watchOS y tvOS. De iOS también se infiere la disponibilidad correspondiente para visionOS y Mac Catalyst. Esta jerarquía permite que las reglas generales y las específicas convivan sin perder información.
@available y #available siguen teniendo funciones distintas
La nueva plataforma puede aparecer tanto en anotaciones como en comprobaciones ejecutadas en tiempo de ejecución, pero ambas construcciones responden a preguntas diferentes.
@available establece desde qué versión se puede utilizar una declaración. El compilador impide llamarla desde un contexto que no garantice ese requisito:
@available(anyAppleOS 26.0, *)
func buildInteractiveIndex(for chapters: [String]) -> [String: Int] {
Dictionary(uniqueKeysWithValues: chapters.enumerated().map { index, chapter in
(chapter, index)
})
}
#available, en cambio, comprueba la versión del sistema en el dispositivo que está ejecutando la aplicación. Sigue siendo necesario cuando el deployment target es anterior a la API que queremos usar:
func makeIndex(for chapters: [String]) -> [String: Int] {
if #available(anyAppleOS 26.0, *) {
buildInteractiveIndex(for: chapters)
} else {
Dictionary(uniqueKeysWithValues: chapters.indices.map { index in
(chapters[index], index)
})
}
}
La compatibilidad es bidireccional. Al compilar para macOS, por ejemplo, un contexto protegido con #available(macOS 26.0, *) puede utilizar una API declarada con @available(anyAppleOS 26.0, *). Del mismo modo, una comprobación con anyAppleOS satisface el requisito de una declaración específica de macOS 26 cuando el destino actual es macOS.
Esto permite que los proveedores de librerías adopten la nueva sintaxis sin romper las aplicaciones de sus usuarios. Tampoco es necesario esperar a que una dependencia actualice todas sus comprobaciones para comenzar a usar #available(anyAppleOS 26.0, *) en el código cliente.
No funciona con versiones anteriores a la 26
anyAppleOS existe gracias a que las versiones actuales están alineadas. Por eso, el compilador rechaza cualquier número inferior a 26.0:
// ❌ Error: 25.0 no es una versión válida para anyAppleOS
@available(anyAppleOS 25.0, *)
func importLegacyLibrary() { }
No hay una equivalencia automática que traduzca anyAppleOS 25 a iOS 18, macOS 15, watchOS 11, tvOS 18 y visionOS 2. Para una API introducida durante esa generación todavía hay que enumerar las versiones reales:
@available(
iOS 18.0,
macOS 15.0,
watchOS 11.0,
tvOS 18.0,
visionOS 2.0,
*
)
func importLegacyLibrary() { }
Este límite evita una ambigüedad importante. Antes de la unificación, un único número no podía representar de forma fiable los ciclos de lanzamiento de todos los sistemas.
Excepciones para una plataforma concreta
Compartir un límite general no significa que todas las APIs tengan que llegar exactamente a la vez. Swift aplica la regla más específica que coincida con el destino actual, por lo que anyAppleOS puede combinarse con excepciones.
Por ejemplo, una función podría estar disponible en la versión 26.0 de todos los sistemas salvo watchOS, donde llega en la 26.2:
@available(anyAppleOS 26.0, watchOS 26.2, *)
public func generateReadingRecap() -> String {
"Tu resumen anual está listo"
}
También se puede marcar una API como no disponible en algunas plataformas:
@available(anyAppleOS 26.0, *)
@available(watchOS, unavailable)
@available(tvOS, unavailable)
public func openEditorialWorkspace() {
// Interfaz disponible en iPhone, iPad, Mac y Apple Vision Pro
}
Además la sintaxis permite otras extensiones y admite por ejemplo más información sobre la obsolescencia, no solo la versión de introducción:
@available(
anyAppleOS,
introduced: 26.0,
deprecated: 27.0,
message: "Usa ReadingRecapGenerator"
)
public func createLegacyRecap() -> String {
"Resumen"
}
Por tanto, anyAppleOS debe entenderse como una regla general que se puede refinar, no como una afirmación irreversible de que una API se comporta igual en cada plataforma.
Compilación condicional con #if os(anyAppleOS)
Swift 6.4 también permite utilizar el nombre dentro de os(...). Así se puede compilar un bloque únicamente para los sistemas operativos de Apple sin encadenar cinco condiciones:
#if os(anyAppleOS)
import Security
func makeSecureToken(length: Int) -> [UInt8] {
var bytes = [UInt8](repeating: 0, count: length)
let status = bytes.withUnsafeMutableBytes { buffer in
SecRandomCopyBytes(kSecRandomDefault, buffer.count, buffer.baseAddress!)
}
precondition(status == errSecSuccess)
return bytes
}
#endif
Conviene no confundir esta construcción con #available:
#if os(anyAppleOS)se resuelve al compilar y comprueba la familia del destino, pero no su versión.#available(anyAppleOS 26.0, *)se evalúa en tiempo de ejecución y comprueba si el sistema actual alcanza la versión indicada.@available(anyAppleOS 26.0, *)describe las restricciones de una declaración para que el compilador pueda validarlas.
Tampoco sustituye a canImport. Si la condición real es la presencia de un framework, la comprobación correcta sigue siendo esta:
#if canImport(HealthKit)
import HealthKit
#endif
Un destino puede pertenecer a la familia de sistemas de Apple y, aun así, no ofrecer el módulo que necesita un fragmento concreto. os(anyAppleOS) habla de plataforma; canImport habla de módulos disponibles.
Interoperabilidad con Objective-C, C y C++
La mejora no se limita a las declaraciones escritas en Swift. Clang incorpora la misma comprobación de disponibilidad para que las APIs de C, Objective-C y C++ puedan evitar la repetición explícita de cada plataforma:
__attribute__((availability(anyAppleOS, introduced=26.0)))
@interface ReadingArchive : NSObject
- (void)rebuildIndex;
@end
Cuando Swift importa una declaración de este tipo, conserva la restricción de anyAppleOS. Esto resulta especialmente útil en frameworks con una interfaz pública mixta, porque ambas partes expresan el mismo contrato de disponibilidad.
El compilador también puede detectar listas redundantes y sugerir su sustitución por anyAppleOS. Además de reducir trabajo manual, el diagnóstico ayuda a mantener un estilo coherente a medida que la librería adopta Swift 6.4.
Lo que no cambia en Package.swift
La nueva sintaxis pertenece al sistema de disponibilidad del lenguaje. No reemplaza la lista de plataformas compatibles de un paquete Swift:
let package = Package(
name: "ReadingTools",
platforms: [
.iOS(.v26),
.macOS(.v26),
.watchOS(.v26),
.tvOS(.v26),
.visionOS(.v26)
],
targets: [
.target(name: "ReadingTools")
]
)
El manifiesto sigue declarando en qué plataformas puede compilarse el paquete. @available y #available describen, dentro de su código fuente, cuándo están disponibles las declaraciones y rutas de ejecución concretas. Son capas relacionadas, pero con responsabilidades diferentes.
Disponibilidad de Swift 6.4
A comienzos de agosto de 2026, Swift 6.4 todavía forma parte del próximo ciclo del lenguaje. Swift.org lo presentó en junio como una versión en beta y el soporte para anyAppleOS puede probarse mediante las development snapshots construidas desde la rama principal del compilador. Por tanto, no conviene introducir esta sintaxis en una rama de producción hasta que el proyecto utilice una toolchain compatible.
La adopción tampoco obliga a elevar el deployment target a la versión 26. Una aplicación puede continuar soportando sistemas anteriores y utilizar #available(anyAppleOS 26.0, *) para seleccionar la implementación moderna en tiempo de ejecución. El requisito nuevo afecta a la versión del compilador que entiende la sintaxis, no necesariamente a la versión mínima del sistema que ejecutará la aplicación.
Conclusión
anyAppleOS es una mejora pequeña, pero especialmente valiosa para paquetes compartidos y frameworks que abarcan todo el ecosistema de Apple. Reduce atributos repetitivos, expresa mejor la intención de una API y conserva la flexibilidad necesaria para declarar excepciones por plataforma.
La regla práctica es sencilla: úsalo cuando una declaración comparte el mismo límite desde la versión 26 en los sistemas de Apple; conserva las plataformas explícitas cuando las versiones difieran; utiliza #if os(anyAppleOS) para compilación condicional y canImport cuando la condición dependa de un framework. Así se gana claridad sin ocultar las diferencias reales entre plataformas.