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

Server-Side Swift @ Devoxx FR

Server-Side Swift @ Devoxx FR

Simone Civetta

April 07, 2017
Tweet

More Decks by Simone Civetta

Other Decks in Programming

Transcript

  1. 6

  2. 8

  3. 9

  4. 10

  5. 11

  6. 12

  7. 13

  8. Plan 1. Les Origines 2. État de l'art 3. Pourquoi

    l'utiliser ? 4. Swift Server dans la vraie vie 5. Évolutions 15
  9. 17

  10. 18

  11. 20

  12. 21

  13. 22

  14. 23

  15. 24

  16. 39

  17. 40

  18. 41

  19. 42

  20. 43

  21. 46

  22. 47

  23. 48

  24. 51

  25. 52

  26. 53

  27. 54

  28. 55

  29. 59

  30. 60

  31. 61

  32. 64

  33. 65

  34. 66

  35. 67

  36. 69

  37. 70

  38. 76

  39. 77

  40. 78

  41. 79

  42. 80

  43. 81

  44. 89

  45. 90

  46. Package.swi! import PackageDescription let package = Package( name: "XebiaTV", dependencies:

    [ .Package(url: "https://github.com/vapor/vapor.git", majorVersion: 1, minor: 5), .Package(url: "https://github.com/vapor/redis-provider.git", majorVersion: 1) ] ) 92
  47. Router import Foundation import Vapor let drop = Droplet() let

    categoryController = CategoryController() let youtubeController = YouTubeController() drop.get("/categories", handler: categoryController.categories) drop.get("/playlistItems", handler: youtubeController.playlistItems) drop.get("/search", handler: youtubeController.search) drop.get("/live", handler: youtubeController.live) drop.get("/video", String.self, handler: youtubeController.video) drop.run() 93
  48. Fichiers statiques import Foundation import Vapor import Core import HTTP

    final class CategoryController { private let dataLoader = DataFile() func categories(_ req: Request) throws -> ResponseRepresentable { let fileBody = try dataLoader.load(path: drop.workDir + "Data/categories.json") return Response(body: .data(fileBody)) } } 94
  49. Proxy YouTube public final class YouTubeController { // ... func

    search(_ req: Request) throws -> ResponseRepresentable { let query = "\(googleApisBaseUrl)/search?key=\(apiKey)&channelId=\(channelId)" return try drop.client.get(query) } } 95
  50. // ... public func video(_ req: Request, videoId: String) throws

    -> ResponseRepresentable { let cacheKey = "video-\(videoId)" guard let cached = try cacheService.load(for: cacheKey) else { let urls = try videoUrls(for: videoId) try cacheService.save(node: urls, with: cacheKey, expiration: cacheExpiration) return try JSON(node: urls) } return try JSON(node: cached) } private func videoUrls(for videoId: String) throws -> Node { guard let urls = try LiveStreamerReader.read(videoId: videoId) else { throw Error.noVideo } return Node(["urls": Node.array(urls)]) } 96
  51. Intégration avec LiveStreamer static func launch(_ command: String, args: [String]

    = []) throws -> Data { #if os(Linux) let task = Task() #else let task = Process() #endif let pipe = Pipe() task.launchPath = command task.arguments = args task.standardOutput = pipe task.launch() let data = pipe.fileHandleForReading.readDataToEndOfFile() task.waitUntilExit() return data } 97
  52. Cache avec Redis public protocol CacheService { func load(for key:

    String) throws -> Node? func save(node: Node, with key: String, expiration: TimeInterval) throws } public class RedisService : CacheService { private let drop: Droplet public init(drop: Droplet) throws { try drop.addProvider(VaporRedis.Provider(config: drop.config)) self.drop = drop } public func load(for key: String) throws -> Node? { return try drop.cache.get(key) } public func save(node: Node, with key: String, expiration: TimeInterval) throws { try drop.cache.set(key, node) if let redisCache = drop.cache as? RedisCache { try redisCache.redbird.command("EXPIRE", params: [key, "\(Int(expiration))"]) } } } 98
  53. Build $ swift build Compile CLibreSSL xts128.c Compile CLibreSSL x_x509a.c

    Compile CLibreSSL xcbc_enc.c Compile CLibreSSL x_spki.c .... Compile Swift Module 'libc' (1 sources) Compile Swift Module 'SocksCore' (15 sources) Compile Swift Module 'Jay' (21 sources) Linking CLibreSSL Compile Swift Module 'Core' (28 sources) Compile Swift Module 'Routing' (11 sources) Compile Swift Module 'HTTPRouting' (5 sources) Compile Swift Module 'TypeSafeRouting' (3 sources) Compile Swift Module 'Cache' (3 sources) Compile Swift Module 'Auth' (15 sources) Compile Swift Module 'Vapor' (92 sources) Compile Swift Module 'VaporRedis' (2 sources) Compile Swift Module 'App' (6 sources) 99
  54. Tests class AppTests: XCTestCase { // ... Create a StubCategoryController

    func testCategoryRoute() throws { let drop = try makeTestDroplet(categoryController: StubCategoryController()) let request = try Request(method: .get, uri: "/categories") let response = try drop.respond(to: request) let jsonBody = try JSON(bytes: response.body.bytes!) XCTAssertEqual(jsonBody["categories"]?.string, "someCategory") } } 100
  55. 101

  56. 104

  57. 105

  58. Tester c'est linker (?) Undefined symbols for architecture x86_64: "test2.test2.init

    () -> test2.test2", referenced from: test2Tests.test2Tests.(testExample () -> ()). (implicit closure #1) in test2Tests.o ld: symbol(s) not found for architecture x86_64 clang: error: linker command failed with exit code 1 109
  59. 113

  60. 114

  61. 123

  62. 125