Extending Swift Value(s) to the Server David Ungar and Robert Dickerson Extending Swift Value(s) to the Server by David Ungar and Robert Dickerson Copyright © 2017 IBM Corporation All rights reserved Printed in the United States of America Published by O’Reilly Media, Inc., 1005 Gravenstein Highway North, Sebastopol, CA 95472 O’Reilly books may be purchased for educational, business, or sales promotional use Online editions are also available for most titles (http://safaribooksonline.com) For more information, contact our corporate/institutional sales department: 800-998-9938 or corporate@oreilly.com Editors: Nan Barber and Susan Conant Production Editor: Shiny Kalapurakkel Copyeditor: Christina Edwards Proofreader: Eliahu Sussman Interior Designer: David Futato Cover Designer: Karen Montgomery Illustrator: Rebecca Panzer January 2017: First Edition Revision History for the First Edition 2017-01-25: First Release The O’Reilly logo is a registered trademark of O’Reilly Media, Inc Extending Swift Value(s) to the Server, the cover image, and related trade dress are trademarks of O’Reilly Media, Inc While the publisher and the authors have used good faith efforts to ensure that the information and instructions contained in this work are accurate, the publisher and the authors disclaim all responsibility for errors or omissions, including without limitation responsibility for damages resulting from the use of or reliance on this work Use of the information and instructions contained in this work is at your own risk If any code samples or other technology this work contains or describes is subject to open source licenses or the intellectual property rights of others, it is your responsibility to ensure that your use thereof complies with such licenses and/or rights 978-1-491-97196-3 [LSI] Preface: Swift for the Rest of Your Application Q: Why did the human put on his boxing gloves? A: He had to punch some cards Today’s applications not run on a single platform Rather, some parts run on resource-limited devices, and other parts run on a vast and mysterious cloud of servers This separation has led to a schism in how we build these applications because different platforms have different requirements: the mobile portions must conserve battery power, while the server portions must handle a large number of requests simultaneously Consequently, programmers use different languages for different parts of applications — for instance, JavaScript for the browser, and Java for the server However, constructing an application out of multiple languages is fraught with drawbacks: different teams in the same organization speak different languages — literally — and must master different developer ecosystems Precious time must be spent translating concepts across language barriers and a few developers must know all of the languages in order to be effective Test cases and data models must be replicated in different languages, introducing bugs and incurring future maintenance efforts Because third-party libraries cannot be shared across groups, each team must learn different APIs to obtain merely the same functionality Swift was introduced by Apple in 2014 and replaced Objective-C as the recommended language for all new applications running on Apple devices Later, when Swift became open source in 2015, it spread to new platforms Currently, Swift is available on x86, ARM (including Raspberry Pi), and zOS architectures, as well as Linux, macOS, tvOS, watchOS, and iOS operating systems So, it is now possible to write a whole end-to-end mobile application — front-end, middle, back, and even toaster — all in Swift That’s why we wrote this book; we wanted to help you, the developer, who is most likely writing in Java or JavaScript, to consider a switch to Swift Why adopt Swift? The Swift language may well be better than what you are currently using You can develop and debug in a consistent environment Integrated development environments (IDEs) offer a tremendous amount of functionality such as text editing, static analysis, code completion, debugging, profiling, and even source-control integration Switching back and forth between say, Eclipse and Xcode is a bit like switching between French horn and electric guitar: neither easy nor productive You can reuse code When each bit of functionality is expressed exactly once, there is less work, more understanding, and fewer bugs You can leverage Swift’s features — such as optional types, value types, and functional programming facilities — to detect many bugs at compile time that would otherwise be hard to find Since Swift uses the LLVM compiler toolchain for producing nativecode binaries, your applications have the potential for competitive performance in terms of speed, startup time, and memory usage However, examination of performance is outside the scope of this book You will find an active and approachable community of Swift developers who are creating web posts, books, and videos In 2016, Swift was cited as the second “Most Loved” language in a StackOverflow survey, and the third most upward trending technology This book will introduce you to the Swift language, illustrate some of its most interesting features, and show you how to create and deploy a simple web service Let’s get started! CODING STYLE & IMPLEMENTATIONS In the examples, the space constraints of this medium have led us to indent, break lines, and place brackets differently than we would in actual code In addition, space has precluded the inclusion of full implementations and blocks of code in this edition contain inconsistencies in color and font If the inconsistencies confuse you, please consult the repositories in Table P-1 Table P-1 Where to find code examples Repository name Referenced in Book snippets Code snippets from the book MiniPromiseKit Created in Chapter 3; used in Chapter Pipes Used in Chapter Kitura To-Do List Created in Chapter Acknowledgments This book would not have been possible without the support, encouragement, and guidance of the IBM Cloud and Swift@IBM leadership team, including Pat Bohrer, Eli Cleary, Jason Gartner, Sandeep Gopisetty, Heiko Ludwig, Giovanni Pacifici, John Ponzo, and Karl Weinmeister In addition, we want to extend our thanks to the many IBM Swift engineers and Swift community members working to bring Swift to the server — including Chris Bailey, Hubertus Franke, David Grove, David Jones, and Shmuel Kallner — for sharing their collective technical insights and creating the tools and libraries described herein The Swift community’s embrace of Swift on the server reassured us that our contribution would be valued The growing number of their instructive blog posts, videos, conference talks, and books have been of great help We would like to thank our technical reviewers: Chris Devers, Shun Jiang, and Andrew Black Nan Barber and the O’Reilly team had the daunting task of editing our lengthy technical drafts and producing this book We owe a huge debt of gratitude to the Apple Core Swift Team for their courage, intelligence, talent, wisdom, and generosity for bringing a new language and ecosystem into existence and moving it to open source Language design involves many difficult and complex tradeoffs, and bringing a new language to the world requires a tremendous amount of work The rapid acceptance of Swift by developers is powerful testimony to the quality of the language Words fall short in plumbing the depths of our gratitude for the support and love of our sweethearts, Barbara Hurd and Valerie Magolan Chapter A Swift Introduction Swift supports several different programming paradigms This chapter provides a brief overview of the parts of the Swift language that will be familiar to a Java or JavaScript programmer Swift is not a small language, and this chapter omits many of its conveniences, including argument labels, shorthand syntax for closures, string interpolation, array and dictionary literals, ranges, and scoping attributes Swift’s breadth lets you try Swift without changing your programming style while you master its basics Later, when ready, you can exploit the additional paradigms it offers A beginning Swift developer may initially be overwhelmed by the cornucopia of features in the Swift language, since it gives you many ways to solve the same problem But taking the time to choose the right approach can often catch bugs, shorten, and clarify your code For instance, value types help prevent unintended mutation of values Paradigms borrowed from functional programming such as generics, closures, and protocols provide ways to factor out not only common code, but also variations on common themes As a result, the underlying themes can be written once, used in varying contexts, and still be statically checked Your programs will be much easier to maintain and debug, especially as they grow larger As you read this chapter, you may want to refer to the documentation, The Swift Programming Language (Swift Edition) Types and Type Inference Swift combines strong and static typing with powerful type inference to keep code relatively concise Swift’s type system and compile-time guarantees help improve the reliability of nontrivial code callNextHandler() } func handleAddItemStruct( request: RouterRequest, response: RouterResponse, callNextHandler: @escaping () -> Void ) { guard case let json(jsonBody)? = request.body else { response.status(.badRequest) callNextHandler() return } { let item = try Item(json: jsonBody) itemStructsLock.wait() itemStructs.append(item) itemStructsLock.signal() response.send("Added '\(item)'\n") callNextHandler() } catch { response.status(.badRequest) let err = error.localizedDescription response.send(err) callNextHandler() } } Register the handlers: router.get ("/v1/struct/item", handler: handleGetItemStructs) router.post("/v1/struct/item", handler: handleAddItemStruct) Test: $ curl -H "Content-Type: application/json" \ -X POST \ -d '{"title":"Finish book!"}' localhost:8090/v1/struct/item Added 'Item(id: 054879B8-B798-4462-AF0B-79B20F9617F4, title: "Herd llamas")' $ curl localhost:8090/v1/struct/item [ { "id" : "054879B8-B798-4462-AF0B-79B20F9617F4", "title" : Finish book! } ] Adding Authentication You may not want to allow a user to add a task to someone else’s list In order to prevent that from happening, you must first add a layer of authentication User identity will be assured by the client securely sending a token that uniquely identifies the user This token is obtained by a third-party service the user already has an account on, such as Facebook, Google, GitHub, or LinkedIn This method is convenient and more secure because your application’s database will not need to store each user’s login name and password The database can just link each user’s login name to that user’s to list items Kitura has a middleware library called Kitura Credentials that makes it easy to add authentication to your application There are several plug-ins that currently support Facebook, Github, and Google OAuth token validation In addition, there are plug-ins for HTTP basic and digest authentication The middleware intercepts each RouterRequest, and then ensures that the token in the header correctly authenticates the user The RouterRequest will only proceed to execute the handler if the authentication succeeds The example below shows how to use the Facebook plug-in After credentials middleware is created, the Facebook plug-in is registered with the middleware Next, the routes matching the item resource are protected with the authentication layer This is useful, since there might be other routes that you might not want to protect; for instance, the welcome page The following sets up the authentication: let credentials = Credentials() let facebookCredentialsPlugin = CredentialsFacebookToken() credentials.register(facebookCredentialsPlugin) router.all("/v1/*/item", middleware: credentials) The login flow works as follows: The client must first obtain a Facebook OAuth token using the Facebook login web service If you are developing an iOS application, this is done through the Facebook SDK for iOS If you are developing a web application, you will use the Facebook SDK for JavaScript When the user imports the Facebook iOS SDK, it will allow you to present a Facebook login panel to the user requesting a username and password If the user has already signed into Facebook (and a cache exists with the token), the login screen is bypassed The username and password are sent to the Facebook server through HTTPS and, assuming the username and password checks out, an OAuth token is returned to the client The client then must include that token in the header of future requests to add or get items For Kitura Facebook Credentials to use this token, the client must bundle the token in the access_token header field, and “FacebookToken” in the X-tokentype field for all HTTP requests The user’s Facebook profile information can be read using the userProfile attribute of the RouterRequest The code can obtain the user’s unique user ID from request.userProfile.id For brevity, we did not show how to relate a user ID to the to-do items, but this would be an easy modification to make to the above examples Setting up the Database There are many different databases to choose from if you are building a to-do list in Swift You can see some examples by cloning our example Kitura TodoList applications, which will show you how to interface with some of the most popular databases You will see, for instance, MongoDB, PostgreSQL, Redis, and more There are many different database drivers for Swift in the Swift Package Catalog Some were written using C bindings to existing database drivers written in C, so you may need to install some development headers and libraries before you can use them Others have been developed from the ground up with pure Swift and therefore will not require the installation of any additional libraries In this section, we explain how to use CouchDB with your to-do list We chose this database since it will demonstrate how to make outbound network calls from your server CouchDB uses a simple REST API that receives and accepts JSON payloads CouchDB is supported well on Bluemix as an offering called Cloudant Everything in CouchDB is a document In order to query the database, you must write a special kind of document called a design document that contains some logic that should be run in the database When that logic is run, it prepares a “view” that is returned to the user The map function is run on every document in the database Usually some condition is checked, and if it passes, a new document is emitted containing the values of that document Another kind of logic is the reduce function It is useful for collapsing the documents down to a single document For instance, if you wanted to get the count or the sum of many documents To create a design, open a new file named todolist_design.json, and add the following to it: { "_id": "_design/tododesign", "_views" : { "all_todos" : { "map" : "function(doc) { emit(doc._id, [doc._id, doc.title]); }" } } } Since CouchDB uses a REST API, you can make new designs, add documents, and query for documents all using basic HTTP calls Before showing how to write the Swift code for these network calls, it is helpful to see what the cURL commands would be for each of these operations For instance, to create a new database called todolist: $ curl -X PUT http://127.0.0.1:5984/todolist To upload the todolist_design.json document to the newly created database: $ curl -X PUT http://127.0.0.1:5984/todolist/_design/tododesign \ data-dinary @todolist_design.json Then, in order to get back all the to-dos, you could make a request to: $ curl http://127.0.0.1:5984/todolist/_design/tododesign \ /_views/all_todos To add a new item to the database, you make the call to the database with a UUID for the document ID: $ curl http://127.0.0.1:5984/todolist/ \ -d '{ "title": "Reticulate Splines" }' Now we can show how to make outbound network connections with Swift Connecting to the Database Because outbound network requests introduce a fair amount of complexity to your application, we recommend using a promise library You can add the promise library created in Chapter by adding the following dependency to Package.swift: Package(url: "https://github.com/davidungar/miniPromiseKit", majorVersion: 4, minor: 1) Now, you can extend URLSession to use a promise instead of using a callback: extension URLSession { func dataTaskPromise(with url: URL) -> Promise { return Promise { fulfill, reject in let dataTask = URLSession(configuration: default).dataTask( with: url) { data, response, error in if let d = data { fulfill(d) } else { reject(error!) } } dataTask.resume() } } } By default, the miniPromiseKit and PromiseKit libraries will use the main queue for dispatching the then, catch, and always blocks, so it is important to create a new concurrent queue and use it as the context for executing these blocks: let queue = DispatchQueue(label: "com.todolist.controller", qos: userInitiated, attributes: concurrent) The body of the firstly call returns a promise for the array of items In the examples below, url is the URL for the all_todos view Once the data is returned, we convert it to an array of items In the interest of space, we have omitted the dataToItems method, which does the JSON parsing Refer to it in the example code This method can throw an error if the data could not be parsed properly, and that error can percolate through the promise chain func getAllItems() -> Promise { return firstly { URLSession().dataTaskPromise(with: url) } then(on: queue) { dataResult in return try dataToItems(data: dataResult) } } You can add a task by sending a URLRequest with a PUT method Assume for the sake of this example that URL is the path to the database and the UUID of the item you want to insert — for example, "127.0.0.1:5984/todolist/E71233AD-01D2-430B-8708F7E2496AEFB2": func addItem(item: Item) -> Promise { return Promise { fulfill, reject in let session = URLSession(configuration: default) var request = URLRequest(url: url) request.httpMethod = "PUT" request.httpBody = try JSONSerialization.data( withJSONObject: item.dictionary, options: []) let dataTask = session.dataTask(with: request) { data, response, error in if let error = error { reject(error) } fulfill(item) } dataTask.resume() } } Next, you can write the handlers Promises help to improve the flow when responding to these error cases They help you write your error handling logic in a linear way The errors “short circuit” to the end of the handler where the code can serialize that error to the client Getting back a list of items takes the following steps: first, all of the items are retrieved from the database as a promise Once that promise is fulfilled, the items can be sent back to the client But if there was an error with servicing the request, the error can be serialized back to the user In all cases, we want the next handler in the chain to be invoked: func handleGetCouchDBItems( request: RouterRequest, response: RouterResponse, callNextHandler: @escaping () -> Void ) throws { firstly { getAllItems() } then(on: queue) { response.send(json: JSON(items.dictionary)) } catch(on: queue) { response.status(.badRequest) response.send(error.localizedDescription) } always(on: queue) { callNextHandler() } } The add item handler also benefits from using promises A new item is parsed from the body, and the item is sent to the database to be persisted If there was an error either with the parsing or with the network connectivity, that error is handled and sent to the client: func handleAddCouchDBItem( request: RouterRequest, response: RouterResponse, callNextHandler: @escaping () -> Void ) { firstly { () -> Promise in guard case let json(jsonBody)? = request.body else { throw ItemError.malformedJSON } let item = try Item(json: jsonBody) return addItem(item: item) } then(on: queue) { item in response.send("Added \(item.title)") } catch(on: queue) { error in response.status(.badRequest) response.send(error.localizedDescription) } always(on: queue) { callNextHandler() } } Register the handlers: router.get ( "/v1/couch/item", handler: handleGetCouchDBItems ) router.post( "/v1/couch/item", handler: handleAddCouchDBItem ) You can now test your application using the same aforementioned cURL statements $ curl -H "Content-Type: application/json" \ -X POST \ -d '{"title":"Finish book!"}' localhost:8090/v1/couch/item Added 'Item(id: 054879B8-B798-4462-AF0B-79B20F9617F4, title: "Finish book!")' $ curl localhost:8090/v1/couch/item [ { "id" : "054879B8-B798-4462-AF0B-79B20F9617F4", "title" : Finish book! } ] You have learned how to create a fully working to-do list application in Swift that uses a database and can be deployed to a server Scratch that off your bucket list Conclusions Swift is a powerful programming language that supports both object-oriented and functional programming Its static type checking, type inferencing, optional types, value types, and closures allow you to write code that is clear, concise, and robust It catches more bugs at compile-time — such as accesses to nil values — than many other popular languages Swift’s facilities ease the implementation of constructs such as promises (a.k.a., futures) that greatly simplify asynchronous programming Its ahead-of-time compilation provides predictable performance and plays nicely with libraries written in other languages such as C Swift is well suited for real applications If you choose to implement your server-side code in Swift, you’ll find the necessary tools to help you The Swift Package Manager aids in importing other libraries into your project and building your application correctly The Kitura library helps get you started quickly building web apps by handling incoming web requests and routing them to your application code Bluemix and the IBM Cloud Tools for Swift provide for easy deployment We hope you consider writing the entire stack of your next application in Swift, from client to sensor to server About the Authors David Ungar holds a Ph.D in Computer Science from U.C Berkeley, taught at Stanford, and enjoys a research position at IBM in the Ubiquitous Platforms group in Cloud and Mobile Enterprise Research He loves programming and has enjoyed APL, C, C++, Smalltalk, Self (which he codesigned), Java, JavaScript, Objective-C, and Swift His interests have included UNIX system programming, microprocessors, object-oriented language design and implementation, cartoon animation techniques for user interfaces, reflection APIs, IDEs, emergence for massive parallelism, and contextual programming Now he builds iOS and macOS applications Four of his papers have been honored by the Association for Computing Machinery for lasting impact In 2009, he was awarded the Dahl-Nygaard prize for outstanding career contributions to object-oriented language technology by the Association Internationale pour les Technologies Objets He blogs at http://blog.davidungar.net and tweets at @senderPath Robert F Dickerson is a software engineer in the Swift@IBM engineering group Having written many end-to-end applications in Java, JavaScript, and Swift (on iOS), he loves being able to write his server-side code in Swift! He was one of the original developers who made Kitura, a web-service middleware framework for Swift He leads a team that builds applications and libraries that use Kitura He is active in the open-source Swift community, where he has given talks about server-side Swift at AltConf 2016, Try! Swift NYC 2016, and the Server-side Swift Year-Long Conference 2016 Before joining IBM as a developer, he was on the faculty in the computer science departments at the College of William and Mary and then at the University of Texas at Austin He holds a Ph.D in Computer Science from the University of Virginia He blogs at https://developer.ibm.com/swift/ and tweets at @rfdickerson Preface: Swift for the Rest of Your Application Acknowledgments A Swift Introduction Types and Type Inference Syntax Simple Enumerations Tuples Custom Operators Closures Object Orientation Protocols Define Interfaces Generic Protocols Extending Classes, Structures, and Enumerations Optional Types, Structures, & Enumerations Optional Types Exterminate Nil-Value Bugs Structures Isolate Mutation Mutating Methods Default Implementations with Protocol Extensions Enumerations with Associated Values Enumerations as Inside-Out Class Hierarchies Choosing an Aggregate Instantiate a Class for a Thing with an Identity Otherwise, Replace the Class with a Value Type Swift Promises to Tame Asynchrony Step 1: Chaining Synchronous Errors Step 2: Linearizing Nested Callbacks Composing Asynchronous Operations Replacing Callbacks Step 3: Coping with Asynchronous Errors Promising Either Success or Failure Promises with Handy Shortcuts for Success Versus Failure Swift Package Manager Semantic Versioning Creating an Application Importing a Library in Your Project Developing in Xcode Creating Your Own Library Sharing a Library with the Swift Community Producing More Complex Projects Using C Dynamic Libraries Writing a To-Do List with Kitura Servers and Routers Creating a Web Service Deploying Your Application to Bluemix More Elaborate To-Do Items Start with a Dictionary Move to a Structure Adding Authentication Setting up the Database Connecting to the Database Conclusions .. .Extending Swift Value(s) to the Server David Ungar and Robert Dickerson Extending Swift Value(s) to the Server by David Ungar and Robert Dickerson Copyright... and Karl Weinmeister In addition, we want to extend our thanks to the many IBM Swift engineers and Swift community members working to bring Swift to the server — including Chris Bailey, Hubertus... Protocols Generic entities allow the same basic code to apply to different types In addition to concrete types, Swift also allows protocols to be generalized to different types, although the