Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Taking Swift to the Server
let me = Person(name: "Jens Ravens", company: "nerdgeschoss")
@JensRavens
GitHub: JensRavens
jensravens.com
nerdgeschoss.de
A short introduction to
http, tcp and ip.
Browser
http://jensravens.com
Browser
http://jensravens.com
DNS Server
Browser
http://jensravens.com
DNS Server
;; ANSWER SECTION:
jensravens.com. 3600 INA 37.120.178.83
Browser
http://jensravens.com
DNS Server
;; ANSWER SECTION:
jensravens.com. 3600 INA 37.120.178.83
37.120.178.83
Browser
http://jensravens.com
DNS Server
37.120.178.83
Browser
http://jensravens.com
DNS Server
37.120.178.83
GET / HTTP/1.1
Host: jensravens.com
User-Agent: curl/7.43.0
Accept:...
Browser
http://jensravens.com
DNS Server
37.120.178.83
Server
GET / HTTP/1.1
Host: jensravens.com
User-Agent: curl/7.43.0
...
Browser
http://jensravens.com
DNS Server
37.120.178.83
Server
Load Balancer
GET / HTTP/1.1
Host: jensravens.com
User-Agent...
Browser
http://jensravens.com
DNS Server
37.120.178.83
Server
Load Balancer
App App
GET / HTTP/1.1
Host: jensravens.com
Us...
Browser
DNS Server Server
Load Balancer
App App
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 7897
Connection: Keep-Alive
<!DOCTYPE html>
Browser
DNS Server Ser...
Hey, that’s just some
plain text over tcp!
Get a Request,
give a Response
public protocol RequestType {
var context: [String:AnyObject] { get set }
var method: HTTPMethod { get }
var path: String ...
public protocol ResponseType {
var headers: [String: HeaderType] { get }
var code: StatusCode { get }
var content: Streama...
public protocol ResponseType {
var headers: [String: HeaderType] { get }
var code: StatusCode { get }
var content: Streama...
public typealias AppType = RequestType throws
-> ResponseType
public typealias AppType = RequestType throws
-> ResponseType
let myApp: AppType = { request in
return Response(code: 200,...
Where to go next:
The Router
public final class Router {
public func route(method: HTTPMethod, path: String,
app: Void -> AppType)
public func get(path...
public final class Router {
public func route(method: HTTPMethod, path: String,
app: Void -> AppType)
public func get(path...
public final class Router {
public func route(method: HTTPMethod, path: String,
app: Void -> AppType)
public func get(path...
Making things pretty -
Rendering Views
public protocol ViewType {
func render() throws -> Streamable
var contentType: FormatType { get }
}
public protocol ViewType {
func render() throws -> Streamable
var contentType: FormatType { get }
}
public struct JSONView...
public protocol ControllerType {}
extension ControllerType {
func redirect(path: PathConvertible,
statusCode: StatusCode =...
struct UsersController: ControllerType {
func index(request: RequestType) throws -> ResponseType {
let users: [User] = …
s...
struct UsersController: ControllerType {
func index(request: RequestType) throws -> ResponseType {
let users: [User] = …
s...
Extending the Stack -
Add some Middleware
public typealias MiddlewareType = AppType -> AppType
public func + (lhs: MiddlewareType, rhs: AppType) -> AppType {
return...
public typealias MiddlewareType = AppType -> AppType
public func + (lhs: MiddlewareType, rhs: AppType) -> AppType {
return...
public typealias MiddlewareType = AppType -> AppType
public func + (lhs: MiddlewareType, rhs: AppType) -> AppType {
return...
class CookieStore {
struct Cookie {
public var name: String
public var value: String
public var validUntil: NSDate?
}
var ...
class CookieStore {
struct Cookie {
public var name: String
public var value: String
public var validUntil: NSDate?
}
var ...
class CookieStore {
struct Cookie {
public var name: String
public var value: String
public var validUntil: NSDate?
}
var ...
func login(request: RequestType) throws -> ResponseType {
let secret = request.params["secret"] ?? ""
request.cookies["sec...
func login(request: RequestType) throws -> ResponseType {
let secret = request.params["secret"] ?? ""
request.cookies["sec...
Middleware
Cookies
Sessions
Current User
Format Detection
Custom Error Pages
HTTP Body Parser
Static File Server
Analytics
Mounting your App
Nest Server Interface
https://github.com/nestproject/Nest
import Curassow
import Inquiline
serve { request in
return Respo...
Epoch
https://github.com/Zewo/Epoch
struct ServerResponder: ResponderType {
func respond(request: Request) -> Response {
/...
Swag
https://github.com/swagproject/swag
import Foundation
import Curassow
import SwagNest
final class NextApp: App {
over...
Thank you.
@JensRavens
github.com/swagproject/swag
Upcoming SlideShare
Loading in …5
×

Server Side Swift with Swag

5,088 views

Published on

Swift on Linux is finally there. What about building your next big coding project in Swift instead of Rails or node.js?

Published in: Software
  • Be the first to comment

Server Side Swift with Swag

  1. 1. Taking Swift to the Server
  2. 2. let me = Person(name: "Jens Ravens", company: "nerdgeschoss") @JensRavens GitHub: JensRavens jensravens.com nerdgeschoss.de
  3. 3. A short introduction to http, tcp and ip.
  4. 4. Browser http://jensravens.com
  5. 5. Browser http://jensravens.com DNS Server
  6. 6. Browser http://jensravens.com DNS Server ;; ANSWER SECTION: jensravens.com. 3600 INA 37.120.178.83
  7. 7. Browser http://jensravens.com DNS Server ;; ANSWER SECTION: jensravens.com. 3600 INA 37.120.178.83 37.120.178.83
  8. 8. Browser http://jensravens.com DNS Server 37.120.178.83
  9. 9. Browser http://jensravens.com DNS Server 37.120.178.83 GET / HTTP/1.1 Host: jensravens.com User-Agent: curl/7.43.0 Accept: */*
  10. 10. Browser http://jensravens.com DNS Server 37.120.178.83 Server GET / HTTP/1.1 Host: jensravens.com User-Agent: curl/7.43.0 Accept: */*
  11. 11. Browser http://jensravens.com DNS Server 37.120.178.83 Server Load Balancer GET / HTTP/1.1 Host: jensravens.com User-Agent: curl/7.43.0 Accept: */*
  12. 12. Browser http://jensravens.com DNS Server 37.120.178.83 Server Load Balancer App App GET / HTTP/1.1 Host: jensravens.com User-Agent: curl/7.43.0 Accept: */*
  13. 13. Browser DNS Server Server Load Balancer App App
  14. 14. HTTP/1.1 200 OK Content-Type: text/html Content-Length: 7897 Connection: Keep-Alive <!DOCTYPE html> Browser DNS Server Server Load Balancer App App
  15. 15. Hey, that’s just some plain text over tcp!
  16. 16. Get a Request, give a Response
  17. 17. public protocol RequestType { var context: [String:AnyObject] { get set } var method: HTTPMethod { get } var path: String { get } var params: [String:String] { get } var headers: [String:String] { get } var format: Format { get } var body: Streamable? { get } }
  18. 18. public protocol ResponseType { var headers: [String: HeaderType] { get } var code: StatusCode { get } var content: Streamable { get } } public protocol RequestType { var context: [String:AnyObject] { get set } var method: HTTPMethod { get } var path: String { get } var params: [String:String] { get } var headers: [String:String] { get } var format: Format { get } var body: Streamable? { get } }
  19. 19. public protocol ResponseType { var headers: [String: HeaderType] { get } var code: StatusCode { get } var content: Streamable { get } } public protocol RequestType { var context: [String:AnyObject] { get set } var method: HTTPMethod { get } var path: String { get } var params: [String:String] { get } var headers: [String:String] { get } var format: Format { get } var body: Streamable? { get } } public protocol Streamable { var stream: Void -> NSData? { get } }
  20. 20. public typealias AppType = RequestType throws -> ResponseType
  21. 21. public typealias AppType = RequestType throws -> ResponseType let myApp: AppType = { request in return Response(code: 200, headers: [:], content: "Hello World") }
  22. 22. Where to go next: The Router
  23. 23. public final class Router { public func route(method: HTTPMethod, path: String, app: Void -> AppType) public func get(path: String, app: Void -> AppType) public func app(request: RequestType) throws -> ResponseType }
  24. 24. public final class Router { public func route(method: HTTPMethod, path: String, app: Void -> AppType) public func get(path: String, app: Void -> AppType) public func app(request: RequestType) throws -> ResponseType } let myApp: AppType = { request in return Response(code: 200, headers: [:], content: "Hello World") }
  25. 25. public final class Router { public func route(method: HTTPMethod, path: String, app: Void -> AppType) public func get(path: String, app: Void -> AppType) public func app(request: RequestType) throws -> ResponseType } let myApp: AppType = { request in return Response(code: 200, headers: [:], content: "Hello World") } let router = Router() router.get("home") { myApp } let routedApp = router.app
  26. 26. Making things pretty - Rendering Views
  27. 27. public protocol ViewType { func render() throws -> Streamable var contentType: FormatType { get } }
  28. 28. public protocol ViewType { func render() throws -> Streamable var contentType: FormatType { get } } public struct JSONView: ViewType { public var contentType: FormatType { return Format.JSON } let contents: AnyObject public init(_ contents: AnyObject) { self.contents = contents } public func render() throws -> Streamable { return try NSJSONSerialization.dataWithJSONObject( contents, options: .PrettyPrinted) } }
  29. 29. public protocol ControllerType {} extension ControllerType { func redirect(path: PathConvertible, statusCode: StatusCode = 302) -> ResponseType func render(view view: ViewType, statusCode: StatusCode = 200) throws -> ResponseType func render(json json: AnyObject, statusCode: StatusCode = 200) throws -> ResponseType func render(html html: String, statusCode: StatusCode = 200) throws -> ResponseType }
  30. 30. struct UsersController: ControllerType { func index(request: RequestType) throws -> ResponseType { let users: [User] = … switch request.format { case .JSON: return try render(json: users.map { $0.json }) default: return try render(view: MustacheView(name: "template", context: users)) } } }
  31. 31. struct UsersController: ControllerType { func index(request: RequestType) throws -> ResponseType { let users: [User] = … switch request.format { case .JSON: return try render(json: users.map { $0.json }) default: return try render(view: MustacheView(name: "template", context: users)) } } } let router = Router() router.get(“/users") { UsersController().index } let routedApp = router.app
  32. 32. Extending the Stack - Add some Middleware
  33. 33. public typealias MiddlewareType = AppType -> AppType public func + (lhs: MiddlewareType, rhs: AppType) -> AppType { return lhs(rhs) }
  34. 34. public typealias MiddlewareType = AppType -> AppType public func + (lhs: MiddlewareType, rhs: AppType) -> AppType { return lhs(rhs) } let awesomeMiddleware: MiddlewareType = { app in return { request in var response = try app(request) response.content = "Overwritten by middleware" return response } }
  35. 35. public typealias MiddlewareType = AppType -> AppType public func + (lhs: MiddlewareType, rhs: AppType) -> AppType { return lhs(rhs) } let awesomeMiddleware: MiddlewareType = { app in return { request in var response = try app(request) response.content = "Overwritten by middleware" return response } } let router = Router() router.get(“/users") { awesomeMiddleware + UsersController().index } let routedApp = router.app
  36. 36. class CookieStore { struct Cookie { public var name: String public var value: String public var validUntil: NSDate? } var cookies = [String:Cookie]() subscript(key: String) -> String? static func middleware(app: AppType) -> AppType }
  37. 37. class CookieStore { struct Cookie { public var name: String public var value: String public var validUntil: NSDate? } var cookies = [String:Cookie]() subscript(key: String) -> String? static func middleware(app: AppType) -> AppType } let router = Router() router.get(“/users") { CookieStore().middleware + UsersController().index } let routedApp = router.app
  38. 38. class CookieStore { struct Cookie { public var name: String public var value: String public var validUntil: NSDate? } var cookies = [String:Cookie]() subscript(key: String) -> String? static func middleware(app: AppType) -> AppType } let router = Router() router.get(“/users") { CookieStore().middleware + UsersController().index } let routedApp = router.app extension RequestType { public var cookies: CookieStore! }
  39. 39. func login(request: RequestType) throws -> ResponseType { let secret = request.params["secret"] ?? "" request.cookies["secret"] = secret return try render(text: "Login successfull") } func secretStuff(request: RequestType) throws -> ResponseType { if request.cookies["secret"] == "superSecret" { return try render(text: "Here's the secret page") } else { return try render(text: "Permission denied!", status: 403) } }
  40. 40. func login(request: RequestType) throws -> ResponseType { let secret = request.params["secret"] ?? "" request.cookies["secret"] = secret return try render(text: "Login successfull") } func secretStuff(request: RequestType) throws -> ResponseType { if request.cookies["secret"] == "superSecret" { return try render(text: "Here's the secret page") } else { return try render(text: "Permission denied!", status: 403) } } let router = Router() router.post(“/login”) { CookieStore().middleware + login } router.get(“/my-secret-page“) { CookieStore().middleware + secretStuff } let routedApp = router.app
  41. 41. Middleware Cookies Sessions Current User Format Detection Custom Error Pages HTTP Body Parser Static File Server Analytics
  42. 42. Mounting your App
  43. 43. Nest Server Interface https://github.com/nestproject/Nest import Curassow import Inquiline serve { request in return Response(.Ok, contentType: "text/plain", body: "Hello World") }
  44. 44. Epoch https://github.com/Zewo/Epoch struct ServerResponder: ResponderType { func respond(request: Request) -> Response { // do something based on the Request return Response(status: .OK) } } let responder = ServerResponder() let server = Server(port: 8080, responder: responder) server.start()
  45. 45. Swag https://github.com/swagproject/swag import Foundation import Curassow import SwagNest final class NextApp: App { override init() { super.init() register(FileServer(root: publicDir).serve) register(BodyParser.middleware) register(CookieStore.middleware) router.get(“/users") { UsersController.middleware + UsersController().index } router.get("/users/:id") { UsersController().show } } } serve(NextApp().nest)
  46. 46. Thank you. @JensRavens github.com/swagproject/swag

×