ServiceStack v4.0.38

Native Support for Swift!

We're happy to announce an exciting new addition to Add ServiceStack Reference with support for Apple's new Swift Programming Language - providing the most productive way for consuming web services on the worlds most desirable platform!

Swift iOS, XCode and OSX Banner

Native Swift support adds compelling value to your existing ServiceStack Services providing an idiomatic and end-to-end typed Swift API that can be effortlessly consumed from iOS and OSX Desktop Apps.

ServiceStack XCode Plugin

To further maximize productivity we've integrated with XCode IDE to allow iOS and OSX developers to import your typed Services API directly into their XCode projects with the ServiceStack XCode plugin below:

ServiceStackXCode.dmg download

The ServiceStack XCode Plugin can be installed by dragging it to the XCode Plugins directory:

ServiceStackXCode.dmg Installer

Once installed the plugin adds a couple new familiar Menu options to the XCode Menu:

Swift Add ServiceStack Reference

XCode Add Reference

Use the Add ServiceStack Reference Menu option to bring up the Add Reference XCode UI Sheet, which just like the Popup Window in VS.NET just needs the Url for your remote ServiceStack instance and the name of the file the generated Swift DTO's should be saved to:

XCode Add Reference Sheet

Clicking Add Reference adds 2 files to your XCode project:

  • JsonServiceClient.swift - A Swift JSON ServiceClient with API's based on that of the .NET JsonServiceClient
  • {FileName}.dtos.swift - Your Services DTO Types converted in Swift

You can also customize how the Swift types are generated by uncommenting the desired option with the behavior you want, e.g. to enable Key-Value Observing (KVO) in the generated DTO models, uncomment BaseClass: NSObject and then click the Update ServiceStack Reference Main Menu item to fetch the latest DTO's with all Types inheriting from NSObject as seen below:

XCode Update Reference

We've temporarily disabled "Update on Save" functionality as it resulted in an unacceptable typing delay on the watched file. We hope to re-enable this in a future version of XCode which doesn't exhibit degraded performance.

Swift Native Types

Like most ServiceStack's features our goal with Add ServiceStack Reference is to deliver the most value as simply as possible. One way we try to achieve this is by reducing the cognitive load required to use our libraries by promoting a simple but powerful conceptual model that works consistently across differring implementations, environments, langauges as well as UI integration with the various VS.NET, Xamarin Studio and now XCode IDE's - in a recent React conference this was nicely captured with the phrase Learn once, Write Anywhere.

Whilst each language is subtly different, all implementations work conceptually similar with all using Clean, Typed DTO's sent using a generic Service Gateway to facilitate its end-to-end typed communications. The client gateways also support DTO's from any source whether shared in source or binary form or generated with Add ServiceStack Reference.

JsonServiceClient.swift

With this in mind we were able to provide the same ideal, high-level API we've enjoyed in .NET's ServiceClients into idiomatic Swift as seen with its ServiceClient protocol definition below:

public protocol ServiceClient
{
    func get<T>(request:T, error:NSErrorPointer) -> T.Return?
    func get<T>(request:T, query:[String:String], error:NSErrorPointer) -> T.Return?
    func get<T>(relativeUrl:String, error:NSErrorPointer) -> T?
    func getAsync<T>(request:T) -> Promise<T.Return>
    func getAsync<T>(request:T, query:[String:String]) -> Promise<T.Return>
    func getAsync<T>(relativeUrl:String) -> Promise<T>
    
    func post<T>(request:T, error:NSErrorPointer) -> T.Return?
    func post<Response, Request>(relativeUrl:String, request:Request?, error:NSErrorPointer) -> Response?
    func postAsync<T>(request:T) -> Promise<T.Return>
    func postAsync<Response, Request>(relativeUrl:String, request:Request?) -> Promise<Response>
    
    func put<T>(request:T, error:NSErrorPointer) -> T.Return?
    func put<Response, Request>(relativeUrl:String, request:Request?, error:NSErrorPointer) -> Response?
    func putAsync<T>(request:T) -> Promise<T.Return>
    func putAsync<Response, Request>(relativeUrl:String, request:Request?) -> Promise<Response>
    
    func delete<T>(request:T, error:NSErrorPointer) -> T.Return?
    func delete<T>(request:T, query:[String:String], error:NSErrorPointer) -> T.Return?
    func delete<T>(relativeUrl:String, error:NSErrorPointer) -> T?
    func deleteAsync<T>(request:T) -> Promise<T.Return>
    func deleteAsync<T>(request:T, query:[String:String]) -> Promise<T.Return>
    func deleteAsync<T>(relativeUrl:String) -> Promise<T>
    
    func send<T>(intoResponse:T, request:NSMutableURLRequest, error:NSErrorPointer) -> T?
    func sendAsync<T>(intoResponse:T, request:NSMutableURLRequest) -> Promise<T>
    
    func getData(url:String, error:NSErrorPointer) -> NSData?
    func getDataAsync(url:String) -> Promise<NSData>
}

Generic type constraints omitted for readability

