Search code examples
swift3swift-extensions

Can't assign the item in _ArrayProtocol


Hi I'm trying to retrieve data by using ParseLiveQuery

My problem is...I can't assign the item in Array..

import Foundation
import Parse
import ParseLiveQuery

extension _ArrayProtocol where Iterator.Element == PFObject {
    mutating func updateWithEvent(event: Event<PFObject>) {
        switch event {
        case .created(let object):
                append(object)
        case .entered(let object):
                append(object)
        case .deleted(let object):
            if let index = index(of: object) {
                remove(at: index)
            }
        case .left(let object):
            if let index = index(of: object) {
                remove(at: index)
            }
        case .updated(let object):
            if let index = index(of: object) {
                //I got an error : Cannot assign through subscript: subscript is get-only
                self[index] = object

            }
        }
    }
}

Here is problem

 case .updated(let object):
                if let index = index(of: object) {
                    //I got an error : Cannot assign through subscript: subscript is get-only
                    self[index] = object

                }
            }

Anyone know about this?


Solution

  • First of all, you should be careful working with internal types and protocols. The underscore prefixing the protocol name (_ArrayProtocol) tells us just this; that it's an internal protocol, meaning it may be prompt for changes without warning in future Swift updates (since developers using Swift should not rely on direct/explicit usage on an internal protocol).

    internal protocol _ArrayProtocol: RangeReplaceableCollection, ...
    

    From swift/stdlib/public/core/ArrayType.swift.


    Now, as for your error, the error message is quite telling

    Cannot assign through subscript: subscript is get-only

    I.e., subscript for _ArrayProtocol has no available setter (e.g. via a default implementation: since you're using _ArrayProtocol as a type itself in your extension). It does have setter for subscript blueprinted, but there exists no default implementation of it, whereas a default implementation for the getter is available from RangeReplaceableCollection.

    So for this particular case, if you want to replace an Element of self at index, you could e.g. make use of the mutating replaceSubRange(_:, with:) method of RangeReplaceableCollection accessible to types conforming to _ArrayProtocol, as the protocol itself conforms to RangeReplaceableCollection:

    replaceSubrange(index...index, with: [object])
    

    If we for a minute leave the subject of the inappropriateness of working directly with an internal protocol, we can construct an example implementing this fix, an example that can actually be be verified (in contrast to the one you've supplied in your question ...).

    enum Foo {
        case created(Int)
        case entered(Int)
        case deleted(Int)
        case left(Int)
        case updated(Int)
    }
    
    // do not work directly with this _internal_ protocol!!
    extension _ArrayProtocol where Iterator.Element == Int {
        mutating func updateWithEvent(event: Foo) {
            switch event {
            case .created(let object):
                append(object)
            case .entered(let object):
                append(object)
            case .deleted(let object):
                if let index = index(of: object) {
                    remove(at: index)
                }
            case .left(let object):
                if let index = index(of: object) {
                    remove(at: index)
                }
            case .updated(let object):
                if let index = index(of: object) {  
                    replaceSubrange(index...index, with: [object])
                }
            }
        }
    }
    

    But then again, you shouldn't implement such an extension to an internal protocol. Consider instead to implement the extension to public type/protocol, e.g. as an extension to Array constrained to Element's that conform to Equatable:

    enum Foo<T> {
        case created(T)
        case entered(T)
        case deleted(T)
        case left(T)
        case updated(T)
    }
    
    extension Array where Element: Equatable {
        mutating func updateWithEvent(event: Foo<Element>) {
            switch event {
            case .created(let object):
                append(object)
            case .entered(let object):
                append(object)
            case .deleted(let object):
                if let index = index(of: object) {
                    remove(at: index)
                }
            case .left(let object):
                if let index = index(of: object) {
                    remove(at: index)
                }
            case .updated(let object):
                if let index = index(of: object) {
                    self[index] = object
                }
            }
        }
    }
    

    Note here that in contrast to working with concrete types of the internal _ArrayProtocol (as in your own extension, usage of self), which have no available setter for subscript, Array, on the other, does, which means you can apply your original simple element replacement self[index] = object when the extension is applied to Array.