Working with IndexPaths

IndexPath’s provide an abstraction over the indexing of our structured data. They are used by common controllers and views, such as UICollectionView, UITableView and NSFetchedResultsController.

IndexPath is basically just an array of integers that defines a ‘path’ that can be used to represent your data. Lets take a look at a simple, but common example.

I’m going to start off my showing simple examples so if you want to jump straight into the more interesting stuff, scroll past the next section 🙂

Quick Example

Just to ensure we’re all on the same page, lets look at an example where we have a contacts application, with each contact grouped by their company name.

Apple:
  - Steve Jobs
  - Alan Kay
  - Andy Hertzfeld
Microsoft:
  - Bill Gates
  - Paul Allen
Google:
  - Larry Page
  - Sergey Brin

If I wanted to reference Paul Allen I could create an IndexPath as such:

IndexPath(item: 1, section: 1) // Paul Allen

So as we can see defining IndexPath’s is actually quiet simple. What about using an IndexPath to find Alan Key?

dataSource
    .companies[indexPath.section]
    .employees[indexPath.item]

Easy peasy!

Adavanced Indexing

So as we’ve seen, using a single IndexPath in isolation is really simple. What if I wanted to use my current IndexPath to find the employee in a list, before the this one?

Well, on its own we can’t really do that because IndexPath‘s don’t have a reference to other IndexPath‘s. Which makes sense because they can be used in any contexts.

More importantly the fact that our grouping is 1 level deep and specific to the company name, is specific to the implementation in a specific part of an application.

So when would we ever want to do this in the first place?

Introducing UICollectionViewLayout

Have you ever implemented a UICollectionViewLayout (or FlowLayout) and realised how complicated it becomes to keep track of all of your IndexPath‘s? It gets even more complicated when you have to deal with headers, footers and perhaps even more supplementary and decoration views.

Recently I began implementing a custom layout for the 5th or 6th time and decided I really wanted to make dealing with IndexPath‘s a lot simpler than it had been in the past. Specifically I wanted the following features:

  1. To be able to iterate over all of my dataSource’s IndexPath‘s
  2. To be able to query my dataSource for specific indexPaths
  3. To be able to easily differentiate the various kinds of IndexPath‘s I use (i.e. headers, footers and items)

Table Layout

For simplicity lets think about how we would build a simple layout that looks like a UITableView, with section headers and footers as well as the items for each section.

So we discussed above how IndexPath‘s don’t know about other IndexPath‘s so how do we plan on solving that? Well luckily enough our DataSource does know about all of our IndexPath‘s, so we can easily write an extension on Set to fetch all of our IndexPath‘s for us.

Easy peasy!

Adavanced Indexing

So as we’ve seen, using a single IndexPath in isolation is really simple. What if I wanted to use my current IndexPath to find the employee in a list, before the this one?

Well, on its own we can’t really do that because IndexPath‘s don’t have a reference to other IndexPath‘s. Which makes sense because they can be used in any contexts.

More importantly the fact that our grouping is 1 level deep and specific to the company name, is specific to the implementation in a specific part of an application.

So when would we ever want to do this in the first place?

Introducing UICollectionViewLayout

Have you ever implemented a UICollectionViewLayout (or FlowLayout) and realised how complicated it becomes to keep track of all of your IndexPath‘s? It gets even more complicated when you have to deal with headers, footers and perhaps even more supplementary and decoration views.

Recently I began implementing a custom layout for the 5th or 6th time and decided I really wanted to make dealing with IndexPath‘s a lot simpler than it had been in the past. Specifically I wanted the following features:

  1. To be able to iterate over all of my dataSource’s IndexPath‘s
  2. To be able to query my dataSource for specific indexPaths
  3. To be able to easily differentiate the various kinds of IndexPath‘s I use (i.e. headers, footers and items)

Table Layout

For simplicity lets think about how we would build a simple layout that looks like a UITableView, with section headers and footers as well as the items for each section.

So we discussed above how IndexPath‘s don’t know about other IndexPath‘s so how do we plan on solving that? Well luckily enough our DataSource does know about all of our IndexPath‘s, so we can easily write an extension on Set to fetch all of our IndexPath‘s for us.

