3

I am experimenting some swift features.( Collection protocols). I would like to implement a structure that implements CollectionType. My code :

 struct Book {
    let id : String
    let name : String

    init(id: String, name: String) {
        self.id = id
        self.name = name
    }
}
struct BookCollection {
    var books : [Book]
}

First attempt :

extension BookCollection : CollectionType {
}

Compiler error : "Type BookCollection does not conform to the protocol Indexable"

Ok compiler let's conform to Indexable :

extension BookCollection : CollectionType {
    var startIndex: Int {return 0}
    var endIndex: Int {return books.count}

    subscript(idx: Int) -> Book {
        return books[idx]
    }
}

The code is working now.

My Question is : if we forget the compiler errors, is there an easy way to know what functions/properties we should implement when conforming to a protocol in swift ? Thanks

PS : I don't want to read all the extensions to see if there are a default implementation or no, it's painful

samir
  • 4,501
  • 6
  • 49
  • 76
  • 1
    Command + Click `CollectionType` would take you to the protocol. Anything that isn't `optional` should be implemented. – hannad Jan 12 '16 at 15:49
  • What's "Anything that is not optional" ? – samir Jan 12 '16 at 15:52
  • In the protocol definition, some are tagged with `@optional`. This means that whatever class wants to conform to this protocol, may or may not implement this function. Any function or property that does not have `@optional` before it must be implemented in order to not have your code produce any compiler errors. – hannad Jan 12 '16 at 15:53
  • (a) it is not so painful, the reference docs should be available in you possession so or so. (b) do you really need some specialized type BookCollection? that question is most important to answer first. what of special functionality would you like to implement there? – user3441734 Jan 12 '16 at 15:54
  • @hannad Where do you see the "@optional" keyword ? – samir Jan 12 '16 at 15:59
  • @hannad that is true, if you are using Xcode, not generally. by the way, Xcode 'support for Swift' is very poor, refactoring ... etc. this question is not very Swift specific, such a functionality should be part of IDE, not the language self. – user3441734 Jan 12 '16 at 16:00
  • @samir If you are using Xcode, Command + Click on `CollectionType` in your code, will open the code file for you to show the properties and methods for that protocol. – hannad Jan 12 '16 at 16:03
  • When you do it with your Xcode, do you see somme functions tagged with @"optional" as you have said it, if yes witch version of Xcode do you have ? Thanks – samir Jan 12 '16 at 16:08
  • I currently don't have it running so I'm not sure about CollectionType, but if you would try the same with TextViewDelegate protocol, because I am sure that this one has optional methods like textViewShouldBeginEditing. Also my Xcode is version 6.4. – hannad Jan 12 '16 at 16:13
  • You are confusing Objective C and Swift, i am talking about only Swift protocols and not Objective C ( UIKit) protocols. – samir Jan 12 '16 at 16:16
  • @samir: I usually look up the protocol I want to conform to at Swiftdoc.org (much easier to navigate than apples language ref...): http://swiftdoc.org/v2.1/protocol/CollectionType/ It clearly marks `required` variables and methods, and you can also see protocol hierarchy there, which makes it easier to know why certain protocols require conforming to another protocol (e.g. `Hashable` requires `Equatable`). – dfrib Jan 12 '16 at 16:16
  • @samir My bad.. But with swift, it is almost the same thing. Please see [this](http://useyourloaf.com/blog/swift-optional-protocol-methods.html) – hannad Jan 12 '16 at 16:19

2 Answers2

1

The compiler errors will tell you. I put your code in a playground and saw the following errors

Playground execution failed: MyPlayground.playground:15:1: error: type 'BookCollection' does not conform to protocol 'Indexable'
extension BookCollection: CollectionType
^
Swift.Indexable:6:15: note: unable to infer associated type 'Index' for protocol 'Indexable'
    typealias Index : ForwardIndexType
              ^
Swift.CollectionType:2:12: note: inferred type 'Range<BookCollection.Index>' (by matching requirement 'subscript') is invalid: does not conform to 'ForwardIndexType'
   public subscript (bounds: Range<Self.Index>) -> Slice<Self> { get }
           ^
MyPlayground.playground:15:1: error: type 'BookCollection' does not conform to protocol 'SequenceType'
extension BookCollection: CollectionType
^
Swift.SequenceType:35:17: note: protocol requires function 'generate()' with type '() -> Generator'
    public func generate() -> Self.Generator
                ^
Swift.SequenceType:2:17: note: candidate has non-matching type '<`Self`> () -> `Self`' (aka '<τ_0_0> () -> τ_0_0')
    public func generate() -> Self
                ^
Swift.SequenceType:5:17: note: candidate has non-matching type '<`Self`> () -> `Self`.Base.Generator' (aka '<τ_0_0> () -> τ_0_0.Base.Generator')
    public func generate() -> Self.Generator
                ^
Swift.CollectionType:2:17: note: candidate has non-matching type '<`Self`> () -> IndexingGenerator<`Self`>' (aka '<τ_0_0> () -> IndexingGenerator<τ_0_0>')
    public func generate() -> IndexingGenerator<Self>

Starting from the top, I see we need a type for the associated index type and then there is a whole load of errors that relate to it not being defined, so I add a typealias for protocol Indexable in the extension and that gives me a whole load more errors, so I change CollectionType to Indexable temporarily

extension BookCollection: Indexable
{
    typealias Index = Int
}

And now i have the following:

Playground execution failed: MyPlayground.playground:15:1: error: type 'BookCollection' does not conform to protocol 'Indexable'
extension BookCollection: Indexable
^
Swift.Indexable:21:15: note: protocol requires nested type '_Element'
    typealias _Element
              ^

_Element is supposed to be an implementation detail (it starts with an underscore), but let's roll with it for now and add a type alias for it too.

extension BookCollection: Indexable
{
    typealias Index = Int
    typealias _Element = Book
}

Now I have the following

Playground execution failed: MyPlayground.playground:15:1: error: type 'BookCollection' does not conform to protocol 'Indexable'
extension BookCollection: Indexable
^
Swift.Indexable:12:16: note: protocol requires property 'startIndex' with type 'Index' (aka 'Int')
    public var startIndex: Self.Index { get }
               ^
Swift.Indexable:20:16: note: protocol requires property 'endIndex' with type 'Index' (aka 'Int')
    public var endIndex: Self.Index { get }
               ^
Swift.Indexable:22:12: note: protocol requires subscript with type 'Index -> _Element'
    public subscript (position: Self.Index) -> Self._Element { get }

So now I implement the two properties and the subscript. I notice that implementing the subscript would allow the compiler to infer the two type alias types so I can delete those declarations.

extension BookCollection: Indexable
{
    var startIndex: Int { return books.startIndex }
    var endIndex: Int { return books.endIndex }

    subscript(index: Int) -> Book
    {
        return books[index]
    }
}

This gives no errors, so we change the protocol back to CollectionType and we still have no errors so we must be done.

JeremyP
  • 84,577
  • 15
  • 123
  • 161
  • Thanks for your answer. I found this method painful. I hope seing an other method (like the @optional keyword in Objective C) in the future release of Swift. – samir Jan 13 '16 at 15:32
  • It took me about 5 minutes to go through all of that. The problem with the optional annotation (which does exist) is that some methods that are not optional do have default implementations and this is the case with CollectionType. There are loads of non optional methods but you don't have to implement them if you implement `Indexable`. – JeremyP Jan 13 '16 at 16:43
0

There is no easy way other than to use what the compiler errors out on. Why? Because protocols support default implementations and you can't know what protocol functions have a default implementation (except that the compiler does not complain about them).

For example, SequenceType has many functions defined. Do you implement them all? No, because most have default implementations. The only thing needed is func generate() -> GeneratorType and the associated GeneratorType. After that, your subtype of SequenceType is done - you might choose to override some default implementations, but need not. For CollectionType it is Indexable (and GeneratorType defaults to IndexingGenerator<Self>)

Of course, if you have the source code, then you can identify the default protocol implementations but, by the time you've done that, you might as well let the compiler tell you. Or, the Apple documentation does list some functions with 'Default Implementation' which might indicate what does not need to be implemented. Or, as noted in a comment, swiftdoc.org identifies the required types/functions in a clear form.

GoZoner
  • 67,920
  • 20
  • 95
  • 145
  • 1
    I though I might add that even without looking at the source code, swiftdoc.org provides a very good overview of which methods or properties that are _compulsory toto provide implementation of_ when conforming to a protocol. In you example above, [swiftdoc's description of SequenceType](http://swiftdoc.org/v2.0/protocol/SequenceType/) clearly shows us that only function `generate()` (note, not `generator()`) is required for protocol conformance. – dfrib Jan 12 '16 at 16:45
  • 1
    @dfri func generate() -> IndexingGenerator has Default Implementation :-) (yes, it is required, but also implemented ...) – user3441734 Jan 12 '16 at 16:54