The minor differences are primarily due to differences in Swift which instead of throwing Exceptions uses error codes and Optional return types and its lack of any asynchrony language support led us to embed a lightweight and well-documented Promises implementation in PromiseKit which closely matches the Task<T> type used in .NET Async API's.

JsonServiceClient.swift Usage

If you've ever had to make HTTP requests using Objective-C's NSURLConnection or NSURLSession static classes in iOS or OSX, the higher-level API's in JsonServiceClient will feel like a breath of fresh air - which enable the same ideal client API's we've enjoyed in ServiceStack's .NET Clients, in Swift Apps!

A nice benefit of using JsonServiceClient over static classes is that Service calls can be easily substituted and mocked with the above ServiceClient protocol, making it easy to test or stub out the external Gateway calls whilst the back-end is under development.

To illustrate its usage we'll go through some client code to consume TechStacks Services after adding a ServiceStack Reference to http://techstaks.io:

var client = JsonServiceClient(baseUrl: "https://techstacks.io")
var response = client.get(AppOverview())

Essentially usage is the same as it is in .NET ServiceClients - where it just needs the baseUrl of the remote ServiceStack instance, which can then be used to consume remote Services by sending typed Request DTO's that respond in kind with the expected Response DTO.

Async API Usage

Whilst the sync API's are easy to use their usage should be limited in background threads so they're not blocking the Apps UI whilst waiting for responses. Most of the time when calling services from the Main UI thread you'll want to use the non-blocking async API's, which for the same API looks like:

client.getAsync(AppOverview())
    .then(body:{(r:AppOverviewResponse) -> Void in 
        ... 
    })

Which is very similar to how we'd make async Task<T> calls in C# when not using its async/await language syntax sugar.

Async callbacks are called back on the main thread, ideal for use in iOS Apps. This behavior is also configurable in the Promise's callback API.

Typed Error Handling

As Swift doesn't provide try/catch Exception Handling, Error handling is a little different in Swift which for most failable API's just returns a nil Optional to indicate when the operation didn't succeed. When more information about the error is required, API's will typically accept an additional NSError pointer argument to populate with more information about the error. Any additional metadata can be attached to NSError's userInfo Dictionary. We also follow this same approach to provide our structured error handling in JsonServiceClient.

To illustrate exception handling we'll connect to ServiceStack's Test Services and call the ThrowType Service to intentionally throw the error specified, e.g:

Sync Error Handling

var client = JsonServiceClient(baseUrl: "https://test.servicestack.net")

var request = ThrowType()
request.type = "NotFound"
request.message = "custom message"

var error:NSError?

let response = client.post(request, &error)
response //= nil

error!.code //= 404
var status:ResponseStatus = error!.convertUserInfo() //Convert into typed ResponseStatus
status.message //= not here
status.stackTrace //= Server Stack Trace

Note the explicit type definition on the return type is required here as Swift uses it as part of the generic method invocation.

Async Error Handling

To handle errors in Async API's we just add a callback on .catch() API on the returned Promise, e.g:

client.postAsync(request)
    .catch({ (error:NSError) -> Void in
        var status:ResponseStatus = error.convertUserInfo()
        //...
    })

JsonServiceClient Error Handlers

Just like in .NET, we can also attach Global or instance error handlers to be able to generically handle all Service Client errors with a custom handler, e.g:

client.onError = {(e:NSError) in ... }
JsonServiceClient.Global.onError = {(e:NSError) in ... }

Custom Routes

As Swift doesn't support Attributes any exported .NET Attributes are emitted in comments on the Request DTO they apply to, e.g:

// @Route("/technology/{Slug}")
public class GetTechnology : IReturn { ... }

This also means that the Custom Routes aren't used when making Service Requests and instead just uses ServiceStack's built-in pre-defined routes.

But when preferred JsonServiceClient can also be used to call Services using Custom Routes, e.g:

var response:GetTechnologyResponse? = client.get("/technology/servicestack")

JsonServiceClient Options

Other options that can be configured on JsonServiceClient include:

client.onError = {(e:NSError) in ... }
client.timeout = ...
client.cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringLocalCacheData
client.requestFilter = {(req:NSMutableURLRequest) in ... }
client.responseFilter = {(res:NSURLResponse) in ... }

//static Global configuration
JsonServiceClient.Global.onError = {(e:NSError) in ... }
JsonServiceClient.Global.requestFilter = {(req:NSMutableURLRequest) in ... }
JsonServiceClient.Global.responseFilter = {(res:NSURLResponse) in ... }

Introducing TechStacks iPhone and iPad App!

To illustrate the ease-of-use and utility of ServiceStack's new Swift support we've developed a native iOS App for https://techstacks.io that has been recently published and is now available to download for free on the AppStore:

TechStacks on AppStore

The complete source code for the TechStacks App is available on GitHub - providing a good example on how easy it is to take advantage of ServiceStack's Swift support to quickly build a rich and responsive Services-heavy native iOS App.

All remote Service Calls used by the App are encapsulated into a single AppData.swift class and only uses JsonServiceClient's non-blocking Async API's to ensure a Responsive UI is maintained throughout the App.