Before we do that lets also add an extension to IndexPath to support a property called kind which will allow us to determine whether we’re dealing with a header, footer or item.

internal extension IndexPath {

    enum Kind: Int {
        case header
        case footer
        case item
    }

    init(kind: Kind, item: Int, section: Int) {
        switch kind {
        case .header:
            self = [section, -1, 0]
        case .footer:
            self = [section, item, 1]
        case .item:
            self = IndexPath(item: item, section: section)
        }
    }
}

You may notice we’re specifying 3 index values for our custom IndexPath‘s. The first index represents the section, the second represents our item and the last value represents whether this is a header or a footer.

For all other IndexPath‘s we use the standard convenience method.

Data Source

Now we have a better way to represent our IndexPath‘s, we can add an extension to Set that will collect all of the IndexPath‘s from our DataSource, in this case our UICollectionView.

public extension Set where Element == IndexPath {

    init(from view: UICollectionView) {
        var indexPaths: Set<IndexPath> = []
        let sectionCount = view.dataSource?.numberOfSections?(in: view) ?? 0

        for section in 0..<sectionCount {
            let headerIndexPath = IndexPath(
                kind: .header,
                item: -1,
                section: section
            )
            indexPaths.insert(headerIndexPath)

            let itemCount = view.dataSource?
                .collectionView(view, numberOfItemsInSection: section) ?? 0

            for item in 0..<itemCount {
                let itemIndexPath = IndexPath(
                    item: item,
                    section: section
                )
                indexPaths.insert(itemIndexPath)
            }

            let footerIndexPath = IndexPath(
                kind: .footer,
                item: itemCount, 
                section: section
            )
            indexPaths.insert(footerIndexPath)
        }

        self.init(indexPaths)
    }
}

If we were to manually configure our IndexPath‘s we would end up with something like this.

// Apple
IndexPath(kind: .header, item: -1, section: 0),
IndexPath(item: 0, section: 0),
IndexPath(item: 1, section: 0),
IndexPath(item: 2, section: 0),
IndexPath(kind: .footer, item: 3, section: 0),
// Microsoft
IndexPath(kind: .header, item: -1, section: 1),
IndexPath(item: 0, section: 1),
IndexPath(item: 1, section: 1),
IndexPath(kind: .footer, item: 2, section: 1),
// Google
IndexPath(kind: .header, item: -1, section: 2),
IndexPath(item: 0, section: 2),
IndexPath(item: 1, section: 2),
IndexPath(kind: .footer, item: 2, section: 1),

Querying

So now we have an array of IndexPath things get a lot simple. First of all, we can easily sort our array and because our custom IndexPath initializer ensures all paths will order correctly.

Secondly we can easily query our array for the IndexPath‘s we care about now. So lets take a look at an extension that will allow us to query our IndexPath‘s.

func filter(include kinds: IndexPath.KindMask, inSections sections: [Int]) 
    -> [SubSequence.Element] {
        return filter { sections.contains($0.section) && kinds.contains($0.kind) }
}

With this one simple function, we can query as such:

// fetch all IndexPath's for headers and items only 
// in the first 2 sections

indexPaths
    .sorted()
    .filter(include: [.header, .item], inSections: [0, 1])

Now that we have a simple flat array of IndexPath‘s we can also easily iterate over it invarious ways:

// Use an iterator
var iterator = indexPaths.makeIterator()
iterator.next()

Since we’re dealing with a simple array now, we can even index into the array to get the next/previous IndexPath without having to consider what section we’re in and whether or not we’re the first/last item in that section, etc…

Summary

IndexPath‘s are a great way to represent your data but can sometimes become unwieldly as your structures get more complex.

Hopefully this has given you some interesting ideas to help simplify your approaches and implementations as well.

You can download the playground to give it a try, or you can checkout the GIST here.

Peek… behind the curtains

Update: new statistics added below

So you spent a long time building something and you think its worthy. You feel strongly that you’ve built something everyone can benefit from. So how do you improve awareness, generate usage and build a community around your product?

Well I’m not going to lie to you. I didn’t have a plan, so [Peek] was ready 2 weeks beforehand but I didn’t want to release it unless I could do it right.

So I had to do some research to see how the pros did it. I had to make a plan and I had to learn how to edit a video!

