In the previous part of this series, I created a PartyPerson protocol and constructed classes around this. And here's the same protocol and classes with some minor tinkering:
protocol PartyPerson: class { var numberOfItemsOnYourPlate:Int {get} var name:String {get set} var isDrunk:Bool {get set} func tellUsABitAboutYourself() -> String func areYouDrunk() -> Bool func whatIsYourName() -> String } class YoungSocialite:PartyPerson { let food:[String] var numberOfItemsOnYourPlate:Int { return food.count } var isDrunk:Bool var name:String init (units:Int, name:String, food:[String]) { isDrunk = units > 3 ? true : false self.name = name self.food = food } func tellUsABitAboutYourself() -> String { return "OMG, I go out like twice every night. OMG, I know you. OMG. What a party. What a party. OMG." } func areYouDrunk() -> Bool { return isDrunk } func whatIsYourName() -> String { return name } } class ThirtySomething:PartyPerson { let varietiesOfCouscous:Int var numberOfItemsOnYourPlate:Int { return varietiesOfCouscous } var isDrunk:Bool var name:String init(units:Int, typesOfCouscous couscous:Int, name:String) { isDrunk = units > 9 ? true : false self.name = name varietiesOfCouscous = couscous } func tellUsABitAboutYourself() -> String { return "Should I have children? Or should I just keep doing this? You tell me. You look wise. Where's the toilet? What a lovely garden." } func areYouDrunk() -> Bool { return isDrunk } func whatIsYourName() -> String { return name } }I've added a name requirement and also restricted the protocol, so that it can only be applied to classes (and not structs or enums) by using the class keyword immediately after the semi-colon that follows the protocol name. I've also added some methods to return details about the person.
(Note: I learnt while testing the code contained in the latter part of this post that while a Playground would let me access class properties directly without complaint, when the app was built in the simulator a crash would occur. Hence the necessity of these added methods without which access to properties can become confused when using what Apple labels "collections of protocol types".)
Element protocols
The addition of the class restriction was just for fun, but the name property was added because I decided that I wanted to be a bit friendlier with my guests. I also decided that instead of calling the app itself the party, that I'd like to create an array of people and that array of people would represent a party:let Dave = ThirtySomething(units: 8, typesOfCouscous: 5, name:"Dave") let Brooklyn = YoungSocialite(units: 8, name: "Brooklyn", food: ["Manchego & Blackberries","Sun-dried Tomato and olive oil Bruschetta","Caprese Bites"])
let party:[PartyPerson] = [Dave,Brooklyn]Notice how the use of a protocol enables us to group instances of different classes without resorting to AnyObject. The problem is now that we can't just write a generic method that accepts a protocol which the array type adopts, because the protocol won't know the methods that our PartyPerson protocol adoptees will respond to:
func mingle<P:SequenceType>(party:P) -> String { var chat = String() for person in party { chat += person.whatIsYourName() // error chat += ": \(person.tellUsABitAboutYourself())" // error chat += person.areYouDrunk() ? " (drunk)\n" : "\n" // error } return chat }We need instead to let the function know what it will find inside the array (if we want to use any of our PartyPerson protocol methods).
func mingle<P:SequenceType where P.Generator.Element == PartyPerson>(party:P) -> String { var chat = String() for person in party { chat += person.whatIsYourName() chat += ": \(person.tellUsABitAboutYourself())" chat += person.areYouDrunk() ? " (drunk)\n" : "\n" } return chat }So what the heck have we done here? Well, we've let our mingle() function take any Type that adopts the SequenceType protocol as long as the elements within that Type have Elements that adopt the PartyPerson protocol.
(This is what P.Generator.Element is all about, every Type that adopts the SequenceType protocol has a generator and that generator enables us to iterate through the sequence and do things like use for-in. The elements, therefore, are each of the items in the sequence.)
Lessons learnt
In the documentation, Apple informs us that 'any protocol you create will become a fully-fledged type for use in your code ... [and] because it is a type, you can use a protocol in many places where other types are allowed, including: As a parameter type or return type in a function, method, or initialiser; As the type of a constant, variable, or property; As the type of items in an array, dictionary, or other container.'
The use of protocols to define types inside a collection is documented as well (under the heading 'Collections of Protocol Types'). However, it is clear from the experimentation that has gone into the writing of this article that, when using protocols to define types, an added awareness of the restrictions of accessing self, as well as accessing properties, need to be noted. (This is especially true when we reach the depths of combining collections of protocol types with generics.)
The use of protocols to define types inside a collection is documented as well (under the heading 'Collections of Protocol Types'). However, it is clear from the experimentation that has gone into the writing of this article that, when using protocols to define types, an added awareness of the restrictions of accessing self, as well as accessing properties, need to be noted. (This is especially true when we reach the depths of combining collections of protocol types with generics.)
It must also be pointed out that the problems I experienced here might not appear in a Playground file but only arise in a Simulator or device build. And this might be both confusing and frustrating when debugging. But to be fair when we reach this deeply into a newly evolving programming language with so many innovations these things are to a certain extent to be expected.
For further discussion of the use of protocols to define types in collections, see this earlier post.
Comments
Post a Comment