Successfully reported this slideshow.
Your SlideShare is downloading. ×

Vapor – Swift is not only for iOS anymore

Ad

Vapor
“Swift is not only for iOS anymore”
1

Ad

Contents
• Introduce Vapor 3
• Demonstrate core concepts of Vapor 3
• Answer questions about Vapor
• (or at least attempt ...

Ad

Very shortly about myself…
Hi, my name is Milan Vít…
3

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

YouTube videos are no longer supported on SlideShare

View original on YouTube

Upcoming SlideShare
Taming Cloud APIs with Swift
Taming Cloud APIs with Swift
Loading in …3
×

Check these out next

1 of 47 Ad
1 of 47 Ad

Vapor – Swift is not only for iOS anymore

Download to read offline

Slides from talk I have as part of Vapor Meetup 2 in Tokyo on 2018-07-06.

The video recording from this talk is available on YouTube (https://www.youtube.com/watch?v=Uq1fJifXcOc) but unfortunately, audio issues plagued my talk. At the very least, here are slides from that evening.

Slides from talk I have as part of Vapor Meetup 2 in Tokyo on 2018-07-06.

The video recording from this talk is available on YouTube (https://www.youtube.com/watch?v=Uq1fJifXcOc) but unfortunately, audio issues plagued my talk. At the very least, here are slides from that evening.

Advertisement
Advertisement

More Related Content

Advertisement

Vapor – Swift is not only for iOS anymore

  1. 1. Vapor “Swift is not only for iOS anymore” 1
  2. 2. Contents • Introduce Vapor 3 • Demonstrate core concepts of Vapor 3 • Answer questions about Vapor • (or at least attempt to) 2
  3. 3. Very shortly about myself… Hi, my name is Milan Vít… 3
  4. 4. … and I’m a Vaporholic. (Fortunately not this kind) 4
  5. 5. About me • Started programming with Vapor in second half of 2017 • 4+ years as an iOS developer • Developed iOS apps for Dior, S.T.Dupont, Calvin Klein, Adidas, Total, Monsanto, Mladá Fronta DNES,… • Currently happily employed as Vapor engineer here in Monstar Lab ! 5
  6. 6. What is Vapor? 6
  7. 7. What is Vapor? • Web framework written in Swift • Built on top of Apple’s non-blocking networking framework SwiftNIO • Over 14,000 ⭐ on GitHub " • Version 3 released on May 4, 2018 7
  8. 8. What is Vapor? • Web framework written in Swift • Built on top of Apple’s non-blocking networking framework SwiftNIO • Over 14,000 ⭐ on GitHub " • Version 3 released(-ish # ) on May 4, 2018 8
  9. 9. Vapor features Async Authentication Crypto Data validation HTTP client JSON JWT Leaf templates MySQL PostgreSQL Redis SQLite URL routing Websocket ! Awesomeness ! 9
  10. 10. What makes Vapor! awesome? 10
  11. 11. Reason 1 Everything is Codable 11
  12. 12. Let’s start with a simple model… import FluentSQLite import Vapor final class Message: Codable { var id: Int? var username: String var text: String init(username: String, text: String) { self.username = username self.text = text } } extension Message: SQLiteModel {} extension Message: Migration {} extension Message: Content {} extension Message: Parameter {} extension Message: Timestampable {} 12
  13. 13. Want to store model in database? import FluentSQLite import Vapor final class Message: Codable { var id: Int? var username: String var text: String init(username: String, text: String) { self.username = username self.text = text } } extension Message: SQLiteModel {} extension Message: Migration {} extension Message: Content {} extension Message: Parameter {} extension Message: Timestampable {} 13
  14. 14. Want to accept or return model as JSON? import FluentSQLite import Vapor final class Message: Codable { var id: Int? var username: String var text: String init(username: String, text: String) { self.username = username self.text = text } } extension Message: SQLiteModel {} extension Message: Migration {} extension Message: Content {} extension Message: Parameter {} extension Message: Timestampable {} 14
  15. 15. Want to make model query-able? import FluentSQLite import Vapor final class Message: Codable { var id: Int? var username: String var text: String init(username: String, text: String) { self.username = username self.text = text } } extension Message: SQLiteModel {} extension Message: Migration {} extension Message: Content {} extension Message: Parameter {} extension Message: Timestampable {} 15
  16. 16. Want to automatically record access date? import FluentSQLite import Vapor final class Message: Codable { var id: Int? var username: String var text: String init(username: String, text: String) { self.username = username self.text = text } } extension Message: SQLiteModel {} extension Message: Migration {} extension Message: Content {} extension Message: Parameter {} extension Message: Timestampable {} 16
  17. 17. Reason 2 Pushing Swift forward 17
  18. 18. The team behind Vapor had to wait with the release of Vapor 3 for Swift 4.1 to be released first 18
  19. 19. Vapor is always one of the first OSS projects to use and embrace any new feature 19
  20. 20. Case in point: Swift keypaths extension User: BasicAuthenticatable { static let usernameKey: UsernameKey = User.username static let passwordKey: PasswordKey = User.password } • Keypath: a way to reference a type’s property without evaluating the property, in a type-safe way • If we ever change the password property to, say, superSecretPassword ➡ build-time error 20
  21. 21. Case in point: Swift keypaths final class MessageController: RouteCollection { func boot(router: Router) throws { let messageRoutes = router.grouped("message") messageRoutes.get("search", use: searchMessage) } func searchMessage(_ req: Request) throws -> Future<[Message]> { guard let term = req.query[String.self, at: "term"] else { throw Abort(.badRequest) } return try Message .query(on: req) .filter(Message.text ~~ term) .all() } } 21
  22. 22. Case in point: Swift keypaths final class MessageController: RouteCollection { func boot(router: Router) throws { let messageRoutes = router.grouped("message") messageRoutes.get("search", use: searchMessage) } func searchMessage(_ req: Request) throws -> Future<[Message]> { guard let term = req.query[String.self, at: "term"] else { throw Abort(.badRequest) } return try Message .query(on: req) .filter(Message.text ~~ term) .all() } } 22
  23. 23. Reason 3 It’s Future-istic 23
  24. 24. All about that Future • Asynchronous even before Apple released SwiftNIO • Drastic change from Vapor 2’s approach • Your code will be full of Futures, maps and flatMaps 24
  25. 25. All about that Future • Asynchronous even before Apple released SwiftNIO • Drastic change from Vapor 2’s approach • Your code will be full of Futures, maps and flatMaps • … and that is fine! 25
  26. 26. Future<T>: “This variable will contain data, the data will be of type T, but it’ll happen in the future, not right now” 26
  27. 27. Future<T>: “This variable will contain data1 , the data will be of type T2 , but it’ll happen in the future, not right now” 2  Future resolved with EventLoopFutureValue.success(T) 1  Likely – or it may be impossible to obtain the data (network error, inaccessible database,…) in which case the Future resolves with EventLoopFutureValue.failure(Error) 27
  28. 28. Synchronous approach func getMessagesHandler(_ req: Request) throws -> [Message] { // do something } • Directly returns the result… • … but blocks any further execution until the result can be delivered to the user 28
  29. 29. Vapor 3’s asynchronous approach func getMessagesHandler(_ req: Request) throws -> Future<[Message]> { // do something } • Specifies that result will be delivered… 29
  30. 30. Vapor 3’s asynchronous approach func getMessagesHandler(_ req: Request) throws -> Future<[Message]> { // do something } • Specifies that result will be delivered… • … at some point in the… 30
  31. 31. Vapor 3’s asynchronous approach func getMessagesHandler(_ req: Request) throws -> Future<[Message]> { // do something } • Specifies that result will be delivered… • … at some point in the… • … future 31
  32. 32. Vapor 3’s asynchronous approach func getMessagesHandler(_ req: Request) throws -> Future<[Message]> { // do something } • Specifies that result will be delivered… • … at some point in the… • … future !!! 32
  33. 33. Demystifying map and flatMap: Use map or flatMap when you want to perform an operation after one future completes (Maybe you can read maps and flatMaps as a simple then in your mind? ) 33
  34. 34. When to use map func getMessageText(_ req: Request) throws -> Future<String> { return try req.parameters.next(Message.self).map { message in // This closure DOES NOT create another Future return message.text } } 34
  35. 35. When to use flatMap func likeMessage(_ req: Request) throws -> Future<Message> { return try req.parameters.next(Message.self).flatMap { message in // This closure DOES create another Future message.likes += 1 return message.save(on: req) } } 35
  36. 36. What if we don’t care about the result? 36
  37. 37. Transform away! func likeMessage(_ req: Request) throws -> Future<HTTPStatus> { return try req.parameters.next(Message.self).flatMap { message in // This closure DOES create another Future message.likes += 1 return message.save(on: req).transform(to: .ok) } } 37
  38. 38. ! You mastered map, flatMap and transform! Can you handle a few more? 38
  39. 39. Let’s unwrap and catch your Optionals func latest(_ req: Request) throws -> Future<Todo> { return req.withPooledConnection(to: .redis) { redis in return redis // Query Redis… .jsonGet("latest", as: Todo.self) // … which returns Optional<Todo> .unwrap(or: Abort(.notFound)) // Unwrap Optional, throw if nil .catchFlatMap { _ in // Catch the error we just threw… return try Todo .query(on: req) // … and try to query DB instead .sort(.id, .descending) .first() .unwrap(or: Abort(.notFound)) } } } 39
  40. 40. Handling multiple Futures with flatten func dislikeAll(_ req: Request) throws -> Future<HTTPStatus> { // Dislike all messages return Message.query(on: req).all().flatMap { messages in // Fetch all messages var messageSaveResult: [Future<Message>] = [] for message in messages { guard message.likes > 0 else { // Skip if no likes continue } message.likes -= 1 // Remove like and… messageSaveResult.append(message.save(on: req)) // … store the save operation } return messageSaveResult .flatten(on: req) // Execute all save operations… .transform(to: .ok) // … but return HTTPStatus.ok instead } } 40
  41. 41. One more flatMap func updateMessage(_ req: Request) throws -> Future<Message> { return try req.parameters.next(Message.self).flatMap { existingMessage in return try req.content.decode(Message.self).flatMap { updateData in existingMessage.username = updateData.username existingMessage.text = updateData.text return existingMessage.save(on: req) } } } ! Could the code be going any more to the right side? Imagine this with many more Futures! 41
  42. 42. One more flatMap func updateMessage(_ req: Request) throws -> Future<Message> { return try flatMap( // Global function flatMap() to: Message.self, // Final result is Message.self req.parameters.next(Message.self), // Future A req.content.decode(Message.self) // Future B ) { existingMessage, updateData in // Closure with result of A and B existingMessage.username = updateData.username existingMessage.text = updateData.text return existingMessage.save(on: req) } } There are overloads for merging up to 5 different Futures! 42
  43. 43. Reason 4 Community 43
  44. 44. Community • ~2000 people on Discord server, ~6000 people on Slack • Friendly, warm, helpful community • Always ready to help with your issue 44
  45. 45. Community • ~2000 people on Discord server, ~6000 people on Slack • Friendly, warm, helpful community • Always ready to help with your issue • Or to make fun of PHP ! " • So join us at http://vapor.team! 45
  46. 46. Thank you for your attention! Question time! 46

×