Contents

  1. The plan
  2. The video
  3. The README
  4. The product page
  5. The blog
  6. Social Networking
  7. The release
  8. Statistics

The plan

I started with a list:

  1. Make the README more approachable for newcomers
  2. Write a blog postnot a copy of the README
  3. Make a product page – on my website
  4. Make a video – including various output resolutions
  5. Make a GIF – useful where embedding isn’t possible and also allows viewers to see a preview/clickbait
  6. Publish on my Blog, Twitter, Instagram, Dribbble, Vimeo, etc…

The video

The video was kind of obvious, its a visual medium to help understanding, you can expose a lot more functionality with a series of pictures than you ever could with a single screenshot or a limited number of characters.

I decided I wanted something that looked like it was made by Apple. That was obviously a little out of reach 🤪 but that was the goal anyway. So I set out to learn Apple Motion and Final Cut Pro X to edit the video myself. I also purchased an awesome backing track that I felt best fit the design, story, etc…

Finally, I exported a short preview version of the video as an animated GIF since not all sites allow embedding and besides, I wanted viewers to see something that would hopefully entice them immediately.

Making the video - If you’re interested in how I made the video & GIF, I’ll do a follow up post in the future. Let me know in the comments or on Twitter.

The README

Less obvious were the small changes I made, including the tweaks to the README. I carefully ordered the content of that page and also added the animated GIF version of the Video since GitHub doesn’t allow embedding. I also made sure this GIF went at the top, so that mobile clients would also see it immediately.

Previously I had the changelog at the top of the file, which is useful if you’re working on the project or actively monitoring it. In reality however this was a waste of time, so I moved it to the end of the file.

I needed to target newcomers. So I moved the GIF up top and added a new entry that went over the top features and how to get started. I don’t have metrics on this, but I’d be surprised if this hasn’t helped people better understand what Peek is and how easy it is to start benefitting from its use.

The product page

Luckily I use Square Space for my hosting, so making a quick and dirty promo page was quite trivial. The tricky part was deciding what image I wanted to fill the page with, and which screenshot to use as the video placeholder.

In my case, I made the video first, then looked for frames that made sense. However in the end I decided to make a dedicated high-resolution mockup of Peek running on a device.

The blog

Previously when I released my open sourced projects, I’d basically just take a copy of the README and post that on my blog as well. This may seem productive at the time, but I’ve actually found it can be quite detrimental.

The point of the blog post – as well as the dedicated product page – is to drive traffic back to your website. This is super important to build a reputation and awareness of you as a developer, producing quality Open Source Software.

More importantly it will help you build a community as well. Some will contribute back to your projects, others will simply be users.

Social networking

So I finally had all my marketing material, the product page was ready, blog post written – all that was left was to publish everything online for people to see. Plus I had to publish to Cocoapods 😬

Thanks to Twitter’s new 280 character limit, I was able to include all the top features in my Tweet as well. Here’s a snapshot of that first tweet:

Anatomy of the Tweet

I just wanted to pay special attention to this tweet, as I’ve spent a lot of time finessing the structure of my product releases when it comes to Twitter to improve my initial exposure and chance of interaction.

  1. Add a title, nice and short
  2. Make sure to include a link – I found near the top works well
  3. Include a short feature list – or short paragraph if that fits better
  4. Add appropriate hashtags – remember your target audience isn’t always other developers
  5. Most importantly a screenshot or GIF – I can’t stress this one enough!

The release

So everything was ready, how did I manage the release?

I started by uploading the video, this can take time to process and become available so its worth preparing first.

The blog post and product page were already up, just not public yet so I could switch them on at any time.

I also prepare my tweet, instagram post, etc… to keep things consistent but platform specific where appropriate.

Once all the content was prepared, I pushed to Cocoapods and it was released!

Statistics

Its been 5 days since the release and I’m happy to report:

GitHub

After 3 days

  • #1 – Swift projects
  • #4 – Overall
  • #7 – My Profile
  • Over 1700 stars
  • 4 new PRs, 57 Forks

After 5 days

  • #1 – Swift projects
  • #4 – Overall
  • #3 – My Profile
  • Over 2000 stars
  • 11 new PRs, 72 Forks

