Gatekeeper par Guillaume Faure

52
Gatekeeper @ Deezer Cocoheads Paris Guillaume Faure Mai 2016 Deezer 1

Transcript of Gatekeeper par Guillaume Faure

Page 1: Gatekeeper par Guillaume Faure

Gatekeeper @ DeezerCocoheads Paris

Guillaume FaureMai 2016

Deezer

1

Page 2: Gatekeeper par Guillaume Faure

Concept

Page 3: Gatekeeper par Guillaume Faure

Qu’est qu’un GateKeeper

Permet au cours de l’exécution de l’application charger/déchargerdes modules.

2

Page 4: Gatekeeper par Guillaume Faure

Pourquoi un GateKeeper

• Pas de rollout progressif (contrairement au Playstore)

• Temps de validation appstore variable• Phases de beta test trop courtes / sans assez d’utilisateurs

3

Page 5: Gatekeeper par Guillaume Faure

Pourquoi un GateKeeper

• Pas de rollout progressif (contrairement au Playstore)• Temps de validation appstore variable

• Phases de beta test trop courtes / sans assez d’utilisateurs

3

Page 6: Gatekeeper par Guillaume Faure

Pourquoi un GateKeeper

• Pas de rollout progressif (contrairement au Playstore)• Temps de validation appstore variable• Phases de beta test trop courtes / sans assez d’utilisateurs

3

Page 7: Gatekeeper par Guillaume Faure

Conditions d’activation

Le choix ou non de l’activation est faite coté serveur

• Employé

• Utilisateur en béta• Office• Pays• Pourcentage d’utilisateur• Offre• Platform• Formfactor

4

Page 8: Gatekeeper par Guillaume Faure

Conditions d’activation

Le choix ou non de l’activation est faite coté serveur

• Employé• Utilisateur en béta

• Office• Pays• Pourcentage d’utilisateur• Offre• Platform• Formfactor

4

Page 9: Gatekeeper par Guillaume Faure

Conditions d’activation

Le choix ou non de l’activation est faite coté serveur

• Employé• Utilisateur en béta• Office

• Pays• Pourcentage d’utilisateur• Offre• Platform• Formfactor

4

Page 10: Gatekeeper par Guillaume Faure

Conditions d’activation

Le choix ou non de l’activation est faite coté serveur

• Employé• Utilisateur en béta• Office• Pays

• Pourcentage d’utilisateur• Offre• Platform• Formfactor

4

Page 11: Gatekeeper par Guillaume Faure

Conditions d’activation

Le choix ou non de l’activation est faite coté serveur

• Employé• Utilisateur en béta• Office• Pays• Pourcentage d’utilisateur

• Offre• Platform• Formfactor

4

Page 12: Gatekeeper par Guillaume Faure

Conditions d’activation

Le choix ou non de l’activation est faite coté serveur

• Employé• Utilisateur en béta• Office• Pays• Pourcentage d’utilisateur• Offre

• Platform• Formfactor

4

Page 13: Gatekeeper par Guillaume Faure

Conditions d’activation

Le choix ou non de l’activation est faite coté serveur

• Employé• Utilisateur en béta• Office• Pays• Pourcentage d’utilisateur• Offre• Platform

• Formfactor

4

Page 14: Gatekeeper par Guillaume Faure

Conditions d’activation

Le choix ou non de l’activation est faite coté serveur

• Employé• Utilisateur en béta• Office• Pays• Pourcentage d’utilisateur• Offre• Platform• Formfactor

4

Page 15: Gatekeeper par Guillaume Faure

Quel utilité ?

Les modules sont des pans entiers de fonctionnalités ou decomportement de l’application.

• Refactoring• Fonctionnalités utilisateur• Test utilisateurs• ...

5

Page 16: Gatekeeper par Guillaume Faure

Quel utilité ?

Les modules sont des pans entiers de fonctionnalités ou decomportement de l’application.

• Refactoring

• Fonctionnalités utilisateur• Test utilisateurs• ...

5

Page 17: Gatekeeper par Guillaume Faure

Quel utilité ?

Les modules sont des pans entiers de fonctionnalités ou decomportement de l’application.

• Refactoring• Fonctionnalités utilisateur