MVC and Key-Value Observables (KVO)

If you've ever had to implement INotifyPropertyChanged in .NET, you'll find the built-in model binding capabilities in iOS/OSX a refreshing alternative thanks to Objective-C's underlying NSObject which automatically generates automatic change notifications for its KV-compliant properties. UIKit and Cocoa frameworks both leverage this feature to enable its Model-View-Controller Pattern.

As keeping UI's updated with Async API callbacks can get unwieldy, we wanted to go through how we're taking advantage of NSObject's KVO support in Service Responses to simplify maintaining dynamic UI's.

Enable Key-Value Observing in Swift DTO's

Firstly to enable KVO in your Swift DTO's we'll want to have each DTO inherit from NSObject which can be done by uncommenting BaseObject option in the header comments as seen below:

/* Options:
Date: 2015-02-19 22:43:04
Version: 1
BaseUrl: https://techstacks.io

BaseClass: NSObject
...
*/

and click the Update ServiceStack Reference Menu Option to fetch the updated DTO's.

Then to enable Key-Value Observing just mark the response DTO variables with the dynamic modifier, e.g:

public dynamic var allTiers:[Option] = []
public dynamic var overview:AppOverviewResponse = AppOverviewResponse()
public dynamic var topTechnologies:[TechnologyInfo] = []
public dynamic var allTechnologies:[Technology] = []
public dynamic var allTechnologyStacks:[TechnologyStack] = []

Which is all that's needed to allow properties to be observed as they'll automatically issue change notifications when they're populated in the Service response async callbacks, e.g:

func loadOverview() -> Promise<AppOverviewResponse> {
    return client.getAsync(AppOverview())
        .then(body:{(r:AppOverviewResponse) -> AppOverviewResponse in
            self.overview = r
            self.allTiers = r.allTiers
            self.topTechnologies = r.topTechnologies
            return r
        })
}

func loadAllTechnologies() -> Promise<GetAllTechnologiesResponse> {
    return client.getAsync(GetAllTechnologies())
        .then(body:{(r:GetAllTechnologiesResponse) -> GetAllTechnologiesResponse in
            self.allTechnologies = r.results
            return r
        })
}

func loadAllTechStacks() -> Promise<GetAllTechnologyStacksResponse> {
    return client.getAsync(GetAllTechnologyStacks())
        .then(body:{(r:GetAllTechnologyStacksResponse) -> GetAllTechnologyStacksResponse in
            self.allTechnologyStacks = r.results
            return r
        })
}

Observing Data Changes

In your ViewController have the datasources for your custom views binded to the desired data (which will initially be empty):

func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
    return appData.allTiers.count
}
...
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return appData.topTechnologies.count
}

Then in viewDidLoad() start observing the properties your UI Controls are bound to, e.g:

override func viewDidLoad() {
    ...
    self.appData.observe(self, properties: ["topTechnologies", "allTiers"])
    self.appData.loadOverview()
}
deinit { self.appData.unobserve(self) }

In the example code above we're using some custom KVO helpers to keep the code required to a minimum.

With the observable bindings in place, the change notifications of your observed properties can be handled by overriding observeValueForKeyPath() which passes the name of the property that's changed in the keyPath argument that can be used to determine the UI Controls to refresh, e.g:

override func observeValueForKeyPath(keyPath:String, ofObject object:AnyObject, change:[NSObject:AnyObject],
  context: UnsafeMutablePointer<Void>) {
    switch keyPath {
    case "allTiers":
        self.technologyPicker.reloadAllComponents()
    case "topTechnologies":
        self.tblView.reloadData()
    default: break
    }
}

Now that everything's configured, the observables provide an alternative to manually updating UI elements within async callbacks, instead you can now fire-and-forget your async API's and rely on the pre-configured bindings to automatically update the appropriate UI Controls when their bounded properties are updated, e.g:

self.appData.loadOverview() //Ignore response and use configured KVO Bindings

Images and Custom Binary Requests

In addition to greatly simplifying Web Service Requests, JsonServiceClient also makes it easy to fetch any custom HTTP response like Images and other Binary data using the generic getData() and getDataAsync() NSData API's. This is used in TechStacks to maintain a cache of all loaded images, reducing number of HTTP requests and load times when navigating between screens:

var imageCache:[String:UIImage] = [:]

public func loadImageAsync(url:String) -> Promise<UIImage?> {
    if let image = imageCache[url] {
        return Promise<UIImage?> { (complete, reject) in complete(image) }
    }
    
    return client.getDataAsync(url)
        .then(body: { (data:NSData) -> UIImage? in
            if let image = UIImage(data:data) {
                self.imageCache[url] = image
                return image
            }
            return nil
        })
}

TechStacks OSX Desktop App!

As JsonServiceClient.swift has no external dependencies and only relies on core Foundation classes it can be used anywhere Swift can including OSX Cocoa Desktop and Command Line Apps and Frameworks.

Most of the API's used in TechStacks iOS App are standard typed Web Services calls. We've also developed a TechStacks OSX Desktop to showcase how easy it is to call ServiceStack's dynamic AutoQuery Services and how much auto-querying functionality they can provide for free.

E.g. The TechStacks Desktop app is essentially powered with these 2 AutoQuery Services:

[Query(QueryTerm.Or)] //change from filtering (default) to combinatory semantics
public class FindTechStacks : QueryBase<TechnologyStack> {}

[Query(QueryTerm.Or)]
public class FindTechnologies : QueryBase<Technology> {}

Basically just a Request DTO telling AutoQuery what Table we want to Query and that we want to change the default Search behavior to have OR semantics. We don't need to specify which properties we can query as the implicit conventions automatically infer it from the table being queried.

The TechStacks Desktop UI is then built around these 2 AutoQuery Services allowing querying against each field and utilizing a subset of the implicit conventions supported:

Querying Technology Stacks

TechStack Desktop Search Fields

Querying Technologies

TechStack Desktop Search Type

Like the TechStacks iOS App all Service Calls are maintained in a single AppData.swift class and uses KVO bindings to update its UI which is populated from these 2 services below:

func searchTechStacks(query:String, field:String? = nil, operand:String? = nil)
  -> Promise<QueryResponse<TechnologyStack>> {
    self.search = query
    
    let queryString = query.count > 0 && field != nil && operand != nil
        ? [createAutoQueryParam(field!, operand!): query]
        : ["NameContains":query, "DescriptionContains":query]
    
    let request = FindTechStacks<TechnologyStack>()
    return client.getAsync(request, query:queryString)
        .then(body:{(r:QueryResponse<TechnologyStack>) -> QueryResponse<TechnologyStack> in
            self.filteredTechStacks = r.results
            return r
        })
}

func searchTechnologies(query:String, field:String? = nil, operand:String? = nil)
  -> Promise<QueryResponse<Technology>> {
    self.search = query

    let queryString = query.count > 0 && field != nil && operand != nil
        ? [createAutoQueryParam(field!, operand!): query]
        : ["NameContains":query, "DescriptionContains":query]
    
    let request = FindTechnologies<Technology>()
    return client.getAsync(request, query:queryString)
        .then(body:{(r:QueryResponse<Technology>) -> QueryResponse<Technology> in
            self.filteredTechnologies = r.results
            return r
        })
}

func createAutoQueryParam(field:String, _ operand:String) -> String {
    let template = autoQueryOperandsMap[operand]!
    let mergedField = template.replace("%", withString:field)
    return mergedField
}

Essentially employing the same strategy for both AutoQuery Services where it builds a query String parameter to send with the request. For incomplete queries, the default search queries both NameContains and DescriptionContains field conventions returning results where the Search Text is either in Name OR Description fields.

Swift Generated DTO Types

With Swift support our goal was to ensure a high-fidelity, idiomatic translation within the constraints of Swift language and built-in libraries, where the .NET Server DTO's are translated into clean Swift POSO's (Plain Old Swift Objects :) having their .NET built-in types mapped to their equivalent Swift data type.

To see what this ended up looking like, we'll peel back behind the covers and look at a couple of the Generated Swift Test Models to see how they're translated in Swift:

public class AllTypes
{
    required public init(){}
    public var id:Int?
    public var nullableId:Int?
    public var byte:Int8?
    public var short:Int16?
    public var int:Int?
    public var long:Int64?
    public var uShort:UInt16?
    public var uInt:UInt32?
    public var uLong:UInt64?
    public var float:Float?
    public var double:Double?
    public var decimal:Double?
    public var string:String?
    public var dateTime:NSDate?
    public var timeSpan:NSTimeInterval?
    public var dateTimeOffset:NSDate?
    public var guid:String?
    public var char:Character?
    public var nullableDateTime:NSDate?
    public var nullableTimeSpan:NSTimeInterval?
    public var stringList:[String] = []
    public var stringArray:[String] = []
    public var stringMap:[String:String] = [:]
    public var intStringMap:[Int:String] = [:]
    public var subType:SubType?
}

public class AllCollectionTypes
{
    required public init(){}
    public var intArray:[Int] = []
    public var intList:[Int] = []
    public var stringArray:[String] = []
    public var stringList:[String] = []
    public var pocoArray:[Poco] = []
    public var pocoList:[Poco] = []
    public var pocoLookup:[String:[Poco]] = [:]
    public var pocoLookupMap:[String:[String:Poco]] = [:]
}

public enum EnumType : Int
{
    case Value1
    case Value2
}

As seen above, properties are essentially mapped to their optimal Swift equivalent. As DTO's can be partially complete all properties are Optional except for enumerables which default to an empty collection - making them easier to work with and despite their semantic differences, .NET enums are translated into typed Swift enums.

Swift Challenges

The current stable version of Swift has several limitations that prevented using similar reflection and metaprogramming/code-gen techniques we're used to with .NET to implement them efficiently in Swift, e.g. Swift has an incomplete reflection API that can't set a property, is unable to cast Any (aka object) back to a concrete Swift type, unable to get the string literal for an enum value and we ran into many other Swift compiler limitations that would segfault whilst exploring this strategy.

