Swift protocol class/AnyObject restriction in extension

Viewed 310

I have 2 protocols, one that gives an object a serverId and the other one that gives him a status (aka is it published on the server or not). The two of them allow me to handle synchronization between my server model and 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
}

The idea behind it is that RemoteObject can be applied to struct (i.e. The JSON structures I get back from server) and class (NSManagedObject). On the other hand, Syncable can only be applied to class (NSManagedObject).

Next step for me is, when the syncStatus is set to .local I also need to get rid of the serverId from RemoteObject on my object by setting it to -1, but when I try I get this error:

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
        }
    }
}

Cannot assign to property: 'self' is immutable

I understand that I get this error because RemoteObject can be applied to structs, which are immutable.

However, considering that Syncable can only be applied to class types, doesnt it force RemoteObject to be applied on a class? and then be mutable?

Is there a way to force RemoteObject to be class type in my extension? for instance extension Syncable where Self: RemoteObject & class ?

1 Answers

I found the solution eventually. According to this post I can do this :

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
            } 
        }
    }
}

This gets rid of the error, and, as far as I understand, this will change serverId for classes which are reference types, and not mutate the structs which are value types ( Anyway they cannot conform to this protocol ).


To go further, for who may find this post useful. My understanding of the issue was not accurate. At first I thought that the issue was on the RemoteObject protocol, which could be applied to any type.

The problem seems to be the other way, according to this discussion. When restricting a procotol co :class or :AnyObject, it makes forces its setter to be immutable.

Then, when I am trying to change the value of serverId, I am calling a mutable setter from an immutable one which causes the compiler to complain. The work-around is fine for my usage, however to get a proper solution, I should get rid of the :class in Syncable, and deal with all the reasons that forced me to make it class-only in the first place.

Related