• Test utilisateurs• ...

5

Page 18: Gatekeeper par Guillaume Faure

Quel utilité ?

Les modules sont des pans entiers de fonctionnalités ou decomportement de l’application.

• Refactoring• Fonctionnalités utilisateur• Test utilisateurs• ...

5

Page 19: Gatekeeper par Guillaume Faure

Modules

Page 20: Gatekeeper par Guillaume Faure

Principe d’un module de gate keep

• Ce n’est pas juste un BOOL

• Est une interface vers une fonctionalité• Les utilisateurs de cette fonctionalité appellent cette interface

6

Page 21: Gatekeeper par Guillaume Faure

Principe d’un module de gate keep

• Ce n’est pas juste un BOOL• Est une interface vers une fonctionalité

• Les utilisateurs de cette fonctionalité appellent cette interface

6

Page 22: Gatekeeper par Guillaume Faure

Principe d’un module de gate keep

• Ce n’est pas juste un BOOL• Est une interface vers une fonctionalité• Les utilisateurs de cette fonctionalité appellent cette interface

6

Page 23: Gatekeeper par Guillaume Faure

Deux formes de modules

• Module simple : Le module n’est chargé que lorsque le gatekeepcorrespondant est actif.

• Module double : Le module est composé de deuximplémentations, l’une lorsque le gatekeep correspondant estactif, l’autre quand il est inactif.

7

Page 24: Gatekeeper par Guillaume Faure

Deux formes de modules

• Module simple : Le module n’est chargé que lorsque le gatekeepcorrespondant est actif.

• Module double : Le module est composé de deuximplémentations, l’une lorsque le gatekeep correspondant estactif, l’autre quand il est inactif.

7

Page 25: Gatekeeper par Guillaume Faure

Module interface

1 @interface DZRGateKeeperModule : NSObject2 + (nullable id)activatedModule;3 + (nullable id)deactivatedModule;4

5 + (nullable NSString *)name;6 + (nullable instancetype)module;7

8 - (void)moduleLoad;9 - (void)moduleUnload;

10 @end

8

Page 26: Gatekeeper par Guillaume Faure

Proxy

Page 27: Gatekeeper par Guillaume Faure

Comment Garantir le déchargement des modules

• Il faut pouvoir charger/décharger les modules à n’ importe quelmoment de l’exécution de l’application.

• Le module, peut être appelé de n’ import où.• On ne controlle pas la politique d’ownership

ProxyUtilisation d’un NSProxy pour protéger les modules.

Le code extérieur, ne voit jamais que le proxy, jamais le moduleréel.

Seul le proxy retain l’ instance du module.

9

Page 28: Gatekeeper par Guillaume Faure

Comment Garantir le déchargement des modules

• Il faut pouvoir charger/décharger les modules à n’ importe quelmoment de l’exécution de l’application.

• Le module, peut être appelé de n’ import où.

• On ne controlle pas la politique d’ownership

ProxyUtilisation d’un NSProxy pour protéger les modules.

Le code extérieur, ne voit jamais que le proxy, jamais le moduleréel.

Seul le proxy retain l’ instance du module.

9

Page 29: Gatekeeper par Guillaume Faure

Comment Garantir le déchargement des modules

• Il faut pouvoir charger/décharger les modules à n’ importe quelmoment de l’exécution de l’application.

• Le module, peut être appelé de n’ import où.• On ne controlle pas la politique d’ownership

ProxyUtilisation d’un NSProxy pour protéger les modules.

Le code extérieur, ne voit jamais que le proxy, jamais le moduleréel.

Seul le proxy retain l’ instance du module.

9

Page 30: Gatekeeper par Guillaume Faure

Comment Garantir le déchargement des modules

• Il faut pouvoir charger/décharger les modules à n’ importe quelmoment de l’exécution de l’application.

• Le module, peut être appelé de n’ import où.• On ne controlle pas la politique d’ownership

ProxyUtilisation d’un NSProxy pour protéger les modules.

Le code extérieur, ne voit jamais que le proxy, jamais le moduleréel.

Seul le proxy retain l’ instance du module.

9

Page 31: Gatekeeper par Guillaume Faure