Website

  • Over 4000 unique visitors on day 1
  • ~400 daily since

Vimeo

After 3 days

  • Over 3000 views so far

After 5 days

  • Over 4000 views so far

Miscellaneous

  • Peek was also featured in iOSDevWeekly for the second time (plus a direct mention on the following Tuesday)
  • Promoted across various other channels and newsletters/lists
  • A few new followers on Twitter and GitHub

Overall I’d say it been a really exciting 5 days. I’m not one of the iOS Elite and I’m not speaking at any events (although I’m interested in doing so). All of that aside, I’ve been able to make a simple tool and get people to notice.

I hope this post gets you a little more excited about marketing your own apps and hopefully gives you some insights into how you can improve awareness of your own projects.

If you’d like to discuss more, ask questions or make suggestions for my next big release, please get in touch or comment below.

Advanced Gesture Recognisers

Through this article I’m going to show you how to use a UIPanGestureRecognizer to create a marquee selection tool that you can use in any of your UIView’s.

Gestures

UIGestureRecogniser’s are a really powerful way of working with touch events in iOS. They allow us to handle taps, pans, pinches and more. Through a simple state-driven API we can easily use them to detect lots of types of interactions in our apps.

Marquee Selection



Finder

Similar to what you would find in Finder or another file browser.

Recently I came across a feature I needed to build. Basically I needed to provide a marquee selection tool like you might see in Finder for selecting files and folders.

UIPanGestueRecognizer seemed like a perfect fit for the job since it provides location data while the you move your finger across the view. So I added one to my view and proceeded to write some code for handling the marquee itself.

Typical Solution

So I started off by checking the recognizer state and recording the initial location when state == .began

I then used location(in view:) while the gesture’s state == .changed — finally creating a rectangle between the two points.

At this point I had a working marquee tool but I was drawing the rectangle using draw(rect:) of the gesture.view, which wasn’t ideal, especially because it meant I couldn’t draw on ‘top’ of the subviews.

So I decided to add a view instead based on the gesture’s state.

.began – add the marqueeView to the source view
.changed – set the frame of the selection view
.ended – remove the selection view from the source view

At this point however I realised that this solution wasn’t ideal since I was now tied to this specific implementation of UIView. Furthermore, if I wanted to provide selection to something like a UICollectionView then I would have to copy/paste a lof of code.

Alternative Solution

The solution I came up with was to move the marqueeViewinto the gesture itself. In hindsight this seemed obvious. The gesture has everything I need to provide a selection rectangle. Its state-driven, provides the location of the touch events during a pan, and even provides me with the source view.

All I had to do was listen for the various state changes and insert/remove my marqueeView appropriately. Lets checkout an example.

import UIKit.UIGestureRecognizerSubclass

public final class MarqueeGestureRecognizer: UIPanGestureRecognizer {
    public private(set) var initialLocation: CGPoint = .zero
    public private(set) var selectionRect: CGRect = .zero
    public var tintColor: UIColor = .yellow
    public var zPosition: Int = 0

    public override init(target: Any?, action: Selector?) {
        super.init(target: target, action: action)
        addTarget(self, action: #selector(handleGesture(gesture:)))
    }

    private var marqueeView: UIView? {
        didSet {
            marqueeView?.backgroundColor = tintColor.withAlphaComponent(0.1)
            marqueeView?.layer.borderColor = tintColor.cgColor
            marqueeView?.layer.borderWidth = 1
            marqueeView?.layer.zPosition = CGFloat(zPosition)
        }
    }

    @objc private func handleGesture(gesture: MarqueeGestureRecognizer) {
        guard let view = gesture.view else { return }

        let currentLocation = gesture.location(in: view)
        selectionRect = CGRectReversible.rect(from: initialLocation, to: currentLocation)

        switch gesture.state {
        case .began:
            let marqueeView = UIView()
            view.insertSubview(marqueeView, at: zPosition)
            self.marqueeView = marqueeView
            initialLocation = currentLocation
        case .changed:
            marqueeView?.frame = selectionRect
        default:
            initialLocation = .zero
            selectionRect = .zero

            marqueeView?.removeFromSuperview()
            marqueeView = nil
        }
    } 
}

For the full code, you can download the Playground on GitHub:

Summary

Subclassing a UIGestureRecognizer and adding behaviour to it has a lot of advantages:

