Web Services with Swift

Web Services with Swift

Steve Goodrich Server Side Swift With Vapor https://github.com/zepedebo/PwnedWatcher Server Side Swift With Vapor https://github.com/zepedebo/PwnedWatcher The Project: Build a database of email addresses and check them against haveibeenpwned.com https://github.com/zepedebo/PwnedWatcher The Technology https://github.com/zepedebo/PwnedWatcher Swift https://swift.org/ Swift Provides Modern approachable language Reasonably good tools Cross platform development Swift Playgrounds https://swift.org/ Vapor https://vapor.codes/ Vapor Provides: Vapor https://vapor.codes/ HTTP/HTTPS Endpoint Web Sockets Middleware Crypto Authentication Validation Session Multipart Data Management Routing Data Serialization Caching Templates Database Logging Async / Deserialization SQLite3 https://www.sqlite.org SQLite Provides: Ubiquitous Data Store SQLite3 Flexible https://www.sqlite.org No setup required Requirements (for macOS) Homebrew Xcode 9.3 or greater (https://brew.sh/) Check Your Xcode slc-steveg-orm:~ steveg$ xcode-select -p /Applications/Xcode-beta.app/Contents/Developer slc-steveg-orm:~ steveg$ sudo xcode-select -s /Applications/Xcode.app Install Vapor slc-steveg-orm:~ steveg$ brew install vapor/tap/vapor https://github.com/zepedebo/PwnedWatcher Pwned Watcher • Create a list of e-mail addresses • Check them against “';--have i been pwned?” • Display how many sites are a problem for me Demonstrates • Simple URL routing • Get and Post HTTP commands • MVC Architecture • Vapor Client calls to a RESTful API • Async Structure of a Vapor Project Generated with: vapor new --web WebHello Simplest Routing Structure of an Address The scheme The computer The application The service / api call https://haveibeenpwned.com:443/api/v2/breaches We are mainly concerned with this part Adding a route All routes start in routes.swift Simplest Route http://localhost/hello // routes.swift import Vapor public func routes(_ router: Router) throws { router. get("hello") { req in return "Hello" } } Managing Complexity Model View Controller Controllers are the Glue http://localhost/hello // routes.swift import Vapor public func routes(_ router: Router) throws { router. get("hello") { req in return "Hello" } } Model Add a Model with Fluent Fluent gives you • Support for: • An ORM • SQLite • Migrations • MySQL • Relations • PostgreSQL • Transactions Interesting Files package.swift - Which libraries are needed configure.swift - Configure services Models folder - Define our data models Add Package Information import PackageDescription Download this let package = Package( name: "URLWatcher", dependencies: [ // � A server-side Swift web framework. .package(url: "https://github.com/vapor/vapor.git", from: "3.0.0"), // Load the driver for sqlite3 .package(url: "https://github.com/vapor/fluent-sqlite.git", from: "3.0.0"), // � An expressive, performant, and extensible templating language built for Swift. .package(url: "https://github.com/vapor/leaf.git", from: "3.0.0"), ], targets: [ .target(name: "App", dependencies: ["Leaf", "Vapor", "FluentSQLite"]), .target(name: "Run", dependencies: ["App"]), .testTarget(name: "AppTests", dependencies: ["App"]) ] ) Compile this Add a Class in Models import FluentSQLite import Vapor final class MonitoredEMail: SQLiteModel { var id: Int? var address: String init(id: Int? = nil, address: String) { self.id = id; self.address = address; } } extension MonitoredEMail: Content {} extension MonitoredEMail: Migration {} Add Configuration import Leaf import Vapor import FluentSQLite /// Called before your application initializes. public func configure(_ config: inout Config, _ env: inout Environment, _ services: inout Services) throws { : try services.register(FluentSQLiteProvider()) : var databases = DatabasesConfig() try databases.add(database: SQLiteDatabase(storage: .memory), as: .sqlite) services.register(databases) : var migrations = MigrationConfig() migrations.add(model: MonitoredEMail.self, database: .sqlite) services.register(migrations) } Change Package Information import PackageDescription let package = Package( name: "URLWatcher", dependencies: [ // � A server-side Swift web framework. .package(url: "https://github.com/vapor/vapor.git", from: "3.0.0"), // Load the driver for sqlite3 .package(url: "https://github.com/vapor/fluent-postgresql.git""https://github.com/vapor/fluent-sqlite.git", from:, from: "3.0.0" "1.0.0"), ) // � An expressive, performant, and extensible templating language built for Swift. .package(url: "https://github.com/vapor/leaf.git", from: "3.0.0"), ], targets: [ ..targettarget(name:(name: "App""App",, dependencies:dependencies: [["Leaf""Leaf",, "Vapor""Vapor",, "FluentPostgreSQL""FluentSQLite"]), ]), .target(name: "Run", dependencies: ["App"]), .testTarget(name: "AppTests", dependencies: ["App"]) ] ) Change the Class in Models import FluentSQLiteFluentPostgreSQL import Vapor final class MonitoredEMail: SQLiteModelPostgreSQLModel { { var id: Int? var address: String init(id: Int? = nil, address: String) { self.id = id; self.address = address; } } extension MonitoredEMail: Content {} extension MonitoredEMail: Migration {} Change Configuration import Leaf import Vapor import FluentSQLite /// Called before your application initializes. public func configure(_ config: inout Config, _ env: inout Environment, _ services: inout Services) throws { : trytry services.services.registerregister((FluentPostgreSQLProviderFluentSQLiteProvider()) ()) : var databases = DatabasesConfig() trylet databases.pgconfig =add PostgreSQLDatabaseConfig(database: SQLiteDatabase(hostname:(storage: "localhost" .memory), ,as: port: .sqlite 32768), username: "postgres", services. register (databases) database: "email", password: nil, transport: .cleartext) let postgres = PostgreSQLDatabase(config: pgconfig) databases.add(database: postgres, as: .psql) services.register(databases) : var migrations = MigrationConfig() migrations.migrations.addadd(model:(model: MonitoredEMailMonitoredEMail..selfself,, database:database: ..sqlitepsql) ) services.register(migrations) } Model Model View Controller View Add a View with Leaf Add a View Add Package Information (package.swift) import PackageDescription let package = Package( name: "WebHello", Download this dependencies: [ // � A server-side Swift web framework. .package(url: "https://github.com/vapor/vapor.git", from: "3.0.0"), // � An expressive, performant, and extensible templating language built for Swift. .package(url: "https://github.com/vapor/leaf.git", from: "3.0.0"), ], targets: [ .target(name: "App", dependencies: ["Leaf", "Vapor"]), .target(name: "Run", dependencies: ["App"]), .testTarget(name: "AppTests", dependencies: ["App"]) ] Compile this ) Add Configuration (configure.swift) import Leaf import Vapor /// Called before your application initializes. public func configure(_ config: inout Config, _ env: inout Environment, _ services: inout Services) throws { // Register providers first try services.register(LeafProvider()) : : : Add a .leaf File (email.leaf) #set("title") { Monitored Emails } #set("body") { <h1 class="mt-3"> Monitored E-Mail addresses </h1> <form method="POST" action="/emails"> <div class="input-group"> <input type="text" name="address" class="form-control"> <div class="input-group-append"> <button class="btn btn-outline-secondary" type="submit"> Create </button> </div> </div> </form> <table class="table"> <tr> <th scope="col">E-Mail Address</th> </tr> #for(email in addresses) { <tr><td>#(email.address)</td></tr> } </table> } #embed("base") Render the View (in our controller) import Vapor /// Register your application's routes here. public func routes(_ router: Router) throws { // "It works" page router.get("emails") { req -> Future<View> in let storedAddresses = MonitoredEMail.query(on: req).all() return storedAddresses.flatMap { addresses in let data = ["addresses": addresses] return try req.view().render("email", data) } } } Model View Controller Controller Add a Controller with Swift Add GET and POST Handlers (Email.swift) import Vapor final class EmailController { func getEMails(_ req: Request) throws -> Future<View> { let storedAddresses = MonitoredEMail.query(on: req).all() return storedAddresses.flatMap { addresses in let data = ["addresses": addresses] return try req.view().render("email", data) } } func addEMail(_ req: Request) throws -> Future<Response> { return try req.content.decode(MonitoredEMail.self).flatMap { url in return url.save(on: req).map { _ in return req.redirect(to: "/emails") } } } } Connect to Routes (Routes.swift) import Vapor /// Register your application's routes here. public func routes(_ router: Router) throws { // "It works" page router.get("emails") { req -> Future<View> in let storedAddresses = MonitoredEMail.query(on: req).all() return storedAddresses.flatMap { addresses in let data = ["addresses": addresses] return try req.view().render("email", data) } } } Connect to Routes (Routes.swift) import Vapor /// Register your application's routes here. public func routes(_ router: Router) throws { let emailController = EmailController() router.get("emails", use:emailController.getEMails) router.post("emails", use:emailController.addEMail) Closures I do not think it runs when you think it runs Quick Closure Review add1 = lambda a: a + 1 let add1 = a => a + 1 let add1 = {(a: Int) -> Int in return a + 1} let add1 = {a in return a + 1} let add1 = {$0 + 1} These are VARIABLES. Trailing Closures [1,2,3].reduce(0, {$0 + $1}) == [1,2,3].reduce(0){$0+$1} [1,2,3].map{$0 * $0} Spot the Closures import Vapor Here final class EmailController { func getEMails(_ req:

View Full Text

Details

  • File Type
    pdf
  • Upload Time
    -
  • Content Languages
    English
  • Upload User
    Anonymous/Not logged-in
  • File Pages
    81 Page
  • File Size
    -

Download

Channel Download Status
Express Download Enable

Copyright

We respect the copyrights and intellectual property rights of all users. All uploaded documents are either original works of the uploader or authorized works of the rightful owners.

  • Not to be reproduced or distributed without explicit permission.
  • Not used for commercial purposes outside of approved use cases.
  • Not used to infringe on the rights of the original creators.
  • If you believe any content infringes your copyright, please contact us immediately.

Support

For help with questions, suggestions, or problems, please contact us