Tailor 2

Why 2

It feels strange to be announcing a version 2 release only three months after the version 1 release. It wasn't the plan; we set out after WWDC to create a 1.1 release that would polish up rough edges and add support for new Swift features. As we started making use of Swift 2.0, particularly protocol extensions, we were more and more convinced that protocols are a better mechanism for sharing functionality and modeling types in the framework than class-based inheritence. One core goal of this framework is to be Swift-like, and it hardly seems right to let the framework lag behind the language. To that end, we're going to be pursuing a different versioning scheme that allows us to make the best use of the language while providing a manageable upgrade path.

Moving Fast

There will be two types of releases for Tailor: major versions and bug fixes. For major versions, we will do whatever possible to maintain compile-time compatibility with the previous major version without sacrificing features. We will only maintain compatibility going one version back: the methods that are deprecated in Tailor 2 will be removed in Tailor 3. Major versions will come out every three to six months for the foreseeable future; bug fixes will be released as often as needed, and will contain small, backward-compatible changes to address serious bugs.

When a new version of Tailor comes out, our recommendation is that you start work immediately on getting your apps to build with it. Ideally the app will still build and run, with deprecation warnings to indicate what needs to be changed for full compatibility. You'll need to update these things as soon as possible, because they'll be gone in the next release. This is definitely a harsh approach to upgrade cycles, but it's designed to follow Apple's lead.

Version 2 is a huge upgrade, and has more breaks in backward compatibility than we would like.

What's New in Version 2

You should check out the changelog for the full details on the new features; for this post we're going to focus on the changes that will require updates to your apps.

Protocols Everywhere

Many of the core types in the framework have been replaced with protocol extensions:

  • Template is now TemplateType
  • Controller is now ControllerType
  • Alteration is now AlterationScript
  • Task is now TaskType
  • Localization is now LocalizationSource
  • CacheStore is now CacheImplementation
  • DatabaseConnection is now DatabaseDriver
  • User is now UserType
  • Layout is now LayoutType

Many of these protocols require adding new fields to the types that implement them. You should read the guides for more information on how these protocols work. With these protocols in place, you should not have to subclass any class provided by the framework.

Request Handling

  • Sessions are now stored on requests, not controllers
  • Response codes are represented as a value from a ResponseCode enum, which provides both the code and a description
  • Instead of having a static method that returns a list of Actions, you should add your controller actions to the route set directly. There is a required static method on controllers called defineRoutes that allows you to add your routes. In your central route configuration, you should call addControllerRoutes to add the routes for a controller. That will call defineRoutes for you.
  • The best method for adding routes for controllers is the new route method on RouteSet. It takes in a path, an action method, and a name. You should read the controller guide in the docs for more details on how this works in practice.
  • Route paths are now represented by a RoutePath enum, which encapsulates both an HTTP method and a path. The enum cases correspond to the HTTP method, and all the cases take in a path as a parameter.

Modelling

  • The MySQL connection code has been factored out into the new TailorMysql framework.
  • The installer provides a new TailorSqlite framework, which provides a database driver backed by a SQLite data store.
  • The initializer required by the Persistable protocol now takes a DatabaseRow rather than a dictionary. It also now throws an exception on error rather than returning nil. The DatabaseRow type provides a read method that fetches values from the row, and throws an exception for missing values or for values that are not of the desired type. It infers the type from the value that you store the result in. You should read the model guide in the docs for more information on how to set up an initializer for a record.

Configuration

  • The ConfigurationSetting class has been replaced by the Application.Configuration type. This type provides a way of setting your configuration in a static, type-checked way rather than relying on plist files. There is a singleton instance of this type available at Application.configuration, and you should use that whenever fetching the configuration. For setting the configuration, you can add an extension to the Application.Configuration type that provides a dynamic implementation of the configure method. The configure method is called as part of the initialization of the configuration. You should read the configuration guide in the docs for more information on what configuration settings are available and what
  • Routes are now stored in a singleton instance available at RouteSet.shared. You can load your routes by calling RouteSet.load, and it is recommended that you do this in your custom configure method on the Configuration type.

Detailed Upgrade Process

You can find out more about the upgrade process in our upgrade guide.