Pie Progress View (iOS and macOS)

Following on from my previous post about cross platform development, I wanted to showcase a simple view/layer implementation I recently built for an app I’m working on.

The design requires a pie chart to represent progress. I initially built it using CAShapeLayer however this quickly proved to be the wrong approach since the shape layer would attempt to ‘morph’ between values, rather than simply adjust the chart.

At this point I decided to move to a more custom approach. I also knew this would lend itself nicely to cross platform development, since layer’s are generally identical across both iOS and macOS.

I plan on doing some more in-depth posts that go into the deeper details of making a cross-platform application, but for the purposes of this post I’ll keep things short.

Cross Platform

I generally write the code for one platform first, one file at a time. Then assess the requirements to work out any extensions, typealias‘s, etc.. I may need.

On iOS its relatively simple to get a cgPath from a UIBezierPath however on macOS, we need to do this ourselves. So I wrote an equivalent extension.

#if os(OSX)
import AppKit

public extension NSBezierPath {
    var cgPath: CGPath { /* implementation */ }
}
#endif

Next up, I needed some typealias‘s to make it easier to use a single call throughout my code.

#if os(OSX)
    import AppKit
    public typealias Color = NSColor
    public typealias View = NSView

    private func scale() -> CGFloat {
        return NSScreen.main?.backingScaleFactor ?? 1
    }

    extension NSView {
        internal var nonOptionalLayer: CALayer {
            return layer!
        }
    }
#else
    import UIKit
    public typealias Color = UIColor
    public typealias View = UIView

    internal func scale() -> CGFloat {
        return UIScreen.main.scale
    }

    extension UIView {
        internal var nonOptionalLayer: CALayer {
            return layer
        }
    }
#endif

Coordinate System

One of the most interesting differences between iOS and macOS development, is that the y-coordinates are inverted. Where iOS expects the origin to be top-left, macOS expects the origin to be bottom-left.

To normalise the origin for both platforms, NSView provides a simple isFlipped property. Returning true for this property, moves the origin to the top-left, matching what we’d expect on iOS.

public override var isFlipped: Bool {
    return true
}

Progress View

The code itself is extremely simple. We simply subclass CALayer to provide our own drawing for each frame. This allows us to animate the progress when its value changes. We even get all the benefits of implicit animation, etc…

I then wrapped the layer with a custom (UI/NS)View, added some IBDesignable support and some configurable properties to make it easier to work with.

Lastly I added a setProgress(progress:animated:) function which was as simple as disabling the implicit animation when animated == false.

public func setProgress(_ progress: CGFloat, animated: Bool) {
    guard animated else {
        CATransaction.setDisableActions(true)
        self.progress = progress
        CATransaction.setDisableActions(false)
        return
    }

    self.progress = progress
}

Summary

Building a cross platform component isn’t too difficult. Focusing on UI components at this level, allows the greatest amount of reuse as well. Adding storyboard/XIB support also aides the user of the component.

Scheduling Services

A micro-post about a micro-framework. Schedule (possibly repeating) events for some time in the future, along with restoration support. An NSNotification-based scheduling service in Swift.

Requirements

  • Schedule a request
  • Cancel a request
  • Restore all requests upon app-launch
  • Pause/resume all requests

Essentially I wanted to remove the knowledge/management of a timer. Obviously the implementation would need to handle this, but I wanted to build an abstraction on top of this that would support all of my requirements, while providing a simple/clean API.

Scheduling Service

The result is a lightweight micro-framework – SchedulingService.

// schedule a request
let request = service.schedule(date: Date().addingTimeInterval(5))

// cancel the request
service.cancel(request)

// schedule an NSNotification
service.schedule(date: Date().addingTimeInterval(20), 
         notification: .CountdownDidUpdate)

Codable

I also wanted to be able to save/restore all scheduled events so that I wouldn’t need to restore them manually upon launch.

// decodes or makes a new Service
let store = SchedulingServiceStore(service: nil)

// encodes the current service
store.service.save()

Super simple, single file micro-framwork.

View Descriptors

When building our apps, its fairly well known that passage models into our views leads to bad things. I don’t want to say its a black and white POV but its rarely a good idea or even necessary to begin with.

An often recommended approach is to use a ViewModel or Extension to add properties that provide UI representations of our models data. I’d like to suggest a slight alternative that testable and scalable even across large projects.

Example

Lets say we have a simple type to represent a Product. We might have a Model similar to the following:

struct Product {
    let title: String
    let price: Decimal
}

A product can have a title and a price. We don’t want to lose our underlying types, but at some point we need to present the price to the user in their local currency.

View Model

One solution to this problem is to introduce a View Model.

struct ProductViewModel {
    let title: String
    let price: String
}

