Skip to content

Instantly share code, notes, and snippets.

@davidahouse
Created June 14, 2015 01:31
Show Gist options
  • Save davidahouse/17f352dfae023e6f337a to your computer and use it in GitHub Desktop.
Save davidahouse/17f352dfae023e6f337a to your computer and use it in GitHub Desktop.
Trying out arrays filled with things conforming to a Protocol
//: Playground demonstrating how to deal with an Array containing items that conform
//: to a protocol.
import Cocoa
/*:
Just a basic protocol with a single function that has to be implemented.
*/
protocol Nameable {
func getName() -> String
}
/*:
And a struct that conforms to the protocol
*/
struct MyStruct : Nameable {
var title: String
init(title:String) {
self.title = title
}
func getName() -> String {
return self.title
}
}
/*:
For good measure, let's add a class that also conforms to the protocol.
*/
class MyClass : Nameable {
var name:String
init(name:String) {
self.name = name
}
func getName() -> String {
return self.name
}
}
//: Now lets create an array that contains some instances of MyStruct and MyClass
let a:Array<Nameable> = [MyStruct(title: "fred"),MyClass(name: "barney")]
/*:
The problem is how do you get these items out of the array safely. The array just
guarantees that each item conforms to the Nameable protocol. But if you want to iterate through
them and deal with them as their specific type then you need to take care!
*/
/*:
So first we can iterate and deal with them as the Nameable using its getName function. Note that
x below is some kind of thing that conforms to the Nameable protocol. That is really all we can do with
it unless we cast it to a more specific type.
*/
for thing in a {
let x = thing.getName()
}
/*:
Next example is how to safely deal with these things using their actual types. This way we can interact
with each thing safely using its type. If the array a had some other kind of object that conforms
to the Nameable protocol then nothing bad would happen here. It would just be ignored since the if let
statements would not match it.
*/
for thing in a {
if let structThing:MyStruct = thing as? MyStruct {
let y = structThing.title
}
else if let classThing:MyClass = thing as? MyClass {
let y = classThing.name
}
}
/*:
If you have an array with all the same types in it, you might want to take an easy way out, but
you can't! This might be a bug in Swift, so I'm filing a radar. For example:
*/
let b:Array<Nameable> = [MyStruct(title: "fred"),MyStruct(title: "wilma")]
/*:
THIS NEXT STATEMENT WILL CRASH WITH AN ERROR: "fatal error: can't unsafeBitCast between types of different sizes"
if let flintstones:Array<MyStruct> = b as? Array<MyStruct> {
}
*/
/*:
Now for something even more tricky. Let's say we want to create a function that returns an optional item
that implements the Nameable protocol. But we want to be able to return either a single Nameable or an
array of them.
*/
/*:
To do this, we need to extend the Array class so it can also conform to the Nameable protocol. Protocols
rock! I'm implementing the getName function just to return an empty string.
*/
extension Array : Nameable {
func getName() -> String {
return ""
}
}
/*:
Now create the function that can take in a string and return either a single Nameable item, or an
array of them.
*/
func foShizzleName(input:String) -> Nameable? {
if input == "fred" {
return MyStruct(title: "Rock Crusher")
}
else if input == "rubble" {
let foPeople:Array<Nameable> = Array<Nameable>(arrayLiteral:MyClass(name: "barney"),MyClass(name: "betty"),MyClass(name: "Bam Bam"))
return foPeople
}
else {
return nil
}
}
//: This is the case where the function returns a single MyStruct
let what = foShizzleName("fred")
if let turnDownFor:MyStruct = what as? MyStruct {
let x = turnDownFor.title
}
//: This is the case where it returns an array. I left in the if let to check for the single item to show how you can
//: detect what kind of thing was returned.
let who = foShizzleName("rubble")
if let turnDownFor:MyStruct = who as? MyStruct {
let x = turnDownFor.title
}
else if let people:Array<Nameable> = who as? Array<Nameable> {
for thing in people {
if let structThing:MyStruct = thing as? MyStruct {
let y = structThing.title
}
else if let classThing:MyClass = thing as? MyClass {
let y = classThing.name
}
}
}
else {
let x = "not any of the above"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment