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

[iOS Conf SG 2016] A Toy Web Framework in Swift

[iOS Conf SG 2016] A Toy Web Framework in Swift

A talk using various parts of a web framework implementation to talk about a few Swift features — what they can do, and what they can't do (yet).

Hwee-Boon Yar

October 21, 2016
Tweet

More Decks by Hwee-Boon Yar

Other Decks in Programming

Transcript

  1. Why a Web Framework? • I've never built one •

    Well-known domain • The basic premise is simple
  2. Wiring up GCDWebServer let response = appServer.process(request: Request(verb: req.method!, path:

    req.path, query: req.query, form: req.arguments)) return GCDWebServerDataResponse(html: response)
  3. Routing get '/hobbies' do 'Return the HTML for a list

    of hobbies here' end post '/hobbies/new' do #Create a new hobby and redirect to GET end
  4. Perfect api2Routes.add(method: .get, uri: "/call2", handler: { _, response in

    response.setBody(string: "API v2 CALL 2") response.completed() }) — h$ps://github.com/PerfectlySo9/PerfectExample-URLRouAng
  5. Routing let routes: [String: Component] = "/something": someComponent, "/anotherPath": anotherComponent,

    "*": mainComponent] let appServer = BlameworkAppServer(routes: routes)
  6. html.head { html.title("Blamework demo") } html.body { html in html.p("My

    TODO list for today:") html.form { html in html.textField("item") html.submit("Add Item") } html.ol { html in for e in items { html.li(e) } } }
  7. A domain-specific language (DSL) is a computer language specialized to

    a particular application domain. — h$ps://en.wikipedia.org/wiki/Domain-specific_language
  8. html.head { html.title("Blamework demo") } html.body { html in html.p("My

    TODO...:") html.form { html in html.textField("item") html.submit("Add Item") } html.ol { html in for e in items { html.li(e) } } }
  9. @discardableResult public func body( _ string: String) -> String {

    return tag(name: "body", string: string) } @discardableResult public func body( block: (_ writer: HTMLWriter)->()) -> String { return tag(name: "body", block: block) }
  10. @discardableResult public func tag( name: String, attributes: [String:String]=[String:String](), block: (_

    writer: HTMLWriter)->()) -> String { let w = self.childWriter() let attr = attributesToString(attributes: attributes) w.name = "\(name)'s" w.string("<\(name) \(attr)>") w.string(block) w.string("</\(name)>") return w.toString() }
  11. @discardableResult public func tag( name: String, attributes: [String:String]=[String:String](), string: String)

    -> String { let w = self.childWriter() let attr = attributesToString(attributes: attributes) w.name = "\(name)'s" w.string("<\(name) \(attr)>") w.string(string) w.string("</\(name)>") return w.toString() }
  12. @discardableResult public func body( _ string: String) -> String {

    return tag(name: "body", string: string) } @discardableResult public func body( block: (_ writer: HTMLWriter)->()) -> String { return tag(name: "body", block: block) }
  13. map([!, ", #], cook) => [$, %, &] filter([$, %,

    &], isVegetarian) => [$, &] reduce([$, &], eat) => ' — h$ps://twi$er.com/steveluscher/status/741089564329054208
  14. var addresses = [ Address(line1: "Block 123", line2: "Tampines Street

    33", zipCode: "523123"), Address(line1: "20", line2: "Maryland Road", zipCode: "567800")] addresses.write(to: "/path/to/somewhere")
  15. struct Address { var line1: String var line2: String var

    zipCode: Int func toDictionary() -> [String:Any] { return ["line1": line1, "line2": line2, "zipCode: zipCode"] } }
  16. extension PersistentObject { func toDictionary() -> [String:Any] { let result

    = gen(value: self) if let result = result as? [String:Any] { return result } else { return [String:Any]() } } func gen(value: Any) -> Any {//...} }
  17. Reflection + Mirror func gen(value: Any) -> Any { let

    m = Mirror(reflecting: value) if m.displayStyle == .struct { var result = [String:Any]() for case let (label?, value) in m.children { result[label] = gen(value: value) } return result } else { return value } }
  18. extension Array where Element: PersistentObject { func write(to: String) ->

    Bool { let array = map {$0.toDictionary()} as NSArray return array.write(to: URL(fileURLWithPath: to), atomically: true) } } extension Dictionary where Value: PersistentObject { func write(to: String) -> Bool { var dict = [Key:[String:Any]]() for (k, v) in self { dict[k] = v.toDictionary() } let dict2 = dict as NSDictionary return dict2.write(to: URL(fileURLWithPath: to), atomically: true) } }
  19. var addresses = [ Address(line1: "Block 123", line2: "Tampines Street

    33", zipCode: "523123"), Address(line1: "20", line2: "Maryland Road", zipCode: "567800")] addresses.write(to: "/path/to/somewhere")
  20. print(items) [Item(title: "Item 1", age: 10, address: Address(value: "address 1")),

    Item(title: "Item 2", age: 10, address: Address(value: "address 1"))]
  21. [Item(title: "Item 1", age: 10, address: Address(value: "address 1")), Item(title:

    "Item 2", age: 10, address: Address(value: "address 1"))]
  22. dump(items) ▿ 2 elements ▿ Item - title: "Item 1"

    - age: 10 ▿ address: Address - value: "address 1" ▿ Item - title: "Item 2" - age: 10 ▿ address: Address - value: "address 1"
  23. func gen(value: Any) -> Any { let m = Mirror(reflecting:

    value) if m.displayStyle == .struct { var result = [String:Any]() for case let (label?, value) in m.children { result[label] = gen(value: value) } return result } else { return value } }
  24. var nums: [Int?] = [1, 2, 3, nil, 5] for

    e in nums { print("Value: ", e) } Value: Optional(1) Value: Optional(2) Value: Optional(3) Value: nil Value: Optional(5)
  25. var nums: [Int?] = [1, 2, 3, nil, 5] for

    case let e? in nums { print("Value: ", e) } Value: 1 Value: 2 Value: 3 Value: 5
  26. var nums: [Int?] = [1, 2, 3, nil, 5] for

    case let e? in nums { print("Value: ", e) } for e in nums.flatMap({$0}) { print("Value: ", e) } Value: 1 Value: 2 Value: 3 Value: 5
  27. extension Array where Element: PersistentObject { func write(to: String) ->

    Bool { let array = map {$0.toDictionary()} as NSArray return array.write(to: URL(fileURLWithPath: to), atomically: true) } }
  28. extension Collection where Iterator.Element: PersistentObject { } // var list:

    [PersistentObject] // this is NOT what I want ^ extension Collection where Iterator.Element == PersistentObject { } // var list: [SomeTypeThatImplementsPersistentObject] //e.g // var list: [Addresses] // this is what I want for PersistentObject^
  29. Reflection Smoke + Mirrors extension PersistentObject { func toDictionary() ->

    [String:Any] {} //... } extension Array where Element: PersistentObject {//...} extension Dictionary where Value: PersistentObject {//...}
  30. for e in 0...6 { print("Seq \(e): \(fib(e))") } Seq

    0: [0] Seq 1: [0, 1] Seq 2: [0, 1, 1] Seq 3: [0, 1, 1, 2] Seq 4: [0, 1, 1, 2, 3] Seq 5: [0, 1, 1, 2, 3, 5] Seq 6: [0, 1, 1, 2, 3, 5, 8]
  31. //0, 1, 1, 2, 3, 5, 8, 13, 21, ...

    func fib(_ i: Int) -> [Int] { if i == 0 { return [0] } else if i == 1 { return [0, 1] } else { let seq = fib(i-1) let c = seq.count let next = [seq[c-2] + seq[c-1]] return seq + next } }
  32. #0, 1, 1, 2, 3, 5, 8, 13, 21, ...

    def fib(0) do [0] end def fib(1) do [1, 0] end def fib(n) do [x, y | rest] = fib(n-1) [x+y, x, y | rest] end