Home > OS >  How do I make the properties of a class iterable is swift using Sequence and IteratorProtocol?
How do I make the properties of a class iterable is swift using Sequence and IteratorProtocol?

Time:07-10

I would like to make my class in Swift iterable.

My goal is to be able to create a class called Contact that holds properties such as the givenName, familyName, and middleName, like iOS CNContact. I would like to be able to have a class function that compares two instances of the class Contact, and finds which property the two contact objects have that match, so that say if both contacts have the same value for the givenName property, then the class function returns the result.

Here is a sample code:

class Contact {
    
    static func compare(left: Contact, right: Contact) {
        
        for property in left.properties {
            if property == right.property {
                // match is found
            }
        }

    }
    
    var givenName: String = ""
    var familyName: String = ""
    var middleName: String = ""
    
    private var properties = [givenName, familyName, middleName]
    
}

let left = Contact()
let right = Contact()

Contact.compare(left: left, right: right)

I found posts that used mirroring and reflection, but I want to use Sequence and IteratorProtocol. I suspect there is already the ability to do exactly what I want to do. It seems to be a logical need that would arise.

What is the way to do this that has a balance between simplicity and the ability to address common needs to iterate through the instance properties of a class. An enumeration can be declared with given has values. Is there a way to make that work for this purpose? Is there a protocol that a class can use that assigns a hash value or other identifiable value that would allow for a sequential order to iterate through the properties of a class?

I was able to find posts and documentation that allowed me to get as far as the following code in playground that generated the following in debug window:

struct Name: Sequence {
    typealias Iterator = NameIterator
    typealias Element = Name
    typealias Name = String
    
    var name = "<name>"
    
    func makeIterator() -> NameIterator {
        return NameIterator()
    }
}

struct NameIterator: IteratorProtocol {
    typealias Iterator = String
    typealias Element = Name
    typealias Name = String

    mutating func next() -> Name? {
        
        let nextName = Name()
        
        return nextName
    }
    
}

let nameStrings = ["Debbie", "Harper", "Indivan", "Juniella"]

for nameString in nameStrings {
    print(nameString)
}
Debbie
Harper
Indivan
Juniella

CodePudding user response:

If you really don't want to use mirror, a straightforward way is to cycle through a list of key paths. This is particularly easy in your case because the properties are all strings:

class Contact {
    static let properties = [\Contact.givenName, \Contact.familyName, \Contact.middleName]

    static func compare(left: Contact, right: Contact) {
        for property in properties {
            if left[keyPath: property] == right[keyPath: property] {
                print("got a match"); return
            }
        }
        print("no match")
    }

    var givenName: String = ""
    var familyName: String = ""
    var middleName: String = ""
}

CodePudding user response:

I think there's some confusion going on here.

The Sequence protocol and friends (IteratorProtocol, Collection, etc.) exist for you to be able to define custom sequences/collections that can leverage the existing collection algorithms (e.g. if you conform to Sequence, your type gets map "for free"). It has absolutely nothing to do with accessing object properties. If you want to do that, the only official reflection API in Swift is Mirror.

It's possible to...

  1. ...just Mirror, to create a standard collection (e.g. Array) of properties of an object
  2. ...just Sequence/Collection, to create a custom collection object that lists the property values of an object from hard-coded keypaths
  3. ...or you can use both, together, to create a custom collection object that uses Mirror to automatically list the properties of an object and their values
  • Related