Comment nous avons contourné les directives d'examen et lancé un serveur par téléphone

Salut, Habr. Je m'appelle Anton Loginov, je suis développeur iOS chez FINCH.

Récemment, nous avons été confrontés au problème de l'utilisation d'interfaces Web pour les jeux de hasard. Dans la prochaine mise à jour des lignes directrices de l' AppStore Review, des collègues de Cupertino ont de nouveau resserré les règles. Plus précisément, Apple peut désormais rediriger l'application si l'une des interfaces Web est classée comme un jeu d'argent réel.

Notre application à elle seule représente 90% des jeux de hasard et les 10% restants sont utilisés pour la publicité de ces jeux. Certains d'entre eux fonctionnent via webView, nous devions donc nous protéger de toute façon de la redirection.

Que pourrait-on faire:

  1. Sortez ces jeux de l'application principale.
    En d'autres termes, il suffit de reporter l'inévitable.
  2. Utilisez un conteneur pour les jeux qui peuvent être mis à jour sans douleur.
    Ça sonne bien → J'ai essayé de pénétrer → J'ai tué plusieurs jours pour étudier React et React Native → J'ai réalisé que «le ski ne marche pas» → ça ne sonne pas si bien.

    C'est une solution très coûteuse, car il y avait peu de temps, et les jeux devraient être réécrits à partir de zéro. Tout dépend du routage interne - il est entièrement lié à urlPathComponents
  3. Réalisez le jeu en natif.
    Long, cher et encore long. À l'avenir, ils devraient être soutenus de manière continue, mais nous n'avions pas de telles opportunités.
  4. Simulez le comportement d'un serveur qui donnerait un site local avec des jeux.
    Cela semble fou, mais c'est l'option que j'ai choisie. C'est rapide, car des modifications minimes de l'héritage des jeux sont nécessaires.
    Les inconvénients estimés comprennent: l'augmentation de la taille de l'assemblage en raison du site situé localement, l'augmentation de la charge sur l'appareil en démarrant le serveur.

Je n'ai trouvé aucun article sur Habré décrivant comment démarrer le serveur par téléphone. Ayant décidé que l'affaire est assez rare et intéressante, j'ai décidé d'en parler ici sur Habré.

La préparation


Alors que notre courageux frontman a essayé de réduire la taille des jeux de 16 fois (80 Mo -> 5 Mo) et a changé les chemins internes en relatif, j'ai décidé de la bibliothèque en sélectionnant GCDWebServer. Il s'agit d'un framework léger avec lequel vous pouvez élever un serveur HTTP en quelques lignes de code.

Après avoir choisi la bibliothèque, il y a eu de longues heures à étudier et à comprendre comment le serveur fonctionne sous le capot, ce qui se passe à quel moment, comment configurer le serveur afin qu'il ne gaspille pas les ressources système. Notre serveur a appris à intercepter les transitions, à les traiter et j'ai appris à travailler avec le serveur de l'autre côté des barricades.

Personnalisation


func initWebServer() { //  let webServer = GCDWebServer() //       , ,     GET/POST : webServer.addDefaultHandler( forMethod: HTTPMethod.get.rawValue, request: GCDWebServerDataRequest.self) { [weak self] request in return self?.handle(request: request) } } 

Commencer


En fait, nous prescrivons les paramètres pour démarrer notre serveur et exécuter:

 do { try webServer.start(options: [GCDWebServerOption_BindToLocalhost: true, GCDWebServerOption_Port: 8080]) } catch { assertionFailure(error.localizedDescription) webServer.start(withPort: 8080, bonjourName: "PROJECT_NAME Web Server") } 

Procurations


image

Le module communique en interne avec l'API, mais utilise sa propre baseURL pour cela. Dans notre cas, localhost. Par conséquent, il était nécessaire d'apprendre au serveur à déterminer les requêtes qui devaient aller à l'API et à modifier leur baseURL.

 // MARK: -     ,        Safari 

Sur la base de ce qui précède, il était nécessaire de configurer des gestionnaires pour des tâches spécifiques:

  • Donner le site. (Eh bien, tout est simple ici) ;
  • Donnez un peu de statique du Bundle. (Démonté l'url de la demande, changé baseUrl en bundleUrl, a donné le contenu (js / media) ;
  • Obtenez les dernières données. (URL démontée, baseUrl modifiée, demandée, renvoyée) ;
  • Envoyez de nouvelles données. (Et nous n'avons pas traité les requêtes POST, les avons vissées, configurées, envoyées) ;

Faisons-le:

 private func handle(request: GCDWebServerRequest) -> GCDWebServerResponse? { // 1)   if request.url.pathComponents.contains(Endpoint.game.rawValue) { guard let indexURL = bundle.url(forResource: "index", withExtension: "html") else { return sendError(.noHTML(nil)) } do { let data = try Data(contentsOf: indexURL) let htmlString = String(data: data, encoding: .utf8) ?? "" return GCDWebServerDataResponse(html: htmlString) } catch { return sendError(.noHTML(error)) } // 2)   (js etc) } else if request.url.pathComponents.contains(Endpoint.nstatic.rawValue) { guard let resoursePath = bundle.resourcePath else { return sendError(.noJS(nil)) } let relativePath = request.url.pathComponents.joined(separator: "/") let absolutePath = resoursePath + relativePath.dropFirst() let staticURL = URL(fileURLWithPath: absolutePath) do { let data = try Data(contentsOf: staticURL) return GCDWebServerDataResponse(data: data, contentType: ContentType.js.description) } catch { return sendError(.noJS(error)) } // 3)    API } else if request.url.pathComponents.contains(Endpoint.api.rawValue) { var proxyRequest = request //  url,  , ,     let output = URLSession.shared.synchronousDataTask(with: proxyRequest) //    ,      let response = GCDWebServerDataResponse(data: outputData, contentType: ContentType.url.description) //     return response } } 

Conclusion


C'était amusant et nerveux. Premièrement, je n'avais jamais rien fait de tel auparavant. Deuxièmement, jusqu'à récemment, nous ne comprenions pas comment nos idées affecteront la demande parent.

Il nous a fallu environ 32 heures pour l'implémenter: 8 pour optimiser la taille du site, 24 pour concevoir et écrire cette fonctionnalité.

En écrivant un article, je suis arrivé à la conclusion qu'une façon plus populaire d'utiliser cette technologie est de développer un natif sans attendre que le backend soit prêt.

Eh bien, pour résumer les avantages de l'approche choisie:

  • Gagnez du temps en backend en lui donnant un modèle de données
  • Possibilité de tester tout comportement de serveur
  • Pour passer de Mock à la vraie API, il suffit de désactiver des gestionnaires spécifiques

Merci de votre attention. Si vous avez eu une expérience similaire, parlez-nous-en dans les commentaires.

Source: https://habr.com/ru/post/fr471874/


All Articles