Some of these limitations could be worked around by having every type inherit from NSObject and bridging to use the dynamism in Objective-C API's, but ultimately we decided against depending on NSObject or using Swift's built-in reflection API's which we also didn't expect to perform well in iOS's NoJIT environment which doesn't allow caching of reflection access to maintain optimal runtime performance.

Swift Code Generation

As we were already using code-gen to generate the Swift types we could extend it without impacting the Developer UX which we expanded to also include what's essentially an explicit Reflection API for each type with API's to support serializing to and from JSON. Thanks to Swift's rich support for extending types we were able to leverage its Type extensions so the implementation details could remain disconnected from the clean Swift type definitions allowing improved readability when inspecting the remote DTO schema's.

We can look at AllCollectionTypes to see an example of the code-gen that's generated for each type, essentially emitting explicit readable/writable closures for each property:

extension AllCollectionTypes : JsonSerializable
{
    public class var typeName:String { return "AllCollectionTypes" }
    public class func reflect() -> Type<AllCollectionTypes> {
        return TypeConfig.config() ?? TypeConfig.configure(Type<AllCollectionTypes>(
            properties: [
                Type<AllCollectionTypes>.arrayProperty("intArray", get: { $0.intArray }, set: { $0.intArray = $1 }),
                Type<AllCollectionTypes>.arrayProperty("intList", get: { $0.intList }, set: { $0.intList = $1 }),
                Type<AllCollectionTypes>.arrayProperty("stringArray", get: { $0.stringArray }, set: { $0.stringArray = $1 }),
                Type<AllCollectionTypes>.arrayProperty("stringList", get: { $0.stringList }, set: { $0.stringList = $1 }),
                Type<AllCollectionTypes>.arrayProperty("pocoArray", get: { $0.pocoArray }, set: { $0.pocoArray = $1 }),
                Type<AllCollectionTypes>.arrayProperty("pocoList", get: { $0.pocoList }, set: { $0.pocoList = $1 }),
                Type<AllCollectionTypes>.objectProperty("pocoLookup", get: { $0.pocoLookup }, set: { $0.pocoLookup = $1 }),
                Type<AllCollectionTypes>.objectProperty("pocoLookupMap", get: { $0.pocoLookupMap }, set: { $0.pocoLookupMap = $1 }),
            ]))
    }
    public func toJson() -> String {
        return AllCollectionTypes.reflect().toJson(self)
    }
    public class func fromJson(json:String) -> AllCollectionTypes? {
        return AllCollectionTypes.reflect().fromJson(AllCollectionTypes(), json: json)
    }
    public class func fromObject(any:AnyObject) -> AllCollectionTypes? {
        return AllCollectionTypes.reflect().fromObject(AllCollectionTypes(), any:any)
    }
    public func toString() -> String {
        return AllCollectionTypes.reflect().toString(self)
    }
    public class func fromString(string:String) -> AllCollectionTypes? {
        return AllCollectionTypes.reflect().fromString(AllCollectionTypes(), string: string)
    }
}

Swift Native Types Limitations

Due to the semantic differences and limitations in Swift there are some limitations of what's not supported. Luckily these limitations are mostly highly-discouraged bad practices which is another reason not to use them. Specifically what's not supported:

No object or Interface properties

When emitting code we'll generate a comment when ignoring these properties, e.g:

//emptyInterface:IEmptyInterface ignored. Swift doesn't support interface properties

Base types must be marked abstract

As Swift doesn't support extension inheritance, when using inheritance in DTO's any Base types must be marked abstract.

All DTO Type Names must be unique