Fonctionnement du proxy

Le Proxy est géré par le mécanisme de GateKeeper.

L’ implementation du module renvoie son proxy lorsqu’on demandeson singleton.

1 + (instancetype)module2 {3 return [[DZRGateKeeper sharedGateKeeper] moduleWithName:[[self class]

name]];♦

↣4 }

Le proxy doit maintenant se faire passer pour une instance dumodule.

10

Page 32: Gatekeeper par Guillaume Faure

GateKeeper Proxy

Le proxy a besoin d’ information sur la classe qu’ il va usurper(notamment pour les modules simples).

1 @interface DZRGateKeeperModuleProxy : NSProxy2 - (id)initWithClass:(Class)moduleClass;3 @end

11

Page 33: Gatekeeper par Guillaume Faure

Déguisons le proxy

Il va ensuite forwarder tous les messages.

1 - (id)forwardingTargetForSelector:(SEL)aSelector2 {3 return self.module ?: (id)self;4 }

Deux possibilités

Fast path

• Première partie de laconditionnelle

• Exécuté quand le proxy disposed’un module

• On donne le module commenouvelle target du message

Slow path

• Seconde partie de laconditionnelle

• Exécuté quand le proxy disposed’aucune instance de module

• Le proxy va devoir répondre aumessage lui-même

12

Page 34: Gatekeeper par Guillaume Faure

Forwarding: Slow path

1 - (void)forwardInvocation:(NSInvocation *)invocation2 {3 }

13

Page 35: Gatekeeper par Guillaume Faure

Forwarding: Slow path

Pour que le runtime puisse créer son NSInvocation, il a besoin dela signature de la méthode.

On la demande gentiment à la classe du module que l’on est entrain d’usurper.

1 - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel2 {3 Method m = class_getInstanceMethod(_moduleClass, sel);4 if (m != nil) {5 const char *typeEncoging = method_getTypeEncoding(m);6 return [NSMethodSignature signatureWithObjCTypes:typeEncoging];7 }8 else {9 return nil;

10 }11 }

14

Page 36: Gatekeeper par Guillaume Faure

Comment mentir ?

Si l’on veux juste créer un module simple mais que l’on a besoinqu’ il réponde autre chose que 0 ou nil, comment peut-on faire?

15

Page 37: Gatekeeper par Guillaume Faure

1 @interface DZRGateKeeperModuleProxy : NSProxy2 - (id)initWithClass:(Class)moduleClass;3

4 /** The lying machine **/5 - (void)forwardSelector:(SEL)sel returningDefaultObject:(NSObject

*)object;♦

↣6 - (void)forwardSelector:(SEL)sel returningDefaultBOOL:(BOOL)boolean;7 - (void)forwardSelector:(SEL)sel returningDefaultFloat:(float)f;8 - (void)forwardSelector:(SEL)sel returningDefaultInteger:(NSInteger)i;9 - (void)forwardSelector:(SEL)sel returningDefaultDouble:(double)d;

10 @end

16

Page 38: Gatekeeper par Guillaume Faure

Implementation des stubs

1 - (void)forwardSelector:(SEL)sel returningDefaultObject:(NSObject*)object

♦↣

2 {3 _stubs[NSStringFromSelector(sel)] = object;4 }5

6 - (void)forwardSelector:(SEL)sel returningDefaultBOOL:(BOOL)boolean7 {8 _stubs[NSStringFromSelector(sel)] = @(boolean);9 }

17

Page 39: Gatekeeper par Guillaume Faure

Améliorons le forwardInfovation:

1 - (void)forwardInvocation:(NSInvocation *)invocation2 {3 id v;4 if ((v = [_stubs

objectForKey:NSStringFromSelector(invocation.selector)])) {♦

↣5 [invocation dzr_setReturnValue:v];6 }7 }

18

Page 40: Gatekeeper par Guillaume Faure

GateKeeper

Page 41: Gatekeeper par Guillaume Faure

Le chef d’orchestre

• Encore un singleton...• Les modules s’enregistrent auprès de lui• L’application l’appelle à des moments clef pourcharger/décharger des modules

19

Page 42: Gatekeeper par Guillaume Faure

Enregistrement d’un module

