Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Server Side Swift - A intro to Swift as a backend language

Server Side Swift - A intro to Swift as a backend language

Ever dreamt of writing your own app but got scared of the thought of having to develop an accompanying backend? You’re not the only one and probably not the last one – but it doesn’t have to be that scary. Swift made big steps to be an all-encompassing language in the past years and has matured enough to power performant backend services. Come and learn how you can quickly develop scalable Swift microservices and create a backend to your frontend in the same language.

Florian Harr

August 28, 2019
Tweet

More Decks by Florian Harr

Other Decks in Programming

Transcript

  1. The Idea Use Swift on Backend Swift ❤ Static &

    Strongly typed Not JavaScript Code Sharing Models Optionality (& Validation)
  2. The Execution Discover available options Find someone to do the

    work (Intern) Deploy backend & consume on iOS
  3. final class TodoController: RouteCollection { func boot(router: Router) throws {

    router.get("todos", use: index) router.post("todos", use: create) router.delete("todos", Todo.parameter, use: delete) } /// Returns a list of all `Todo`s. func index(_ req: Request) throws -> Future<[Todo]> { return Todo.query(on: req).all() } } public func routes(_ router: Router) throws { // Example of configuring a controller let todoController = TodoController() try router.register(collection: todoController) } Global /// A single entry of a Todo list. final class Todo: SQLiteModel { /// The unique identifier for this `Todo`. var id: Int? /// A title describing what this is var title: String /// Creates a new `Todo`. init(id: Int? = nil, title: String) { self.id = id self.title = title } } /// Allows `Todo` to be used extension Todo: Migration { } /// Allows `Todo` to be encoded extension Todo: Content { } /// Allows `Todo` to be used as a dynamic extension Todo: Parameter { } Model
  4. Purely written in Swift (5) Fully Open Source Codable baked

    in via Content Protocol Native DB connectors Native Templating (Leaf) and ORM (Fluent) engines ~∞ hours of tutorial on Vapor University, RayWenderlich … Vapor Cloud allows easy deployment
  5. func postInit() throws{ router.post("/", handler: createHandler) router.get("/", handler: getAllHandler) router.get("/",

    handler: getOneHandler) } func getAllHandler(completion: ([ToDo]?, RequestError?) -> Void ) -> Void { completion(todoStore, nil) } Handler (Application.swift) public struct ToDo: Codable, Equatable { public var id: Int? public var title: String? public var user: String? public var order: Int? public var completed: Bool? public var url: String? public init(title: String?, user: String?, order: Int?, completed: Bool?) { self.title = title self.user = user self.order = order self.completed = completed self.url = nil self.id = nil } } Model Router (Application.swift)
  6. Purely written in Swift (5) Developed by IBM - open

    source Uses and exposes Codable directly Kuery provides SQL abstraction layer as ORM Large availability of middleware and adapters (e.g. FastCGI, …) Tutorials and Documentation provided by IBM 1st class deployment on IBM BlueMix/Cloud functions
  7. routes.add(method: .get, uri: "/", handler: { request, response in let

    todo = Todo() // Setting the response content type explicitly to application/json response.setHeader(.contentType, value: "application/json") // Setting the body response to the JSON list generated response.appendBody(string: try! todo.jsonEncodedString()) // Signalling that the request is completed response.completed() } ) server.addRoutes(routes) class Todo: JSONConvertibleObject { public var title: String? public var user: String? public var order: Int? public var completed: Bool? public var url: String? override public func setJSONValues(_ values: [String : Any]) { self.title = getJSONValue(named: "title", from: values, defaultValue: "") self.user = getJSONValue(named: "user", from: values, defaultValue: "") self.order = getJSONValue(named: "order", from: values, defaultValue: nil) self.completed = getJSONValue(named: "completed", from: values, defaultValue: nil) self.url = getJSONValue(named: "url", from: values, defaultValue: "") } override public func getJSONValues() -> [String : Any] { return [ "title": title, "user": user, Model Router + Handler
  8. Mix of C/C++ bindings to Swift (4.1) Developed by PerfectlySoft

    - .ca Startup Uses custom JSON implementations Biggest number of connectors (some are outdated though) Native DB Connectors and StORM Offers deployment desktop applications and native connectors (FastCGI)
  9. Code Sharing Models can be shared (Extracted) Business Logic Shared

    code goes into additional shared (SPM) package Foundation (macOS) != Foundation (linux)
  10. Cocoa Touch
 like syntax Community
 Size* Library 
 Availability Deployment

    
 Options +++ +++ - +++ + + ++ +++ ++ +++ +++ +/++
  11. Server Side Swift - MicroService Code Sharing takes it’s toll

    but solves problems Vapor and Kitura easy entry for “simple” REST APIs Swift being compiled (vs Interpreted) uses on avg. 1/10th of the resources (RAM)* Foundation being different is (ノಠ益ಠ)ノ⼺彡┻━┻
  12. TRADITIONAL REST /persons /posts [{ “name”: “Florian”, “age”: 100 },

    { “name”: “John”, “age”: 10 }] [{ “title”: “Server Side Swift”, “author”: “Florian” }, {…} ]
  13. GRAPHQL / { "data": { "allPersons": [ { "name": "Florian",

    "age": 100, "posts": [ { "title": "Server Side Swift" } ] }, { "name": "John", "age": 10, "posts": [ { "title": "How to get started with React & GraphQL" } ] } ] } } { allPersons { name age posts { title } } }
  14. enum Episode : String, Codable { case newHope = "NEWHOPE"

    case empire = "EMPIRE" case jedi = "JEDI" } protocol Character : Codable { …} struct Planet : Codable { … } struct Human : Character { … } struct Droid : Character { … } final class StarWarsResolver { func getCharacter(id: String) -> Character? { humanData[id] ?? droidData[id] } */ func getFriends(of character: Character) -> [Character] { character.friends.compactMap { id in getCharacter(id: id) } } */ func search(query: String) -> [SearchResult] { return getPlanets(query: query) + getHumans(query: query) + getDroids(query: query) } } Define your Entities Define your Resolvers
  15. struct StarWarsAPI : FieldKeyProvider { typealias FieldKey = FieldKeys enum

    FieldKeys : String { … } struct HeroArguments : Codable { let episode: Episode? } func getHero(store: StarWarsStore, arguments: HeroArguments) -> Character { store.getHero(of: arguments.episode) } struct HumanArguments : Codable { let id: String } func getHuman(store: StarWarsStore, arguments: HumanArguments) -> Human? { store.getHuman(id: arguments.id) } struct DroidArguments : Codable { let id: String } func getDroid(store: StarWarsStore, arguments: DroidArguments) -> Droid? { store.getDroid(id: arguments.id) } struct SearchArguments : Codable { let query: String } func search(store: StarWarsStore, arguments: SearchArguments) -> [SearchResult] { store.search(query: arguments.query) } } API Definition Defining API context
 and available resolvers
  16. import Graphiti let starWarsSchema = Schema<StarWarsAPI, StarWarsStore> { Enum(Episode.self) {

    Value(.newHope) .description("Released in 1977.") } .description("One of the films in the Star Wars Trilogy.") Interface(Character.self, fieldKeys: CharacterFieldKeys.self) { Field(.id, at: \.id) .description("The id of the character.") } .description("A character in the Star Wars Trilogy.") Type(Droid.self, interfaces: Character.self) { Field(.id, at: \.id) Field(.friends, at: Droid.getFriends) .description("The friends of the droid, or an empty list if they have none.") } .description("A mechanical creature in the Star Wars universe.") Union(SearchResult.self, members: Planet.self, Human.self, Droid.self) Query { Field(.hero, at: StarWarsAPI.getHero) .description("Returns a hero based on the given episode.") .argument(.episode, at: \.episode, description: "") } Types(Human.self, Droid.self) } Schema Definition Types, Interfaces, Unions 
 and Queries Schema information 
 displayed during 
 introspection
  17. let query = """ query HeroNameQuery { hero { name

    friends { name appearsIn } } } let result = try starWarsSchema.execute( request: query, root: StarWarsAPI(), context: StarWarsStore(), eventLoopGroup: eventLoopGroup ) { "data": { "hero": { "friends": [ { "name": "Luke Skywalker", "appearsIn": [ "NEWHOPE", "EMPIRE", "JEDI" ] }, { "name": "Han Solo", "appearsIn": [ "NEWHOPE", "EMPIRE", "JEDI" ] }, { "name": "Leia Organa", "appearsIn": [ "NEWHOPE", "EMPIRE", "JEDI" ] } ], "name": "R2-D2" } } }
  18. Deployment Options Use in conjunction with Vapor/Kitura Streamline to Apache/Nginx

    server Vapor Cloud, Docker or bare installation on AWS
  19. Swift or Node? Swift is fast and type-safe Low memory

    footprint and type safety a joy Great Error Handling (Chain of doom) iOS Integration with Apollo is fantastic Node has extensive support and widely available Future extensibility probably never an issue Deployment is way easier More learning resources/help available
  20. Use Cases Server Side Swift already Library available? No-Brainer No

    (external) Dependencies? No-Brainer Complex needs with other services? Probably-Not BUT SWIFT?! Wait a little and contribute
  21. A Step Further WWDC 2016: Going Server-side with Swift Open

    Source Shortly after: Vapor, Kitura and Perfect got release October 2016: SSWG Swift Server Work Group founded September 2018: Swift NIO Accepted 2019: Postgres, Redis, APNS, HTTP native clients coming 2020: You?