Required as there are no namespaces in Swift (Also required for F# and TypeScript). ServiceStack only requires Request DTO's to be unique, but our recommendation is for all DTO names to be unique.

IReturn not added for Array Responses

As Swift doesn't allow extending generic Arrays with public protocols, the IReturn marker that enables the typed ServiceClient API isn't available for Requests returning Array responses. You can workaround this limitation by wrapping the array in a Response DTO whilst we look at other solutions to support this in future.

Swift Configuration

The header comments in the generated DTO's allows for further customization of how the DTO's are generated which can then be updated with any custom Options provided using the Update ServiceStack Reference Menu Item in XCode. Options that are preceded by a Swift single line comment // are defaults from the server that can be overridden, e.g:

/* Options:
Date: 2015-02-22 13:52:26
Version: 1
BaseUrl: https://techstacks.io

//BaseClass: 
//AddModelExtensions: True
//AddServiceStackTypes: True
//IncludeTypes: 
//ExcludeTypes: 
//AddResponseStatus: False
//AddImplicitVersion: 
//InitializeCollections: True
//DefaultImports: Foundation
*/

To override a value, remove the // and specify the value to the right of the :. Any value uncommented will be sent to the server to override any server defaults.

We'll go through and cover each of the above options to see how they affect the generated DTO's:

BaseClass

Specify a base class that's inherited by all Swift DTO's, e.g. to enable Key-Value Observing (KVO) in the generated DTO models have all types inherit from NSObject:

/* Options:
BaseClass: NSObject

Will change all DTO types to inherit from NSObject:

public class UserInfo : NSObject { ... }

AddModelExtensions

Remove the the code-generated type extensions required to support typed JSON serialization of the Swift types and leave only the clean Swift DTO Type definitions.

/* Options:
AddModelExtensions: False

AddServiceStackTypes

Don't generate the types for built-in ServiceStack classes and Services like ResponseStatus and Authenticate, etc.

/* Options:
AddServiceStackTypes: False

IncludeTypes

Is used as a Whitelist that can be used to specify only the types you would like to have code-generated:

/* Options:
IncludeTypes: GetTechnology,GetTechnologyResponse

Will only generate GetTechnology and GetTechnologyResponse DTO's:

public class GetTechnology { ... }
public class GetTechnologyResponse { ... }

ExcludeTypes

Is used as a Blacklist where you can specify which types you would like to exclude from being generated:

/* Options:
ExcludeTypes: GetTechnology,GetTechnologyResponse

Will exclude GetTechnology and GetTechnologyResponse DTO's from being generated.

AddResponseStatus

Automatically add a ResponseStatus property on all Response DTO's, regardless if it wasn't already defined:

/* Options:
AddResponseStatus: True

Will add a ResponseStatus property to all Response DTO's:

public class GetAllTechnologiesResponse
{
    ...
    public var responseStatus:ResponseStatus
}

AddImplicitVersion

Lets you specify the Version number to be automatically populated in all Request DTO's sent from the client:

/* Options:
AddImplicitVersion: 1

Will add an initialized version property to all Request DTO's:

public class GetAllTechnologies : IReturn
{
    ...
    public var version:Int = 1
}

This lets you know what Version of the Service Contract that existing clients are using making it easy to implement ServiceStack's recommended versioning strategy.

InitializeCollections

Whether enumerables should be initialized with an empty collection (default) or changed to use an Optional type:

/* Options:
InitializeCollections: False

Changes Collection Definitions to be declared as Optional Types instead of being initialized with an empty collection:

public class ResponseStatus
{
    public var errors:[ResponseError]?
}

DefaultImports

Add additional import statements to the generated DTO's:

/* Options:
DefaultImports: UIKit,Foundation

Will import the UIKit and Foundation frameworks:

import UIKit;
import Foundation;

Improved Add ServiceStack Reference

Whilst extending Add ServiceStack Reference to add support for Swift above we've also made a number of refinements to the existing native type providers including:

  • Improved support for nested classes
  • Improved support from complex generic and inherited generic type definitions
  • Ignored DTO properties are no longer emitted
  • Uncommon Language-specific configuration moved into the native type providers
  • New DefaultImports option available to TypeScript and Swift native types

New Include and Exclude Types option added to all languages

You can now control what types are generated by using ExcludeTypes which acts as a blacklist excluding those specific types, e.g:

ExcludeTypes: ResponseStatus,ResponseError

In contrast to ExcludeTypes, if you're only making use of a couple of Services you can use IncludeTypes which acts like a White-List ensuring only those specific types are generated, e.g:

IncludeTypes: GetTechnologyStacks,GetTechnologyStacksResponse

GlobalNamespace option added in C# and VB.NET projects

F#, TypeScript and Swift are limited to generating all DTO's under a single global namespace, however in most cases this is actually preferred as it strips away the unnecessary details of how the DTO's are organized on the Server (potentially across multiple dlls/namespaces) and presents them under a single configurable namespace to the client.

As it's a nice client feature, we've also added this option to C# and VB.NET native types as well which can be enabled by uncommenting the GlobalNamespace option, e.g:

/* Options:
Version: 1
BaseUrl: https://techstacks.io

GlobalNamespace: ServiceModels
...
*/

namespace ServiceModels
{
...
}

Integrated HTML, CSS and JavaScript Minification

As part of our quest to provide a complete and productive solution for developing highly responsive Web, Desktop and Mobile Apps, ServiceStack now includes minifiers for compressing HTML, CSS and JavaScript available from the new Minifiers class:

var minifiedJs = Minifiers.JavaScript.Compress(js);
var minifiedCss = Minifiers.Css.Compress(css);
var minifiedHtml = Minifiers.Html.Compress(html);

// Also minify in-line CSS and JavaScript
var advancedMinifiedHtml = Minifiers.HtmlAdvanced.Compress(html);

Each minifier implements the lightweight ICompressor interface making it trivial to mock or subtitute with a custom implementation.

JS Minifier

For the JavaScript minifier we're using Ext.Net's C# port of Douglas Crockford's venerable JSMin.

CSS Minifier

The CSS Minifer uses Mads Kristensen simple CSS Minifer.

HTML Compressor

For compressing HTML we're using a C# Port of Google's excellent HTML Compressor which we've further modified to remove the public API's ugly Java-esque idioms and replaced them with C# properties.

The HtmlCompressor also includes a number of well-documented options which can be customized by configuring the available properties on its concrete type, e.g:

var htmlCompressor = (HtmlCompressor)Minifier.Html;
htmlCompressor.RemoveComments = false;

Easy win for server generated websites

If your project is not based off one of our optimized Gulp/Grunt.js powered React JS and AngularJS Single Page App templates or configured to use our eariler node.js-powered Bundler Web Optimization solution, these built-in minifiers now offers the easiest solution to effortlessly optimize your existing website which is able to work transparently with your existing Razor Views and static .js, .css and .html files without requiring adding any additional external tooling or build steps to your existing development workflow.

Minify dynamic Razor Views

Minification of Razor Views is easily enabled by specifying MinifyHtml=true when registering the RazorFormat plugin:

Plugins.Add(new RazorFormat {
    MinifyHtml = true,
    UseAdvancedCompression = true,
});

Use the UseAdvancedCompression=true option if you also want to minify inline js/css, although as this requires a bit more processing you'll want to benchmark it to see if it's providing an overall performance benefit to end users. It's a recommended option if you're caching Razor Pages. Another solution is to minimize the use of in-line js/css and move them to static files to avoid needing in-line js/css compression.

Minify static .js, .css and .html files

With nothing other than the new minifiers, we can leverage the flexibility in ServiceStack's Virtual File System to provide an elegant solution for minifying static .html, .css and .js resources by simply pre-loading a new InMemory Virtual FileSystem with minified versions of existing files and giving the Memory FS a higher precedence so any matching requests serve up the minified version first. We only need to pre-load the minified versions once on StartUp by overriding GetVirtualPathProviders() in the AppHost:

public override List<IVirtualPathProvider> GetVirtualPathProviders()
{
    var existingProviders = base.GetVirtualPathProviders();
    var memFs = new InMemoryVirtualPathProvider(this);

    //Get existing Local FileSystem Provider
    var fs = existingProviders.First(x => x is FileSystemVirtualPathProvider);

    //Process all .html files:
    foreach (var file in fs.GetAllMatchingFiles("*.html"))
    {
        var contents = Minifiers.HtmlAdvanced.Compress(file.ReadAllText());
        memFs.AddFile(file.VirtualPath, contents);
    }

    //Process all .css files:
    foreach (var file in fs.GetAllMatchingFiles("*.css")
      .Where(file => !file.VirtualPath.EndsWith(".min.css"))) //ignore pre-minified .css
    {
        var contents = Minifiers.Css.Compress(file.ReadAllText());
        memFs.AddFile(file.VirtualPath, contents);
    }

    //Process all .js files
    foreach (var file in fs.GetAllMatchingFiles("*.js")
      .Where(file => !file.VirtualPath.EndsWith(".min.js"))) //ignore pre-minified .js
    {
        try
        {
            var js = file.ReadAllText();
            var contents = Minifiers.JavaScript.Compress(js);
            memFs.AddFile(file.VirtualPath, contents);
        }
        catch (Exception ex)
        {
            //As JSMin is a strict subset of JavaScript, this can fail on valid JS.
            //We can report exceptions in StartUpErrors so they're visible in ?debug=requestinfo
            base.OnStartupException(new Exception("JSMin Error {0}: {1}".Fmt(file.VirtualPath, ex.Message)));
        }
    }

    //Give new Memory FS the highest priority
    existingProviders.Insert(0, memFs);
    return existingProviders;
}

A nice benefit of this approach is that it doesn't pollute your project with minified build artifacts, has excellent runtime performance with the minfied contents being served from Memory and as the file names remain the same, the links in HTML don't need to be rewritten to reference the minified versions. i.e. When a request is made it just looks through the registered virtual path providers and returns the first match, which given the Memory FS was inserted at the start of the list, returns the minified version.

Enabled in servicestack.net

As this was an quick and non-invasive feature to add, we've enabled it on all servicestack.net Razor views and static files. You can view-source:https://servicestack.net/ (as url in Chrome, Firefox or Opera) to see an example of the resulting minified output.

New ServiceStack Cookbook Released!

A new ServiceStack Cookbook was just released by ThoughtWorker @kylehodgson and our own Darren Reid.

The ServiceStack Cookbook includes over 70 recipes on creating message-based Web Services and Apps including leveraging OrmLite to build fast, testable and maintainable Web APIs - focusing on solving real-world problems that are a pleasure to create, maintain and consume with ServiceStack.

Support for RecyclableMemoryStream

The Bing team recently opensourced their RecyclableMemoryStream implementation which uses pooled, reusable byte buffers to help minimize GC pressure. To support switching to the new RecyclableMemoryStream we've changed most of ServiceStack's MemoryStream usages to use the new MemoryStreamFactory.GetStream() API's, allowing ServiceStack to be configured to use the new RecyclableMemoryStream implementation with:

 MemoryStreamFactory.UseRecyclableMemoryStream = true;

Which now changes MemoryStreamFactory.GetStream() to return instances of RecyclableMemoryStream, e.g:

using (var ms = (RecyclableMemoryStream)MemoryStreamFactory.GetStream()) { ... }

To reduce dependencies and be able to support PCL clients we're using an interned and PCL-compatible version of RecyclableMemoryStream in ServiceStack.Text which has its auditing features disabled.

RecyclableMemoryStream is strict

Whilst the announcement says RecyclableMemoryStream is a drop-in replacement for MemoryStream, this isn't strictly true as it enforces stricter rules on how you can use MemoryStreams. E.g. a common pattern when using a MemoryStream with a StreamWriter or StreamReader is, since it's also disposable, to enclose it in a nested using block as well:

using (var ms = MemoryStreamFactory.GetStream())
using (var writer = new StreamWriter(ms)) {
...
}

But this has the effect of closing the stream twice which is fine in MemoryStream but throws an InvalidOperationException when using RecyclableMemoryStream. If you find this happening in your Application you can use the new MemoryStreamFactory.MuteDuplicateDisposeExceptions=true option we've added as a stop-gap to mute these exceptions until you're able to update your code to prevent this.

Doesn't allow reading from closed streams

Another gotcha when switching over to RecyclableMemoryStream is that once you close the Stream you'll no longer be able to read from it. This makes it incompatible to use with .NET's built-in GZipStream and DeflateStream classes which can only be read after its closed, having the effect of closing the underlying MemoryStream - preventing being able to access the compressed bytes.

Where possible we've refactored ServiceStack to use MemoryStreamFactory.GetStream() and adhered to its stricter usage so ServiceStack can be switched over to use it with MemoryStreamFactory.UseRecyclableMemoryStream=true, although we still have some more optimization work to be able to fully take advantage of it by changing our usage of ToArray() to use the more optimal GetBuffer() and Length API's where possible.

Authentication

  • New MicrosoftLiveOAuth2Provider Microsoft Live OAuth2 Provider added by @ivanfioravanti
  • New InstagramOAuth2Provider Instagram OAuth2 Provider added by @ricardobrandao

New Url Filters added to all AuthProviders

New Url Filters have been added to all AuthProvider redirects letting you inspect or customize and decorate any redirect urls that are forwarded to the remote OAuth Server or sent back to the authenticating client. The list different Url Filters available in all AuthProviders include:

new AuthProvider {
    PreAuthUrlFilter = (authProvider, redirectUrl) => customize(redirectUrl),
    AccessTokenUrlFilter = (authProvider, redirectUrl) => customize(redirectUrl),
    SuccessRedirectUrlFilter = (authProvider, redirectUrl) => customize(redirectUrl),
    FailedRedirectUrlFilter = (authProvider, redirectUrl) => customize(redirectUrl),
    LogoutUrlFilter = (authProvider, redirectUrl) => customize(redirectUrl),
}

OrmLite

  • Custom Decimal precision with [DecimalLength(precision,scale)] added to all OrmLite RDBMS Providers
  • Sqlite persists and queries DateTime's using LocalTime

Messaging

Misc

  • Default 404 Handler for HTML now emits Error message in page body
  • New Dictionary<string, object> Items were added to all Response Contexts which can be used to transfer metadata through the response pipeline
  • BufferedStream is now accessible on concrete Request/Response Contexts
  • Added new GetKeysByPattern() API to MemoryCacheClient
  • Allow DTO.ToAbsoluteUrl() extension method to support ASP.NET requests without needing to configure Config.WebHostUrl. Self Hosts can use the explicit DTO.ToAbsoluteUrl(IRequest) API.
  • New HostContext.TryGetCurrentRequest() Singleton returns Current Request for ASP.NET hosts, null for Self Hosts.
    • HostContext.GetCurrentRequest() will throw for Self Hosts which don't provide singleton access to the current HTTP Request
  • Added new string.CollapseWhitespace() extension method to collapse multiple white-spaces into a single space.

Using ServiceStack as a Proxy

The new Config.SkipFormDataInCreatingRequest option instructs ServiceStack to skip reading from the Request's FormData on initialization (to support X-Http-Method-Override Header) so it avoids forced loading of the Request InputStream allowing ServiceStack to be used as a HTTP proxy with:

RawHttpHandlers.Add(_ => new CustomActionHandler((req, res) => {
    var bytes = req.InputStream.ReadFully();
    res.OutputStream.Write(bytes, 0, bytes.Length);
}));

NuGet dependency updates

  • Npgsql updated to 2.2.4.3
  • NLog updated to v3.2.0.0

Updated Versioning Strategy

To make it easier for developers using interim pre-release NuGet packages upgrade to the official NuGet packages once they're released, we've started using odd version numbers (e.g v4.0.37) for pre-release MyGet builds and even numbers (e.g. v4.0.38) for official released packages on NuGet.

Breaking changes

  • void or null responses return 204 NoContent by default, can be disabled with Config.Return204NoContentForEmptyResponse = false
  • Failed Auth Validations now clear the Users Session
  • ServiceExtensions.RequestItemsSessionKey moved to SessionFeature.RequestItemsSessionKey