1 - (id)registerModule:(Class)module2 {3 DZRGateKeeperModule * moduleInstance = nil;4

5 if ([self checkDZRGateKeeperModule:module]) {6 NSString *name = [module name];7 DZRGateKeeperModuleProxy *proxy = [[DZRGateKeeperModuleProxy

alloc] initWithClass:module];♦

↣8 DZRGateKeeperModule * deactivatedInstance = moduleInstance =

[module deactivatedModule];♦

↣9 [deactivatedInstance moduleLoad];

10 proxy.module = deactivatedInstance;11 (self.modules)[name] = proxy;12 (self.registeredModules)[name] = module;13 }14 else {15 [NSException16 raise:NSGenericException format:@"You can only register class

deriving DZRGateKeeperModule"];♦

↣17 }18 return moduleInstance;19 }

20

Page 43: Gatekeeper par Guillaume Faure

Charger un module

1 - (DZRGateKeeperModule*)activateModuleWithName:(NSString *)moduleName2 {3 /** Check registration **/4

5 Class module = self.registeredModules[moduleName];6 DZRGateKeeperModuleProxy *proxy = self.modules[moduleName];7 DZRGateKeeperModule *moduleInstance = [module activatedModule];8

9 if (moduleInstance) {10 /** swap modules instances **/11 }12

13 return moduleInstance;14 }

21

Page 44: Gatekeeper par Guillaume Faure

Check registration

1 if (self.registeredModules[moduleName] == nil) {2 [NSException raise:NSGenericException3 format:@"The %@ module was not registered to the Gate

Keeper system",♦

↣4 moduleName];5 }

22

Page 45: Gatekeeper par Guillaume Faure

Swap modules instance

1 [proxy.module moduleUnload];2 [moduleInstance moduleLoad];3 proxy.module = moduleInstance;4 proxy.activated = YES;5 [[NSNotificationCenter defaultCenter]6 postNotificationName:DZRGateKeeperModuleLoadedNotification7 object:self8 userInfo:@{DZRGateKeeperUserInfoModuleInstance: proxy,9 DZRGateKeeperUserInfoModuleName: moduleName}];

23

Page 46: Gatekeeper par Guillaume Faure

Appliquer la configuration

1 - (void)applyConfiguration2 {3 /** Gathering necessary information **/4 /** Compute the set of module ids to desactivate **/5 /** Compute the set of module ids to activate **/6 [toDesactivate enumerateObjectsUsingBlock:^(NSString *module,

BOOL *stop) {♦

↣7 [self desactivateModuleWithName:module];8 }];9 [toActivate enumerateObjectsUsingBlock:^(NSString *module, BOOL

*stop) {♦

↣10 [self activateModuleWithName:module];11 }];12 }

24

Page 47: Gatekeeper par Guillaume Faure

Gathering necessary information

1 NSSet *activated = self.activatedModules;2 NSSet *configured = self.persistedConfiguration;3 NSSet *configuredAndForced = [configured

setByAddingObjectsFromSet:[DZRGateKeeper activationForced]];♦

↣4 NSSet *registered = [NSSet setWithArray:self.registeredModules.allKeys];

25

Page 48: Gatekeeper par Guillaume Faure

Compute the set of module ids to desactivate

1 NSMutableSet *toDesactivate = [activated mutableCopy];2 [toDesactivate minusSet:configuredAndForced];3 [toDesactivate intersectSet:registered];

26

Page 49: Gatekeeper par Guillaume Faure

Compute the set of module ids to activate

1 NSMutableSet *toActivate = [configuredAndForced mutableCopy];2 [toActivate minusSet:activated];3 [toActivate intersectSet:registered];

27

Page 50: Gatekeeper par Guillaume Faure

Conclusion

Page 51: Gatekeeper par Guillaume Faure

En résumé

Pro

• Rollout progressif et maitrisé

• Desactivation de fonctionalitéinstable

• Partagé avec les autresplateformes

Cons

• Impose une architecture clientpour les modules

• Mutiplication des configurations

• Difficile de supprimer un module(fonctionalité devenuepermanante)

28

Page 52: Gatekeeper par Guillaume Faure

Questions ?

28