While this approach is highly testable and allows you to abstract away the presence of the original Model, it also comes with some caveats.

  1. The name of the ViewModel is tightly coupled to the original Model itself, and so refactoring could be painful to keep these names in sync. This is the #1 issue I’ve faced myself.
  2. Often leads to some duplication where the value can be mapped directly.

Swift Extension

Another solution is to simply extend the Model itself.

extension Product {
    var priceString: String? {
        let decimal = NSDecimalNumber(decimal: product.price)
        let formatter = NumberFormatter()
        formatter.numberStyle = .currency
        return formatter.string(from: decimal)
    }
}

While this approach isn’t inherently bad – as long as you keep it immutable – one of the biggest issues arises when you start naming things. You can already see that we now have a price as well as a priceString variable to make it clear that the latter returns a string representation of the price.

Descriptors

An alternative approach I’ve come up with that I’ve battle tested and had proven success across even large projects, is something I call View Descriptors.

Its basically a merge of the above concepts. Where the Descriptor is a dedicated type that’s added to your model type through an extension.

extension Product {
    struct Descriptor {
        private let product: Product

        init(product: Product) {
            self.product = product
        }
    }

    var descriptor: Descriptor {
        return Descriptor(product: self)
    }
}

Now we have a dedicated type that can better describe our model to the UI. Lets add a currency specific price value to our descriptor.

extension Product.Descriptor {
    var price: String? {
        let decimal = NSDecimalNumber(decimal: product.price)
        let formatter = NumberFormatter()
        formatter.numberStyle = .currency
        return formatter.string(from: decimal)
    }
}

We can now access our model’s values as such:

let product = Product(title: "iPhone X", price: 1_000)
product.price
product.descriptor.price

// prints
// 1000
// £1,000.00

Conclusion

Compared to the approaches above, I feel this solution provides the best of both worlds, with almost none of the caveats.

  1. No need to sync type names (i.e. Product ‣ ProductVM)
  2. No unnecessary duplication
  3. API is much more descriptive – allowing variable name reuse
  4. Still highly testable API
  5. Really easy to migrate/refactor existing models in an incremental fashion

My favourite thing about this solution is that the model and the descriptors are implicitly tied together, while still providing unique interfaces for their specific domain.

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.

Behaviour Composition with Storyboard’s

Applications are made up of multiple interactions and behaviours. Often times, our view controllers and views end up bloated because of the shear number of interactions that can occur. Furthermore, this makes testing our behaviours even more complicated since they are tied to our view layers.

If you just want to jump straight into the source, you can find a demo project @ github.com

The Deck

Here’s a deck that better explains the concept. Don’t forget to checkout the code and please share 👍

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.

Generic Delegate’s in Swift

A common pattern in cocoa development is the use of the delegate pattern. Most commonly used as a callback mechanism.

In Swift however sometimes we would like to use this pattern along with a generic implementation of our type. First, lets look at the problem.

The Problem

A typical approach would look like this:

struct Item { }

protocol ItemDelegate: class {
    func didSelect(item: Item)
}

final class ItemController {
    weak var delegate: ItemDelegate?
}

This works great while Item is a concrete type, but what if we wanted to make Item generic?

protocol Item { }
struct MyItem: Item { }

final class ItemController<ItemType: Item> {
    weak var delegate: ItemDelegate?
}

Well we can simply make our delegate generic as well right?

protocol ItemDelegate: class {
    func didSelect<ItemType: Item>(item: ItemType)
}

Well actually this isn’t right. To see the problem lets check out the callsite:

let controller = ItemController<MyItem>()
func didSelect<ItemType>(item: ItemType) where ItemType : Item {
    print("\(item)")
}

Specifically where ItemType : Item. Item is our protocol and not our concrete type MyItem. So even though we’ve successfully constrained our controller to a specific type, our delegate is much less specific.

The Solution

What we really want to do is define an associatedtype on our delegate.

protocol ItemDelegate {
    associatedtype ItemType: Item
    func didSelect(item: ItemType)
}

The problem is that now we have a generic constraint on our ItemDelegate. The solution is actually quite simple. Instead of constraining our ItemController to a specific Item, instead constrain it to a specific ItemDelegate.

Our final solution would look like the following:

protocol ItemDelegate: class {
    associatedtype ItemType: Item
    func didSelect(item: ItemType)
}

final class ItemController<Delegate: ItemDelegate> {
    weak var delegate: Delegate?
    func addItem(item: Delegate.ItemType)
}

Now if we look at our callsite:

let controller = ItemController<MyItem>()
func didSelect(item: MyItem) {
    print("\(item.name)")
}

Recap

By moving the generic constraint to the delegate we can now safely define an instance of ItemController and ensure our delegate callbacks are type-safe to the same concrete instance.

This is a fairly simple approach but makes working with generic delegate’s much simpler and much safer.