Classe di protocollo Swift / restrizione AnyObject nell'estensione

Aug 22 2020

Ho 2 protocolli, uno che dà un oggetto a serverIde l'altro che gli dà un status(aka è pubblicato sul server o meno). Entrambi mi consentono di gestire la sincronizzazione tra il mio modello di server e Core Data.

/// By conforming to this protocol, classes AND struct have an identifiable serverid `Int64`
protocol RemoteObject {
    var serverId: ServerId { get set }
}

/// This can only be applied to class
protocol Syncable: class {
    var syncStatus: SyncStatus { get set }

}

enum SyncStatus: Int64 {
    case published
    case local
}

L'idea alla base è che RemoteObjectpuò essere applicato a struct (cioè le strutture JSON che ricevo dal server) e class ( NSManagedObject). D'altra parte, Syncablepuò essere applicato solo a class ( NSManagedObject).

Il prossimo passo per me è, quando syncStatusè impostato su, .localdevo anche eliminare serverIdda RemoteObject sul mio oggetto impostandolo su -1, ma quando provo ottengo questo errore:

extension Syncable where Self: RemoteObject {
    var syncStatus: SyncStatus {
        get {
            SyncStatus(rawValue: syncStatusValue) ?? .local
        }
        set {
            syncStatusValue = newValue.rawValue
            if newValue == .local { serverId = -1 } // 🛑 Cannot assign to property: 'self' is immutable
        }
    }
}

🛑 Impossibile assegnare alla proprietà: "self" è immutabile

Capisco che ricevo questo errore perché RemoteObjectpuò essere applicato a strutture, che sono immutabili.

Tuttavia , considerando che Syncablepuò essere applicato solo ai tipi di classe, non forza RemoteObjectl'applicazione su una classe? e poi essere mutevole?

C'è un modo per forzare RemoteObjectdi essere tipo di classe nella mia estensione? per esempio extension Syncable where Self: RemoteObject & class?

Risposte

3 Olympiloutre Aug 22 2020 at 19:06

Alla fine ho trovato la soluzione. Secondo questo post posso farlo:

extension Syncable where Self: RemoteObject {
    var syncStatus: SyncStatus {
        get {
            SyncStatus(rawValue: syncStatusValue) ?? .local
        }
        set {
            syncStatusValue = newValue.rawValue
            if newValue == .local { 
                var s = self
                s.serverId = -1
            } 
        }
    }
}

Questo elimina l'errore e, per quanto ho capito, cambierà serverIdper le classi che sono tipi di riferimento e non muterà le strutture che sono tipi di valore ( comunque non possono essere conformi a questo protocollo ).


Per andare oltre, per chi potrebbe trovare utile questo post. La mia comprensione del problema non era accurata. All'inizio ho pensato che il problema fosse sul RemoteObjectprotocollo, che poteva essere applicato a qualsiasi tipo.

Il problema sembra essere il contrario, secondo questa discussione . Quando si limita un procotol co :classo :AnyObject, fa sì che le sue forze settersiano immutabili .

Quindi, quando provo a modificare il valore di serverId, chiamo un setter mutabile da uno immutabile che fa lamentare il compilatore. Il work-around va bene per il mio utilizzo, tuttavia per ottenere una soluzione adeguata, dovrei sbarazzarmi di :classin Syncablee affrontare tutti i motivi che mi hanno costretto a farlo solo in classe in primo luogo.