Swift Macros: esempi pratici di metaprogrammazione
Guida completa alle Swift Macros: creazione di macro freestanding e attached incluse le body macro di Swift 6, manipolazione dell'AST con swift-syntax ed esempi pratici per eliminare il codice ripetitivo.

Le Swift Macros trasformano il modo in cui gli sviluppatori scrivono e mantengono il codice. Introdotte con Swift 5.9 ed espanse significativamente in Swift 6, le macro consentono la generazione di codice in fase di compilazione preservando la sicurezza dei tipi statica. A differenza delle macro del preprocessore C, le Swift Macros sono type-safe, integrate nel compilatore e completamente supportate dagli strumenti di sviluppo, inclusa la funzione "Expand Macro" di Xcode.
Questa guida tratta la creazione delle Swift Macros dai concetti fondamentali alle implementazioni avanzate, incluse le nuove body macro introdotte in Swift 6 (SE-0415). Tutti gli esempi utilizzano Swift 6 e swift-syntax 600.0.0+.
Comprendere i tipi di Swift Macros
Swift offre due categorie principali di macro, ciascuna con casi d'uso distinti. Le macro freestanding agiscono in modo autonomo come espressioni o dichiarazioni, mentre le macro attached si legano a dichiarazioni esistenti per modificarle o arricchirle.
Macro freestanding: espressione e dichiarazione
Le macro freestanding iniziano con il simbolo # e possono restituire un valore (espressione) oppure creare nuove dichiarazioni. Ecco un esempio concreto di macro di espressione:
// Freestanding expression macro - generates a value
let buildInfo = #buildDate
// Expansion → "2026-08-23 10:30:45"
// Freestanding macro with arguments
let message = #stringify(1 + 2)
// Expansion → "1 + 2 = 3"
// Freestanding declaration macro - creates declarations
#makeCase("success", "failure", "pending")
// Expansion →
// case success
// case failure
// case pendingLa differenza fondamentale tra espressione e dichiarazione risiede nel risultato: un'espressione produce un valore, mentre una dichiarazione produce codice strutturale (tipi, funzioni, variabili).
Macro attached: i sei ruoli
Le macro attached iniziano con @ e si collocano davanti a una dichiarazione. Swift 6 definisce sei ruoli distinti per queste macro, incluso il nuovo ruolo body per l'implementazione delle funzioni:
// @attached(peer) - adds declarations at the same level
@AddAsync
func fetchUser(id: Int) -> User { ... }
// Expansion → adds func fetchUserAsync(id: Int) async -> User
// @attached(accessor) - adds getters/setters
@UserDefault("theme")
var currentTheme: String
// Expansion → adds get { UserDefaults.standard.string(...) }
// @attached(member) - adds members to a type
@AutoEquatable
struct Point {
var x: Int
var y: Int
}
// Expansion → adds static func == (lhs: Point, rhs: Point) -> Bool
// @attached(memberAttribute) - applies attributes to members
@CodableKeys
struct Config {
var apiUrl: String
var timeout: Int
}
// Expansion → adds @CodingKey("api_url") before apiUrl
// @attached(extension) - adds protocol conformances
@Hashable
struct User {
var id: Int
var name: String
}
// Expansion → adds extension User: Hashable { ... }
// @attached(body) - NEW in Swift 6: synthesizes function bodies
@Remote
func fetchData(endpoint: String) -> Data
// Expansion → generates the entire function implementationQuesti ruoli possono essere combinati per creare macro potenti, capaci di trasformare il codice su più dimensioni contemporaneamente.
Swift 6 Body Macros: SE-0415
Swift 6 ha introdotto le function body macro attraverso SE-0415, consentendo alle macro di sintetizzare o sostituire le implementazioni delle funzioni. Questo risolve casi d'uso che nessun ruolo macro precedente poteva gestire.
Il protocollo BodyMacro
Il protocollo BodyMacro consente la sostituzione completa del corpo di una funzione:
import SwiftSyntax
import SwiftSyntaxMacros
public protocol BodyMacro: AttachedMacro {
static func expansion(
of node: AttributeSyntax,
providingBodyFor declaration: some DeclSyntaxProtocol &
WithOptionalCodeBlockSyntax,
in context: some MacroExpansionContext
) throws -> [CodeBlockItemSyntax]
}La macro riceve la dichiarazione originale e restituisce elementi code block che diventano il corpo della funzione. Qualsiasi corpo esistente viene sostituito.
Implementazione di una macro Remote Procedure Call
Le body macro sono particolarmente utili per generare codice del layer di rete dalle firme delle funzioni:
import SwiftSyntax
import SwiftSyntaxMacros
public struct RemoteMacro: BodyMacro {
public static func expansion(
of node: AttributeSyntax,
providingBodyFor declaration: some DeclSyntaxProtocol &
WithOptionalCodeBlockSyntax,
in context: some MacroExpansionContext
) throws -> [CodeBlockItemSyntax] {
guard let funcDecl = declaration.as(FunctionDeclSyntax.self) else {
throw MacroError.invalidSyntax("@Remote requires a function")
}
let functionName = funcDecl.name.text
let parameters = funcDecl.signature.parameterClause.parameters
// Generate parameter encoding
let paramDict = parameters.map { param in
let name = param.firstName.text
return "\"\(name)\": \(name)"
}.joined(separator: ", ")
// Generate the RPC body
return [
"""
let params: [String: Any] = [\(raw: paramDict)]
return try await RPCClient.shared.call(
method: "\(raw: functionName)",
params: params
)
"""
]
}
}Dichiarazione e utilizzo
/// Generates a remote procedure call implementation
@attached(body)
public macro Remote() = #externalMacro(
module: "MyMacrosPlugin",
type: "RemoteMacro"
)class APIService {
@Remote
func createUser(name: String, email: String) async throws -> User
@Remote
func deleteUser(id: UUID) async throws -> Bool
// Each function body is generated at compile time:
// func createUser(name: String, email: String) async throws -> User {
// let params: [String: Any] = ["name": name, "email": email]
// return try await RPCClient.shared.call(
// method: "createUser",
// params: params
// )
// }
}Al massimo una body macro può essere applicata a una data funzione. Per l'iniezione di preamble componibili (logging, tracing), il protocollo PreambleMacro è in considerazione per le future versioni di Swift.
Configurazione del progetto per creare macro
La creazione delle Swift Macros richiede uno Swift Package con una struttura specifica. Il pacchetto dipende da swift-syntax, la libreria ufficiale per manipolare il codice Swift come albero della sintassi astratta (AST).
Struttura di Package.swift
// swift-tools-version: 6.0
import PackageDescription
let package = Package(
name: "MyMacros",
platforms: [.macOS(.v10_15), .iOS(.v13)],
products: [
// Library exposing macros to the main project
.library(
name: "MyMacros",
targets: ["MyMacros"]
),
// Executable for testing macros
.executable(
name: "MyMacrosClient",
targets: ["MyMacrosClient"]
)
],
dependencies: [
// Required dependency for macros - use 600.0.0+ for Swift 6
.package(
url: "https://github.com/swiftlang/swift-syntax.git",
from: "600.0.0"
)
],
targets: [
// Compiler plugin containing implementation
.macro(
name: "MyMacrosPlugin",
dependencies: [
.product(name: "SwiftSyntax", package: "swift-syntax"),
.product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
.product(name: "SwiftCompilerPlugin", package: "swift-syntax")
]
),
// Target exposing macro declarations
.target(
name: "MyMacros",
dependencies: ["MyMacrosPlugin"]
),
// Test client
.executableTarget(
name: "MyMacrosClient",
dependencies: ["MyMacros"]
),
// Unit tests
.testTarget(
name: "MyMacrosTests",
dependencies: [
"MyMacrosPlugin",
.product(name: "SwiftSyntaxMacrosTestSupport", package: "swift-syntax")
]
)
]
)Questa configurazione separa nettamente le dichiarazioni delle macro (ciò che il codice client vede) dalla loro implementazione (eseguita in fase di compilazione).
Servono almeno tre file: MyMacros.swift per le dichiarazioni, MyMacrosPlugin.swift per le implementazioni e MyMacrosTests.swift per i test. Questa separazione semplifica la manutenzione.
Creare una macro di espressione
Le macro di espressione generano un valore utilizzabile direttamente nel codice. Vediamo come creare una macro #unwrap che esegue l'unwrap di un opzionale con un messaggio di errore personalizzato che include il nome della variabile. Per approfondimenti sui pattern di gestione degli errori in Swift, consultare la guida su Swift Structured Concurrency.
Dichiarazione della macro
import Foundation
/// Macro that unwraps an optional with an explicit error message
/// Usage: let value = #unwrap(optionalValue)
/// Expansion: guard let optionalValue else { fatalError("...") }; optionalValue
@freestanding(expression)
public macro unwrap<T>(_ value: T?) -> T = #externalMacro(
module: "MyMacrosPlugin",
type: "UnwrapMacro"
)La firma dichiara che la macro accetta un opzionale e restituisce il valore non opzionale. #externalMacro punta all'implementazione presente nel plugin.
Implementazione con swift-syntax
import SwiftSyntax
import SwiftSyntaxMacros
import SwiftCompilerPlugin
public struct UnwrapMacro: ExpressionMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> ExprSyntax {
// Get the first argument passed to the macro
guard let argument = node.argumentList.first?.expression else {
throw MacroError.missingArgument
}
// Extract the variable name for the error message
let variableName = argument.description.trimmingCharacters(
in: .whitespacesAndNewlines
)
// Generate the expansion code
// Uses an immediately-invoked closure to encapsulate the guard
return """
{
guard let value = \(argument) else {
fatalError("Failed to unwrap '\\(\(literal: variableName))' - value was nil")
}
return value
}()
"""
}
}
// Custom errors for macros
enum MacroError: Error, CustomStringConvertible {
case missingArgument
case invalidSyntax(String)
var description: String {
switch self {
case .missingArgument:
return "The macro requires an argument"
case .invalidSyntax(let message):
return "Invalid syntax: \(message)"
}
}
}Il metodo expansion riceve il nodo dell'AST che rappresenta la chiamata della macro e il contesto di compilazione. Restituisce un ExprSyntax che contiene il codice generato.
Registrazione del plugin
import SwiftCompilerPlugin
import SwiftSyntaxMacros
@main
struct MyMacrosPlugin: CompilerPlugin {
// List all macros provided by this plugin
let providingMacros: [Macro.Type] = [
UnwrapMacro.self,
RemoteMacro.self,
AutoInitMacro.self,
// Add other macros here
]
}Questo entry point informa il compilatore sulle macro disponibili nel plugin.
Pronto a superare i tuoi colloqui su iOS?
Pratica con i nostri simulatori interattivi, flashcards e test tecnici.
Creare una macro attached di tipo member
Le macro member aggiungono membri (proprietà, metodi, tipi annidati) a un tipo esistente. Ecco una macro @AutoInit che genera automaticamente un inizializzatore con tutte le proprietà memorizzate.
Dichiarazione e implementazione complete
/// Automatically generates an initializer with all stored properties
@attached(member, names: named(init))
public macro AutoInit() = #externalMacro(
module: "MyMacrosPlugin",
type: "AutoInitMacro"
)import SwiftSyntax
import SwiftSyntaxMacros
public struct AutoInitMacro: MemberMacro {
public static func expansion(
of node: AttributeSyntax,
providingMembersOf declaration: some DeclGroupSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
// Verify the macro is applied to a struct or class
guard declaration.is(StructDeclSyntax.self) ||
declaration.is(ClassDeclSyntax.self) else {
throw MacroError.invalidSyntax(
"@AutoInit can only be applied to structs and classes"
)
}
// Collect stored properties
let properties = declaration.memberBlock.members
.compactMap { $0.decl.as(VariableDeclSyntax.self) }
.filter { isStoredProperty($0) }
// Generate initializer parameters
let parameters = properties.compactMap { property -> String? in
guard let binding = property.bindings.first,
let identifier = binding.pattern.as(IdentifierPatternSyntax.self),
let type = binding.typeAnnotation?.type else {
return nil
}
let name = identifier.identifier.text
let typeName = type.description.trimmingCharacters(in: .whitespaces)
// Check if the property has a default value
if binding.initializer != nil {
return "\(name): \(typeName) = \(binding.initializer!.value)"
}
return "\(name): \(typeName)"
}
// Generate assignments in the init body
let assignments = properties.compactMap { property -> String? in
guard let binding = property.bindings.first,
let identifier = binding.pattern.as(IdentifierPatternSyntax.self) else {
return nil
}
let name = identifier.identifier.text
return "self.\(name) = \(name)"
}
// Build the complete initializer
let initDecl: DeclSyntax = """
public init(\(raw: parameters.joined(separator: ", "))) {
\(raw: assignments.joined(separator: "\n "))
}
"""
return [initDecl]
}
// Check if a variable is a stored property (not computed)
private static func isStoredProperty(_ variable: VariableDeclSyntax) -> Bool {
guard let binding = variable.bindings.first else { return false }
// A computed property has an accessor block with get/set
if let accessor = binding.accessorBlock {
// If it's a block with explicit accessors, it's computed
if accessor.accessors.is(AccessorDeclListSyntax.self) {
return false
}
}
// let or var without accessor = stored property
return true
}
}Utilizzo della macro AutoInit
@AutoInit
struct User {
let id: UUID
var name: String
var email: String
var isActive: Bool = true
}
// Automatically generated code:
// public init(id: UUID, name: String, email: String, isActive: Bool = true) {
// self.id = id
// self.name = name
// self.email = email
// self.isActive = isActive
// }
// Usage
let user = User(id: UUID(), name: "Alice", email: "alice@example.com")
// isActive uses the default valueQuesta macro elimina il boilerplate dell'inizializzatore, particolarmente utile per i modelli di dati con molte proprietà.
Macro attached peer per la generazione async
Le macro peer aggiungono dichiarazioni allo stesso livello della dichiarazione annotata. Ecco una macro @AddAsync che genera la versione async di una funzione basata su completion handler. Questo pattern è particolarmente rilevante nella migrazione di codice di rete legacy, come trattato nella guida alla migrazione Combine vs async/await.
/// Automatically generates an async version of a function with completion handler
@attached(peer, names: suffixed(Async))
public macro AddAsync() = #externalMacro(
module: "MyMacrosPlugin",
type: "AddAsyncMacro"
)import SwiftSyntax
import SwiftSyntaxMacros
public struct AddAsyncMacro: PeerMacro {
public static func expansion(
of node: AttributeSyntax,
providingPeersOf declaration: some DeclSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
// Verify it's a function
guard let funcDecl = declaration.as(FunctionDeclSyntax.self) else {
throw MacroError.invalidSyntax(
"@AddAsync requires a function"
)
}
let functionName = funcDecl.name.text
let asyncFunctionName = "\(functionName)Async"
// Analyze parameters to find the completion handler
let parameters = funcDecl.signature.parameterClause.parameters
// Filter parameters (exclude completion handler)
var regularParams: [String] = []
var completionType: String? = nil
for param in parameters {
let paramType = param.type.description
// Detect a completion handler (closure with Result or simple value)
if paramType.contains("->") && paramType.contains("Void") {
// Extract the return type from completion
completionType = extractCompletionReturnType(from: paramType)
} else {
let paramName = param.firstName.text
let paramSecondName = param.secondName?.text
let label = paramSecondName ?? paramName
regularParams.append("\(paramName): \(paramType)")
}
}
guard let returnType = completionType else {
throw MacroError.invalidSyntax(
"No completion handler found"
)
}
// Generate arguments for internal call
let callArgs = parameters.dropLast().map { param in
let name = param.firstName.text
return "\(name): \(name)"
}.joined(separator: ", ")
// Generate the async function
let asyncFunc: DeclSyntax = """
func \(raw: asyncFunctionName)(\(raw: regularParams.joined(separator: ", "))) async throws -> \(raw: returnType) {
try await withCheckedThrowingContinuation { continuation in
\(raw: functionName)(\(raw: callArgs.isEmpty ? "" : callArgs + ", ")completion: { result in
switch result {
case .success(let value):
continuation.resume(returning: value)
case .failure(let error):
continuation.resume(throwing: error)
}
})
}
}
"""
return [asyncFunc]
}
// Extract return type from a Result type
private static func extractCompletionReturnType(from type: String) -> String {
// Simplified pattern - in production, use the AST
if let match = type.range(of: #"Result<([^,]+)"#, options: .regularExpression) {
var result = String(type[match])
result = result.replacingOccurrences(of: "Result<", with: "")
return result.trimmingCharacters(in: .whitespaces)
}
return "Void"
}
}Dimostrazione della macro AddAsync
class NetworkService {
@AddAsync
func fetchUser(
id: Int,
completion: @escaping (Result<User, Error>) -> Void
) {
// Implementation with callback
URLSession.shared.dataTask(with: URL(string: "/users/\(id)")!) { data, _, error in
if let error = error {
completion(.failure(error))
} else if let data = data {
let user = try? JSONDecoder().decode(User.self, from: data)
completion(.success(user!))
}
}.resume()
}
// Automatically generates:
// func fetchUserAsync(id: Int) async throws -> User {
// try await withCheckedThrowingContinuation { continuation in
// fetchUser(id: id, completion: { result in
// switch result {
// case .success(let value):
// continuation.resume(returning: value)
// case .failure(let error):
// continuation.resume(throwing: error)
// }
// })
// }
// }
}
// Modern usage with async/await
let user = try await networkService.fetchUserAsync(id: 42)Il nome della funzione generata deve essere dichiarato in names: dell'attributo @attached. Qui suffixed(Async) indica che la funzione generata avrà il suffisso "Async" aggiunto al nome originale.
Test unitari per le macro
Testare le macro è essenziale, dato che generano codice destinato alla compilazione. Swift fornisce SwiftSyntaxMacrosTestSupport per agevolare questi test. A partire da swift-syntax 603.0.0+, oltre a XCTest è supportato anche Swift Testing.
import SwiftSyntaxMacros
import SwiftSyntaxMacrosTestSupport
import XCTest
@testable import MyMacrosPlugin
final class MyMacrosTests: XCTestCase {
// Dictionary of macros to test
let testMacros: [String: Macro.Type] = [
"unwrap": UnwrapMacro.self,
"AutoInit": AutoInitMacro.self,
"AddAsync": AddAsyncMacro.self,
"Remote": RemoteMacro.self
]
func testUnwrapMacroExpansion() throws {
assertMacroExpansion(
"""
let value = #unwrap(optionalString)
""",
expandedSource: """
let value = {
guard let value = optionalString else {
fatalError("Failed to unwrap 'optionalString' - value was nil")
}
return value
}()
""",
macros: testMacros
)
}
func testAutoInitMacroWithStruct() throws {
assertMacroExpansion(
"""
@AutoInit
struct Point {
let x: Int
var y: Int
}
""",
expandedSource: """
struct Point {
let x: Int
var y: Int
public init(x: Int, y: Int) {
self.x = x
self.y = y
}
}
""",
macros: testMacros
)
}
func testAutoInitWithDefaultValues() throws {
assertMacroExpansion(
"""
@AutoInit
struct Config {
var timeout: Int = 30
var retryCount: Int
}
""",
expandedSource: """
struct Config {
var timeout: Int = 30
var retryCount: Int
public init(timeout: Int = 30, retryCount: Int) {
self.timeout = timeout
self.retryCount = retryCount
}
}
""",
macros: testMacros
)
}
func testAutoInitFailsOnEnum() throws {
assertMacroExpansion(
"""
@AutoInit
enum Status {
case active
}
""",
expandedSource: """
enum Status {
case active
}
""",
diagnostics: [
DiagnosticSpec(
message: "@AutoInit can only be applied to structs and classes",
line: 1,
column: 1
)
],
macros: testMacros
)
}
func testRemoteMacroExpansion() throws {
assertMacroExpansion(
"""
@Remote
func fetchUser(id: Int) async throws -> User
""",
expandedSource: """
func fetchUser(id: Int) async throws -> User {
let params: [String: Any] = ["id": id]
return try await RPCClient.shared.call(
method: "fetchUser",
params: params
)
}
""",
macros: testMacros
)
}
}I test verificano la corretta espansione del codice e i messaggi di errore appropriati in caso di utilizzo non valido.
Eseguire il debug e ispezionare le macro
Xcode offre diversi strumenti per il debug delle macro e per comprendere il codice generato.
Espansione in Xcode
// Right-click on macro call → "Expand Macro"
// Displays generated code inline
@AutoInit
struct Product {
let id: UUID
var name: String
var price: Decimal
}
// To see the expansion:
// 1. Right-click on @AutoInit
// 2. Select "Expand Macro"
// 3. Generated code displays inline for inspection and debuggingLogging durante lo sviluppo
public struct DebugMacro: ExpressionMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> ExprSyntax {
// Print the node's AST to understand the structure
print("=== DEBUG MACRO ===")
print("Node: \(node)")
print("Arguments: \(node.argumentList)")
// Complete dump of the syntax tree
dump(node)
// Continue with normal expansion
return "42"
}
}Esplorare l'AST con swift-ast-explorer
Lo strumento online swift-ast-explorer.com permette di visualizzare l'albero della sintassi di qualsiasi codice Swift. Comprendere la struttura dei nodi AST è fondamentale nell'implementazione delle macro, come mostrato nella guida al framework Swift Testing che tratta le macro di test integrate in Swift.
Best practice per le Swift Macros
Creare macro manutenibili richiede di seguire alcune convenzioni ed evitare le insidie più comuni.
Validazione e messaggi di errore
public struct ValidatedMacro: MemberMacro {
public static func expansion(
of node: AttributeSyntax,
providingMembersOf declaration: some DeclGroupSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
// Validate usage context
guard declaration.is(StructDeclSyntax.self) else {
// Clear error messages with possible localization
context.diagnose(
Diagnostic(
node: node,
message: MacroDiagnosticMessage(
id: "invalid-target",
message: "This macro can only be applied to structs",
severity: .error
)
)
)
return []
}
// Check required arguments
guard let arguments = node.arguments else {
context.diagnose(
Diagnostic(
node: node,
message: MacroDiagnosticMessage(
id: "missing-args",
message: "Required arguments missing",
severity: .error
)
)
)
return []
}
// Implementation...
return []
}
}
// Structure for diagnostic messages
struct MacroDiagnosticMessage: DiagnosticMessage {
let id: String
let message: String
let severity: DiagnosticSeverity
var diagnosticID: MessageID {
MessageID(domain: "MyMacros", id: id)
}
}Generare codice leggibile
// Avoid: hard-to-read generated code
let badCode: DeclSyntax = "public init(a:Int,b:String,c:Bool){self.a=a;self.b=b;self.c=c}"
// Preferred: properly formatted generated code
let goodCode: DeclSyntax = """
public init(
a: Int,
b: String,
c: Bool
) {
self.a = a
self.b = b
self.c = c
}
"""Il codice generato dovrebbe essere leggibile quanto quello scritto a mano, perché chi sviluppa lo ispezionerà tramite "Expand Macro".
Sources
- SE-0415: Function Body Macros, implementato in Swift 6.0
- swift-syntax releases, versione 600.0.0+ per Swift 6
- Swift.org Blog, note di rilascio Swift 6.3
Cosa consentono le Swift Macros nella pratica
Le Swift Macros rappresentano uno strumento potente per eliminare il boilerplate preservando la sicurezza dei tipi statica. Questa tecnologia consente:
- Due categorie di macro: freestanding (
#) e attached (@) - Sei ruoli attached in Swift 6: peer, accessor, member, memberAttribute, extension e il nuovo ruolo body
- Implementazione tramite swift-syntax 600.0.0+ e manipolazione dell'AST
- Test obbligatori con
SwiftSyntaxMacrosTestSupport, ora con supporto per Swift Testing - Pacchetto separato necessario per le implementazioni
- Debug tramite "Expand Macro" in Xcode
- Messaggi di errore espliciti essenziali per la developer experience
Le Swift Macros sono particolarmente utili per generare conformità (Equatable, Codable), creare property wrapper avanzati, sintetizzare implementazioni RPC dalle firme e modernizzare API basate su callback verso async/await.
Inizia a praticare!
Metti alla prova le tue conoscenze con i nostri simulatori di colloquio e test tecnici.
Sapresti trovare il bug in iOS?
Uno snippet reale, un bug nascosto, un tentativo al giorno. Senza account per provare.

Scritto da
Anthony Fillion-MailletFondatore di SharpSkill
Sviluppatore fullstack da oltre 10 anni. Guida SharpSkill e risponde di tutto ciò che vi viene pubblicato.
Aggiornato il 23 agosto 2026
Tag
Condividi
Articoli correlati

Lavoro sviluppatore iOS 2026: dove cercare, stipendi e preparazione ai colloqui
Il mercato del lavoro per sviluppatori iOS nel 2026: dove trovare offerte, fasce salariali attuali e come prepararsi efficacemente ai colloqui tecnici.

Swift Package Manager nel 2026: Creare, Pubblicare Pacchetti e Domande da Colloquio
Guida completa a Swift Package Manager per sviluppatori iOS. Come creare pacchetti, gestire dipendenze, pubblicare librerie e rispondere alle domande tecniche sui colloqui SPM.

Combine vs async/await in Swift: Pattern di Migrazione Progressiva
Guida completa alla migrazione da Combine ad async/await in Swift: strategie progressive, pattern di bridging e coesistenza dei paradigmi nelle codebase iOS.