COLLECTION TYPE, SEQUENCE TYPE & INDEXABLE TYPE – Update

This is an update to the topic covered earlier. You can read about the Protocols in detail in the original article. Collection Type, Sequence Type & Indexable Type

Most of the things are the same here are some of the changes:

  1. The Indexable protocol is now not necessarily required. All the aspects of the indexable protocol now fall under the Collection Protocol
  2. The names of the protocols have changed from SequenceType & CollectionType to Sequence & Collection
  3. The keyword Generator has been renamed to Iterator. Accordingly the generate function has been renamed makeIterator function.
  4. The collection protocol now requires the implementation of the function index, this function returns the value of the next index.

The sample code below should clarify

class CustomStack
{
    var data : [Element] = [Element]()

    func push(Element newElement : Element)
    {
        data.append(newElement)
    }

    func pop() -> Element
    {
        return data.removeLast()
    }
}

//Additional Implementations - not strictly required
extension CustomStack
{
    typealias Index = Int

    var startIndex : Index
    {
        return 0
    }

    var endIndex: Index
    {
        return (data.count - 1)
    }

    subscript (position : Index) -> Element
    {
        return data[position]
    }
}

extension CustomStack : Sequence
{
    typealias Iterator = AnyIterator

    func makeIterator() -> Iterator
    {
        var index = 0
        return AnyIterator({() -> Element? in
            if index < self.data.count
            {
                let res =  self.data[index]
                index += 1
                return res
            }
            return nil
        })
    }
}

extension CustomStack : Collection
{
    typealias SubSequence = CustomStack

    subscript (bounds: Range) -> CustomStack.SubSequence
    {
        let newStack : CustomStack = CustomStack()

        for i in bounds.lowerBound...bounds.upperBound
        {
            newStack.push(Element: data[i])
        }
        return newStack
    }

    /// Returns the position immediately after the given index.
    /// - Parameter i: A valid index of the collection. `i` must be less than
    ///   `endIndex`.
    /// - Returns: The index value immediately after `i`.
    func index(after i: Int) -> Int
    {
        if i < endIndex
        {
            return i + 1
        }
        return i
    }
}

When to use Swift & when to use Objective-C?

Over the past few years I have received a number of questions with regards to Swift & Objective-C. Specifically related to the future of the 2. I will try to address those questions in the form of an FAQ.

Should I learn Swift or Objective-C?

This is a question that I get from developers new to iOS/macOS App Development. Ideally speaking, you should learn Swift. As that is going to become the main language for App development on Apple’s ecosystem. However, the reality is a little different. There are a large number of applications that are written in Objective-C. You are likely to encounter them at your workplace. You may also have to maintain, upgrade & improve those apps. In such a case, it makes sense to learn Objective-C too.

Can I mix Swift & Objective-C in the same project?

Yes! But remember that you should check for feature compatibility between the 2 languages. Adding Swift code to an Objective-C project may not be very beneficial as only those features that are compatible with Objective-C can be written in Swift.

Going the other way round is not a problem. You can read more about that here:Mixing Swift & Objective-C

Will Objective-C be deprecated in the future?

That is an interesting question. There is no formal announcement from Apple stating the Objective-C is going to be deprecated. However, one can expect more attention to be paid to Swift. That is where most of the newest techniques, tools & technologies are going to be available. Objective-C will keep running as it is as of now.

Can I mix Swift with other Programming Languages?

Swift can easily be mixed with Objective-C. If you wish to incorporate C++ or C code in your Swift Project then wrapping them in Objective-C code allows you to achieve this.

Apart from that Swift does support working with C code code. You can read about that here:Interacting with C APIs.

Swift does not provide interoperability support for any other languages as of now.

Which version of Swift should I use?

It is recommended that you use the latest available version of Swift. However, the actual version that you work on depends on many other factors like: compatibility with OS Versions, support & business related choices.

Why shouldn’t we just convert all our Objective-C code to Swift and keep things simple?

A very tempting proposition. However, practical realities prevent us from doing this. The process of converting from Objective-C to Swift is time consuming. Apart from having to convert the syntax, the code also needs to be optimised taking into account the new features that are available. This will mean extensive testing and quality assurance. Most companies will not invest their resources into this endeavour.

A better approach is to migrate to Swift gradually. Here are some ways to do this:

  1. If its a brand new product/app that you are creating, start it in Swift.
  2. Any new reusable code components that are being created should be done in Swift (they should be Objective-C compatible if you intend to use this code in Objective-C projects).
  3. If any part of a product is going to undergo heavy change, either due to a bug fix or a new feature. This is a good time to convert it into Swift.

A good example is how Apple is approaching the process of migrating to Swift. They are doing it component by component.

I have been developing apps in Objective-C for some time. I am able to create any reasonably complicated app now. If Objective-C hasn’t been deprecated then should I start making apps in Swift?

This is a choice that you have to make. It is recommended that new apps (at the very least) be made in Swift as that is the language that will undergo the maximum amount of changes & improvements in the future.

What do you suggest as a trainer?

Another question that I get very often. It depends on the situation. I would say learn both Swift & Objective-C. You can skip learning Objective-C if you are confident that you will not have to work with any projects written in that language.

If I am starting on a brand new project I would use Swift. But if its an Objective-C project I would stick to Objective-C.

Can Swift development only be done on macOS?

No! Swift development can also be done on Linux. However, iOS/macOS/tvOS/watchOS App Development can only be done on macOS through Xcode.

How should I migrate to Swift?

There are different approaches that one can use. It all depends on the situation and needs of your organisation. Here are some things that you can do:

  • Start development of brand new apps (from scratch) in Swift.
  • If you are creating a brand new library which will be used for future projects then go ahead with Swift.
  • If a major component of an existing app is going to be changed significantly then you can go ahead with Swift.

