Opaque Type è un meccanismo di Swift che consente a una funzione di restituire un valore di qualche tipo senza rivelare il tipo concreto al codice chiamante. La parola chiave some nel tipo di ritorno è l’esempio più famoso: some View in SwiftUI significa “la funzione restituisce un tipo che è conforme a View, ma quale esattamente è un dettaglio implementativo.” Un opaque type preserva l’identità del tipo (a differenza di un protocollo come tipo), consentendo al compilatore di ottimizzare il codice e garantendo la coerenza del tipo restituito. Secondo Swift Book, 2025, gli opaque types risolvono il problema dei protocolli con tipi associati, consentendo di restituire valori di tali protocolli dalle funzioni.
Punti chiave
Opaque Type è un tipo di ritorno dichiarato con la parola chiave some che nasconde l’implementazione concreta dal codice chiamante. Il chiamante sa solo che il valore restituito è conforme a un determinato protocollo, ma non sa esattamente quale tipo si cela dietro some. Nel frattempo, il compilatore conosce il tipo esatto e lo utilizza per dispatch statico e ottimizzazione.
Prima dell’introduzione degli opaque types in Swift 5.1 (SE-0244), era impossibile restituire un protocollo con tipi associati da una funzione senza un wrapper di boxing. Ad esempio, il protocollo Equatable ha un tipo associato, e una funzione non poteva semplicemente restituire Equatable — il compilatore dava un errore “il protocollo può essere usato solo come vincolo generico.” Opaque type ha risolto questo problema.
func makeInt() -> some Equatable {
return 42
}
func makeString() -> some Equatable {
return "Hello"
}
// Il compilatore sa che makeInt restituisce Int
// makeInt() == makeString() — ❌ errore, tipi diversi
Entrambe le funzioni restituiscono some Equatable, ma i tipi concreti sono diversi: Int e String. Tentare di confrontarli con == causerà un errore di compilazione perché un opaque type garantisce che una specifica chiamata restituisca lo stesso tipo, ma non tra funzioni diverse. Questa è una caratteristica, non un bug: opaque type preserva l’identità del tipo là dove un protocollo come tipo (any Equatable) la perde.
Generic e Opaque Type sono due facce della stessa medaglia. I generics permettono al codice chiamante di scegliere il tipo, mentre opaque type permette alla funzione di nascondere il tipo dal codice chiamante. La differenza sta nella direzione del controllo.
| Caratteristica | Generic | Opaque some |
|---|---|---|
| Chi sceglie il tipo | Codice chiamante | Funzione/metodo |
| Identità del tipo | Preservata (stabile) | Preservata (stabile) |
| Numero di rami di ritorno | Uno (tramite generic) | Stesso tipo in tutti i rami |
| Utilizzo | Algoritmi, strutture dati | SwiftUI, metodi factory |
In una funzione generica, il chiamante decide quale tipo utilizzare. La funzione deve funzionare con qualsiasi T che soddisfi i vincoli. Per opaque type, il chiamante non conosce il tipo concreto — l’implementazione prende la decisione.
// Generic: il chiamante sceglie il tipo
func identity<T>(_ value: T) -> T { value }
let x: Int = identity(42)
// Opaque: la funzione nasconde il tipo
func makeSomeEquatable() -> some Equatable { 42 }
let y = makeSomeEquatable()
La scelta tra generic e opaque type dipende dall’intenzione. Se il codice chiamante deve scegliere il tipo, usa i generics. Se la funzione deve nascondere i dettagli implementativi, usa some. SwiftUI ha scelto some View proprio perché body deve essere flessibile internamente ma stabile esternamente.
some è una parola chiave di Swift introdotta in Swift 5.1 (SE-0244). Viene utilizzata in posizione di ritorno per dichiarare un opaque type, oltre che in parametri (SE-0341) e proprietà. some garantisce che il tipo concreto sia stabile e noto al compilatore ma nascosto dal codice esterno.
A partire da Swift 5.7, some può essere usato non solo in posizione di ritorno ma anche nei parametri. some Equatable in un parametro significa “questa funzione accetta qualsiasi tipo Equatable, ma tutte le chiamate all’interno di un corpo specifico vedono lo stesso tipo.”
func areEqual(_ a: some Equatable, _ b: some Equatable) -> Bool {
// a e b — tipi potenzialmente diversi, == non funzionerà direttamente
return isEqual(a, b)
}
func isEqual<T: Equatable>(_ a: T, _ b: T) -> Bool {
return a == b
}
Usare some nei parametri offre una sintassi più concisa rispetto a
any è una parola chiave di Swift 5.6+ per dichiarare esplicitamente tipi esistenziali (protocollo come tipo). A differenza di some, any cancella l’identità del tipo: il compilatore non sa quale tipo concreto si nasconde dietro il protocollo. Questo offre flessibilità (puoi memorizzare tipi diversi in un singolo array), ma a scapito delle prestazioni.
some — polimorfismo statico: il compilatore conosce il tipo concreto, utilizza dispatch diretto e può inlineare il codice. any — polimorfismo dinamico: viene utilizzata una tabella di metodi virtuali (contenitore esistenziale), che aggiunge indirezione.
protocol Drawable {
func draw()
}
// some: il tipo statico è noto
func makeDrawable() -> some Drawable {
return Circle() // Tipo di ritorno singolo
}
// any: dinamico, può memorizzare tipi diversi
var shapes: [any Drawable] = [Circle(), Square()]
shapes.append(Triangle())
La scelta tra some e any è un compromesso tra prestazioni e flessibilità. Some è più veloce ma si limita a una singola implementazione. Any è più flessibile (puoi mescolare tipi) ma più lento a causa del dispatch dinamico. In SwiftUI, body usa sempre some View perché il body di ogni View è un tipo concreto.
Opaque Type risolve un problema fondamentale di Swift: i protocolli con tipi associati (PAT) non possono essere utilizzati direttamente come tipo. Una funzione non può semplicemente restituire Collection — il compilatore richiede di specificare Element. some Collection risolve questo nascondendo il tipo associato.
Senza opaque type, restituire una Collection richiederebbe l’uso di un tipo concreto (Array
func makeReversedCollection<T>(
of array: [T]
) -> some Collection {
return array.reversed()
}
let result = makeReversedCollection(of: [1, 2, 3])
// result — ReversedCollection>, hidden from caller
for item in result {
print(item)
}
result può essere iterato, ma non puoi accedere direttamente alle proprietà di ReversedCollection. Questo protegge l’incapsulamento: se in seguito sostituisci reversed() con un altro metodo con un’implementazione diversa, il codice chiamante non si romperà. Opaque type ti dà la libertà di cambiare l’implementazione senza cambiare l’API.
some View è l’uso più famoso di opaque type. Ogni View in SwiftUI dichiara body come some View. Ciò significa che body restituisce qualche tipo concreto di View, ma lo sviluppatore non deve pensare a cosa sia esattamente — TupleView, Group, ModifiedContent o qualsiasi altro tipo del framework.
Senza opaque type, body dovrebbe restituire un tipo concreto, ad esempio ModifiedContent<Button<Text>, Padding>, il che è poco pratico. some View nasconde questa complessità. Il compilatore deduce il tipo esatto di body automaticamente al momento della compilazione.
struct ContentView: View {
var body: some View {
VStack {
Text("Ciao")
.font(.title)
Button("Toccami") {
print("Toccato")
}
}
.padding()
}
}
Il compilatore deduce body come ModifiedContent<VStack<TupleView<(Text, Button<Text>)>>, Padding>. Lo sviluppatore vede some View. Se cambi il layout da VStack a HStack, il compilatore dedurrà automaticamente il tipo — nessuna modifica manuale necessaria. Questa è la magia di opaque type: lo sviluppatore si concentra sulla logica dell’interfaccia, non sui tipi di composizione.
Domande frequenti
Opaque Type è un tipo dichiarato con la parola chiave some che nasconde l’implementazione concreta dal codice chiamante. Il compilatore conosce il tipo esatto, ma lo sviluppatore che usa la funzione vede solo il protocollo.
some è un opaque type con identità statica: il compilatore conosce il tipo concreto. any è un tipo esistenziale con dispatch dinamico: l’identità del tipo viene cancellata. Some è più performante, any è più flessibile.
some View nasconde il complesso tipo concreto di body, che il compilatore deduce automaticamente. Questo libera lo sviluppatore dalla necessità di scrivere il tipo esatto composto da wrapper generici (VStack, Group, ModifiedContent).
Sì, a partire da Swift 5.7. Some nei parametri è zucchero sintattico su un parametro generico. Semplifica le dichiarazioni di funzione, specialmente quando si lavora con protocolli dove ogni parametro some non richiede un
Il compilatore genererà un errore: opaque type richiede che tutti i rami di ritorno restituiscano lo stesso tipo concreto. È intenzionale per preservare l’identità del tipo. Se devi restituire tipi diversi, usa any.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche