• Using Swift’s Enums for Quick Actions
  • This article is authorized by the original author Jordan Morgan
  • The Nuggets translation Project
  • Translator: DeadLion
  • Proofread by: Graning, Cbangchen

Perfect implementation of 3D Touch

I’m not sure if the creators of Swift could have anticipated the level of enthusiasm they would generate for what would be an incredibly beautiful language. Suffice it to say that the Swift community has grown and the language has stabilized to the point that there is now even a term for the bright future of Swift programming.

Swifty.

“That code isn’t Swifty”, “This should be more Swifty”, “This is a Swifty pattern”, “We can make This Swifty”. Take your breath away or something)

These words of praise will continue to grow. While I’m not a big advocate of such praise, I can’t really find an alternative to praising the “beauty” of writing shortcuts to 3D Touch with Swift.

This week, let’s take a look at UIApplicationShortcutItem implementation details, Swift is how to make us become a “first-class citizen”.

Implementation scheme

When a user starts a shortcut on the home screen, one of two things happens. Applications can invoke the specified function to handle the shortcut, or fast dormancy restart — which means that ultimately through the familiar didFinishLaunchingWithOptions to execute.

Either way, developers often depending on the type of UIApplicationShortcutItem attributes to decide which one to use.

if shortcutItem.type= ="bundleid.shortcutType"
{
    //Action triggered
}Copy the code

The above code is correct, but it is ok to use it only once in a project.

Unfortunately, even with the added benefit of switch conditions using string instances in Swiftosphere**™**, this approach quickly became tedious as more and more shortcuts were added. It is also well documented that using string literals in this case may be a waste of time:

if shortcutItem.type= ="bundleid.shortcutType"
{
    //Action triggered
}
else if shortcutItem.type= ="bundleid.shortcutTypeXYZ"
{
    //Another action
}
//and on and onCopy the code

Handling these shortcuts is like a small slice of your code base, though – Swift handles them better and more securely. So let’s see how Swift works its magic to give us a better alternative.

Enum .Fun

To be honest, the Swift enumeration is “crazy.” When Swift was released in 2014, IT never occurred to me that you could use attributes, initialize and call functions in enumerations, but now we do.

Anyway, we can use them at work. When you consider support UIApplicationShortcutItem implementation details, several key points should be paid attention to:

  • Must passtypeProperty to give the shortcut a name
  • According to the Official Apple guidelines, these operations must be prefixed with the Bundle ID
  • There may be multiple shortcuts
  • Specific actions based on type may be taken at multiple locations in the application

Our game plan was simple. Instead of hard-coding a string literal, we initialize an enumerated instance to indicate that this is the shortcut to be called.

The specific implementation

After making up two shortcuts and adding an extra one to each, we are now represented by an enumeration.

enum IncomingShortcutItem : String
{
    case SomeStaticAction
    case SomeDynamicAction
}Copy the code

If we were doing Objective-C, we might end up here. I think it’s accepted that using enumerations is far superior to using string literals. However, for the application of each operation type attribute specifies the bundle id for the prefix (for example, com. Dreaminginbinary. MyApp. MyApp), USES some string interpolation is still the best solution.

However, because Swift enumeration is super powerful, we can use it to implement it in a very neat way:

enum IncomingShortcutItem : String
{
    case SomeStaticAction
    case SomeDynamicAction
    private static let prefix: String = {
        return NSBundle.mainBundle().bundleIdentifier! + "."(1)}}Copy the code

Look! Badly!!!! We can safely retrieve the application package path from the calculation property. Recalling last week’s article, which mentioned insert values at the end of the introduction to closures, we wanted to assign prefixes to the return statements of closures, not to the closure itself.

Best model

In the end, we’ll use two of our favorite Swift features. When creating an initialization function for an enumeration that might fail, use guard statements to clear null values to be safe.

enum IncomingShortcutItem : String
{
    case SomeStaticAction
    case SomeDynamicAction
    private static let prefix: String = {
        return NSBundle.mainBundle().bundleIdentifier! + "."
    }()

    init?(shortCutType: String)
    {
        guard let bundleStringRange = shortCutType.rangeOfString(IncomingShortcutItem.prefix) else
        {
            return nil
        }
        var enumValueString = shortCutType
        enumValueString.removeRange(bundleStringRange)
        self.init(rawValue: enumValueString)
    }
}Copy the code

It is important to allow initialization to fail. If no character string corresponding to the shortcut operation is matched, it should be displayed. It also tells me that if I’m the maintainer, it might be better to use guard statements when it’s time to use it.

I particularly like this section, which is how we can take advantage of enumerated rawValue and easily concatenate it into package paths. It’s all in the right place, inside an initialization function.

Remember, once it’s initialized, we can also use it as an enumeration. This means that we will have a switch statement that is very readable, with some arguments against it.

Here’s what the final product might look like, with everything integrated and slightly stripped down compared to the online app:

static func handleShortcutItem(shortcutItem:UIApplicationShortcutItem) -> Bool
{
    //Initialize our enum instance to check for a shortcut
    guard let shortCutAction = IncomingShortcutItem(shortCutType: shortcutItem.type) else
    {
        return false
    }
    //Now we've got a valid shortcut, and can use a switch
    switch shortCutAction
    {
        case .ShowFavorites:
            return ShortcutItemHelper.showFavorites()
        case .ShowDeveloper:
            return ShortcutItemHelper.handleAction(with: developer)
    }
}Copy the code

So far, by using this pattern, our shortcuts become sortable and content-safe, which is why I like it so much. It is not necessary to provide a final “return false” statement at the end of the method (even if it is started by default in switch statements), since we already know enough to simplify the code.

Compare this to the previous code:

static func handleShortcutItem(shortcutItem:UIApplicationShortcutItem) -> Bool
{
    //Initialize our enum instance to check for a shortcut
    let shortcutAction = NSBundle.mainBundle().bundleIdentifier! + "." + shortcutItem.type

    if shortCutAction == "com.aCoolCompany.aCoolApp.shortCutOne"
    {
        return ShortcutItemHelper.showFavorites()
    }
    else if shortCutAction == "com.aCoolCompany.aCoolApp.shortCutTwo"
    {
         return ShortcutItemHelper.handleAction(with: developer)
    }
    return false
}Copy the code

Really, it looks a little easier than switching. But I’ve seen a lot of code like this before (I wrote it myself, of course) and while it works fine, I think I can take advantage of the Swift feature and write better code.

Final thoughts

When I first started reading the returns from the Swift enumeration, I found them a little “heavy.” With class inits(), why should I enumerate protocol compliance, which seems a bit redundant? Over the years, I think this model has fully demonstrated why this is the case.

When I see Apple implement this model, I am really happy. I think this is a great way to solve a small problem and a “team friendly” approach to the implementation details of quick operations. I think they would agree with me, as this approach is also in their two 3D Touch sample projects.

See you next time