You can do all or some of the above. There may be other strategies too. You should also factor in the cost of migration from one language to another.

 

Collection Type, Sequence Type & Indexable Type

This is for Swift Version 2.2 & earlier. I will be adding the snippet of code for the changes the Swift 3.x have introduced.

What are the Collection Type & Sequence Type Protocols?

The Collection Type, Sequence Type & Generator Type Protocols define rules that govern how different data structures or collections of data can be used, interacted with and operated within the Swift programming language. The CollectionType is a special case of the SequenceType.

Why do we need such Protocols?

Lets take the example of the Swift For-Loop.

var arrOfStrings : [String] = [String]()

arrOfStrings.append("Jill")
arrOfStrings.append("Jack")
arrOfStrings.append("John")
arrOfStrings.append("Jane")

for name in arrOfString
{
     print("The name is \(name)")
}

Now, if we have created our own data type. We would not be able to use the above for-loop as it would not conform to the … type protocols. The for-loop is expecting a data structure that acts and behaves in a way that is governed by the … protocols.

Just like the for-loop example above there are many other features within the Swift Programming Language that expect data structures to act and behave in a particular way. By designing our data structures to conform to these protocols we can make the easily compatible with the existing code and language features out there.

How do we use these protocols for our own data structures?

First we need to decide what kind of collection are we making. For the sake of this example I will create a Custom Stack.

class CustomStack<Element>
{
    var data : [Element] = [Element]()

    func push(Element newElement : Element)
    {
        data.append(newElement)
    }

    func pop() -> Element
    {
        return data.removeLast()
    }
}

The above code is very simple for the purpose of this exercise. Its a stack. Which is internally really an Array. It has functions to push data and pop data. We are now going to convert this type to a collection to conform to the CollectionType protocol.

Implementing the Indexable Protocol methods

As a first step we are going to make our CustomStack conform to the Indexable Protocol.

extension CustomStack : Indexable
{
    //INDEXABLE PROTOCOLS
    typealias Index = Int

    var startIndex : Int
    {
        return 0
    }

    var endIndex: Int
    {
        return (data.count - 1)
    }

    subscript (position : Int) -> Element
    {
        return data[position]
    }
}

The above change makes the data structure conform to the Indexable protocol. This is a requirement for it to be of type CollectionType. In order to conform to the Indexable protocol we need to implement a few computed properties. Let us look at the changes

typealias Index = Int

This line informs the system that the Indexing type for my data structure is an Int.

var startIndex : Int
{
    return 0
}

var endIndex: Int
{
    return (data.count - 1)
}

The next 2 are computed properties. Each provides the implementation of the startIndex  and endIndex properties. Note that the type for both is Int as we have declared the Index type earlier as Int.

subscript (position : Int) -> Element
{
    return data[position]
}

The last implementation is of subscript. This provides the implementation to access an Element from the Stack using the Subscript operator.

Implementing the Sequence Type Protocol

Next we will implement the Sequence Type Protocol methods.

extension CustomStack : SequenceType
{
    typealias Generator = AnyGenerator<Element>
    
    func generate() -> Generator
    {
        var index = 0
        
        return AnyGenerator(body: {() -> Element? in
            if index < self.data.count
            {
                let res =  self.data[index]
                index += 1
                return res
            }
            return nil
        })
    }
}

Let us examine this code line by line.

typealias Generator = AnyGenerator<Element>

Objects of type Generator allow us to navigate through our collection. Quite like how iterators  work in C++. This line specifies the type to be AnyGenerator for Elements.

func generate() -> Generator

Next we start the implementation of the generate function. This is required as part of the SequenceType protocol.

var index = 0

This index variable is used to track the element that is currently being accessed.

return AnyGenerator(body: {() -> Element? in
            if index < self.data.count
            {
                let res =  self.data[index]
                index += 1
                return res
            }
            return nil
        })

The return statement is the main statement. Here we are creating an object of type AnyGenerator. As an argument to the constructor call we are passing in a closure that will be used to iterate through the sequence. Note that the closure captures the index variable and holds a reference to its value even though we have left the original function.

Implementing the Collection Type Protocol

Next we will implement the Collection Type Protocol methods. We don’t really need to implement a lot in order to conform to the CollectionType protocol. In fact, if we just conform to the CollectionType protocol and use the implementations of the previous 2 extensions we should be just fine. However, for the sake of demonstration we are implementing the subscript functionality within the CollectionType.

extension CustomStack : CollectionType
{
    typealias SubSequence = CustomStack<Element>
    
    subscript (bounds: Range<CustomStack.Index>) -> CustomStack.SubSequence
    {
        let newStack : CustomStack<Element> = CustomStack<Element>()
        
        for i in bounds.startIndex...bounds.endIndex
        {
            newStack.push(Element: data[i])
        }
        return newStack
    }
}

Let us look at the code line by line.

typealias SubSequence = CustomStack<Element>

Again, as before this line indicates that the SubSequence type is actually a CustomStack.

subscript (bounds: Range<CustomStack.Index>) -> CustomStack.SubSequence

Here we start the implementation of the subscript functionality.

let newStack : CustomStack<Element> = CustomStack<Element>()
        
for i in bounds.startIndex...bounds.endIndex
{
     newStack.push(Element: data[i])
}
return newStack

The rest of the code is the implementation of the subscript range behaviour. One can have different implementations to achieve the same result.

CollectionType Video

Conclusion

As we can see, by designing our data structure to conform to a particular set of protocols. We have made it possible for our data structure to take advantages of the different features, functionalities and even API’s available within the Swift Language and the Frameworks used as a part of iOS, macOS, watchOS & tvOS development.