  • You can easily composite this into any view
  • You can add advanced behaviour to your gesture; e.g.
  • Hold down a second finger to constrain aspect ratio
  • The marqueeView’s lifecycle is bound to the state of the gesture; i.e.
  • no need to manage it from your view controller, etc…
  • Gesture’s are state-driven by default, which makes them great to work with.

Have a cool idea for another gesture recognizer? Maybe you’ve done something similar before? I’d love to hear about it.

Introducing Peek 2.0

Version
License
Language
Platform

Peek is an open source library that lets you easily check your application against your user interface’s specification guide(s). Peek can be used by engineers, designers & testers, allowing developers to spend more time on code and less time checking that fonts, colors and layout are pixel perfect.

How does it work?

Tap your volume key(s) to activate/deactivate Peek.

Then tap, or drag your finger across the screen to select a component and see its layout information.

Double-tap anywhere on the screen to bring up the Peek Inspectors. Here peek will show you contextual information about the view you’re inspecting. For example, a label will show information such as font and textColor, whereas an image might show information about its size or scaling mode.

You can also tap and hold on any property to copy it to your pasteboard. More features coming soon regarding this — which will allow you to report issues back to your dev(s).

Peek currently supports a single overlay mode, but more are coming soon – including an overlay that allows you to see measures between components!

How do I get started?

Peek was designed to be extremely easy to use.

If you’re a designer, send a link to this page to your dev(s) 😁

For developers, simply use CocoaPods (alternativel you can simply drag the files into your project.

pod 'Peek'

Then add a single line to your App Delegate:

window?.peek.enabled = true

Optionally, I’d suggest adding the DEBUG Swift compiler flag and changing the code to:

#if DEBUG
  window?.peek.enabled = true
#endif

You can also provide options when enabling Peek:

window?.peek.enableWithOptions { options in
  // Peek will require a shake on the device and simulator
  options.activationMode = .shake

  // Container views (UIView) will not be ignored
  // Note: this does not include system containers
  options.shouldIgnoreContainers = false
}

Now whenever you run your app, you can now activate/deactivate Peek by pressing one of the volume keys on your device 😉

Safety First

Peek is designed to be as safe as possible. Peek will never retain objects from your application. It will never use a background thread. Peek won’t even run unless you explicitly enable it!

Go ahead, take a Peek at your app now 🙂

What about using Peek from the simulator?

Since the volume keys are not available on the Simulator, we can use a shake gesture instead. Simply forward the motion events to Peek by adding the following code to your AppDelegate.swift:

override func motionBegan(motion: UIEventSubtype, withEvent event: UIEvent?) {
  window?.peek.handleShake(motion)
}

Now you can press CMD+CTRL+Z (or use the Menu option) to activate/deactivate Peek from your Simulator.

Supported Platforms and Versions

Peek is tested and supported on iOS 8 and above. Its also iPhone and iPad supported, in all orientations. In fact, Peek will only rotate if your app can 😉

About Peek

  1. Peek only requires a single line of code to get started
  2. Peek is disabled by default, so just make sure to use #if DEBUG to prevent it from being included in your release binary
  3. No 3rd party code or libraries are used in Peek — all code is my own!
  4. Peek never interferes with your apps normal behavior, gestures or layout – and it NEVER retains an object from your application
  5. Peek supports all orientations and devices.

How does Peek work?

Peek scans all the views in your entire view hierarchy that is currently on the screen then overlays these views with layout information.

Peek then uses its filtering system to best determine which views you care about and those that you are not likely to be interested in.

For example, by default Peek will not show you many of Apple’s system components unless they are subclassed. A label however is a perfect exception to this, where you might not want to see all labels in a tab-bar but you do want to see the labels inside a cell.

Peek presents itself in its own window that sits directly on top of your own app’s user interface to ensure that it doesn’t interfere with normal functionality.

Peek also allows you to test all supported orientations on both iPhone and iPad.

Roadmap

There are a lot more features still to come, here’s a short preview I think are quite interesting:

  • Slack Integration (post issues to a Slack channel)
  • Image crop indicators
  • Auto Layout overlays