SlideShare une entreprise Scribd logo
1  sur  25
Télécharger pour lire hors ligne
Troubles start at
version 1
Laurent Cerveau
lcerveau@nephorider.com
Before version 1.0
“You’ll never gonna die you gonna make it
if you try they gonna love you”
Focus is naturally on this first version, get
feature working and debugged
After version 1.0
“And in the end the love you take is equal
to the love you make”
Some future changes may break what is
already exist and _really_ annoy users
So the topic is...
• I am shipping a new version of a client , and its
internal have changed.
• I am deploying new server code I do not want to
have application crash or behave weirdly
• I need to ship a new client... in preparation for
future server changes
• I would like that users upgrade to a new version
• I need to force users to upgrade to the latest
version
Make an often heard question more useful
“Which version is it?”
Configuration
Code snippets and practice advices to manage
evolution of versions on client and server
Client Native
application
Server
API, push notification
1.0 1.0.1 1.1 2.0
v1 v2 v3 v4
2 main topics
• How to overuse versioning capabilities
• Structure tips for safe client server exchange
No complicated stuff, many little things to ease
future developments
It’s all about versioning
Apple system
•Avoid going out of those conventions (1.2b)
CFBundleVersion “Int” e.g. 34 Development
CFBundleShortVersion
String
“String” e.g.1.2.3 Marketing
•Apple environment provides 2 versions
• Use agvtool to modify them (in a build system)
agvtool bump -all
agvtool new-marketing-version “1.0.1”
(For each build that is distributed)
• Here, each component stays below 10
In Code
• Set up the XCode project
•Translate marketing version easily in integer for
easy manipulation
int MMUtilityConvertMarketingVersionTo3Digit(NSString *version)
{
NSMutableArray *componentArray = [NSMutableArray arrayWithArray:[version componentsSeparatedByString:@"."]];
for(NSUInteger idx = [componentArray count]; idx < 3; idx++) {
[componentArray addObject:@0];
}
__block int result = 0;
[componentArray enumerateObjectsUsingBlock:^(NSString *aComponent, NSUInteger idx, BOOL *stop) {
result = 10*result+[aComponent intValue];
}];
return result;
}
Centralized management
MMEnvironmentMonitor
singleton or on the application delegate, created early
@property @methods
firstTime, firstTimeForCurrentVersion,
previousVersion
applicationVariant
developmentStage
Tutorial of the app, present new features
When you have a lite and a full version
alpha, beta, final
-(void) runUpgradeScenario
-(void) detectBoundariesVersions(EndBlock)
-(BOOL) testRunability
Set up defaults, upgrade internal data structure
Time limit for beta, warn if wrong OS version
Read those parameters from the server
Actions
• Detect first launch(es)
_current3DigitVersion = MMUtilityConvertMarketingVersionTo3Digit([[[NSBundle mainBundle] infoDictionary]
objectForKey:@"CFBundleShortVersionString"]]);
id tmpObj = [[NSUserDefaults standardUserDefaults] objectForKey:kNEPVersionRunDefaultsKey];
_firstTime = (nil == tmpObj);
_firstTimeForCurrentVersion = (nil == [tmpObj objectForKey:[NSString
stringWithFormat:@"%d",_current3DigitVersion]]);
if(_firstTimeForCurrentVersion) {
_previous3DigitVersion = [[[tmpObj keysSortedByValueUsingSelector:@selector(compare:)] lastObject] intValue];
[[NSUserDefaults standardUserDefaults] setObject:@{[NSString
stringWithFormat:@"%d",_current3DigitVersion]:@YES} forKey:kNEPVersionRunDefaultsKey];
[[NSUserDefaults standardUserDefaults] synchronize];
} else {
_previous3DigitVersion = _current3DigitVersion;
}
• Run Upgrade scenarios : convention naming
/* Have all start and upgrade method named with the same scheme */
- (void) _startAt100;
- (void) _startAt101;
- (void) _upgradeFrom100To101;
- (void) _upgradeFrom102To110;
/* Use the Objective-C runtime */
- (BOOL) runUpgradeScenario
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
__block BOOL result = NO;
if(NO == self.firstTimeForCurrentVersion && NO == self.firstTime)
return result;
}
• Run Upgrade scenarios : apply the upgrade or start
NSMutableDictionary *allUpgrades= [NSMutableDictionary dictionary];
NSMutableDictionary *allStarts= [NSMutableDictionary dictionary];
//Find all upgrade methods
unsigned int outCount;
Method * allMethods = class_copyMethodList([self class], &outCount);
for(unsigned int idx = 0; idx < outCount; idx++) {
Method aMethod = allMethods[idx];
NSString *aMethodName = NSStringFromSelector(method_getName(aMethod));
if([aMethodName hasPrefix:@"_upgradeFrom"]) {
NSString *upgradeVersionString = [aMethodName substringWithRange:NSMakeRange([@"_upgradeFrom" length], 3)];
[allUpgrades setObject:aMethodName forKey:upgradeVersionString];
} else if ([aMethodName hasPrefix:@"_startAt"]) {
NSString *startVersionString = [aMethodName substringWithRange:NSMakeRange([@"_startAt" length], 3)];
[allStarts setObject:aMethodName forKey:startVersionString];
}
}
if(allMethods) free(allMethods);
if(self.firstTime) {
//sort them and perform the most "recent" one
SEL startSelector = NSSelectorFromString([allStarts[[[allStarts keysSortedByValueUsingSelector:@selector(compare:)]lastObject]]]);
[self performSelector:startSelector withObject:nil];
result = YES;
} else if(self.firstTimeForCurrentVersion) {
//Sort them and apply the one that needs to be applied
[[allUpgrades keysSortedByValueUsingSelector:@selector(compare:)] enumerateObjectsUsingBlock:^(NSString *obj, NSUInteger idx, BOOL
*stop) {
if([obj intValue] > _previous3DigitVersion) {
result = YES;
[self performSelector:NSSelectorFromString([allUpgrades objectForKey:obj]) withObject:nil];
}
}];
}
#pragma clang diagnostic pop
return result;
Runability
• Beta lock : generate a .m file with limit date at
each build. Put it in a Run script phase
tmp_path = os.path.join(os.getcwd(),'Sources/frontend/NEPDevelopmentStage.m')
tmp_now = time.strftime('%Y-%m-%d %X +0000', time.localtime(time.time()
+24*3600*20))
f.write('NSString *const kMMLimitTimeForNonFinalVersion =@"')
f.write(tmp_now)
f.write('";')
f.close()
• Be nice and say goodbye
Server can help
• Have a server call returns information
{
last_version_in_apple_store:”1.2.3”,
minimal_client_version:”1.0.1”
}
Run limited
2 Small server/client tips
• It is always good to deal with HTTP 503
(service unavailable) instead of nothing
happening. Useful for big (or non mastered)
changes
• Client limitation can be done with extra HTTP header
or user agent change (be cautious) and send back 403
/* Change user agent */
userAgent = [NSString stringWithFormat:@"MyApp/%@ iOS CFNetwork/%@ Darwin/%s", [[[NSBundle mainBundle]
infoDictionary] objectForKey:@"CFBundleShortVersionString"], cfNetworkVersion, kernelVersion];
[request setValue:userAgent forHTTPHeaderField:@"User-Agent"];
Data structure
•Whatever I send you, you should not
crash on data (Obj-C nil insertion,
receiving HTML instead of JSON...)
•The user is not aware of what is not
visible - spread the changes
•The user may forgive missing data (if
she/he has been prepared)
•The user should always find back its
environment
Rules of thumb
Consequences
Self-describing uuid
{
__class_name:person,
uuid:person_123456,
firstname:laurent,
lastname:cerveau,
age:...
}
{
__class_name:person,
uuid:person_123456,
data_version:1,
object_version:2
firstname:laurent...
}
or
The topic of uuid
• use directly “id” of DB table
unique only in one table, dangerous in case of DB
technology change
• use full uuidgen
“B238BC15-DF27-4538-9FDA-2F972FE24B59”
• use something helpful in debugging
“video_12345”
• use something with a meaning (FQDN)
“video.tv_episode.12345”
NB: server may forget UUID in case of non persistent data
Base object
@interface MMBaseObject : NSObject
{
NSString *_uuid;
int _objectVersion;
int _dataVersion;
MMObjectType _type;
}
• Every object created with server data derives from
such
• Parsing of data instantiates all objects, ...if
understood
• Each object is responsible to be defensive in its
parsing
• Base object class methods allows creation of a
factory
• Use of a enum-based type can be convenient
Step 1 Registration
• Each subclass register itself at load time
/* Register towards to the base class */
+ (void)load
{
[MMBaseObject registerClass:NSStringFromClass([self class]) forType:kMMObjectTypePerson
JSONClassName:@"person"];
}
/* Class registration: to be called by subclasses */
+ (void) registerClass:(NSString *)className forType:(MMObjectType)type JSONClassName:(NSString *)jsonClassName
{
if(nil == _allObjectClasses) _allObjectClasses = [[NSMutableDictionary alloc] init];
if(nil == _jsonClassToObjectClass) _jsonClassToObjectClass = [[NSMutableDictionary alloc] init];
@autoreleasepool {
[_allObjectClasses setObject:[NSNumber numberWithUnsignedInteger:type] forKey:className];
[_jsonClassToObjectClass setObject:className forKey:jsonClassName];
}
}
• Registration maintains the mapping
• Class method on the Base class allows to
retrieve class from JSON class name and so on...
Step 2 Parsing
• Starts on the base class
/* Entry point for JSON parsing and MMObject instantiations */
+ (void) createMMObjectsFromJSONResult:(id)jsonResult
{
_ParseAPIObjectWithExecutionBlock(jsonResult);
return ;
}
/* Transform a Cocoa object JSON inspired representation into a real object */
void _ParseAPIObjectWithExecutionBlock(id inputObj) {
if([inputObj isKindOfClass:[NSArray class]]) {
[inputObj enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
_ParseAPIObjectWithExecutionBlock(obj);
}];
} else if([inputObj isKindOfClass:[NSDictionary class]]) {
NSDictionary *tmpDictionary = (NSDictionary *)inputObj;
NSString *objectAPIType = tmpDictionary[@"__class_name"];
NSString *objectUUID = tmpDictionary[@"uuid"] ;
if(objectUUID) {
MMBaseObject *tmpObject = [_dataStore objectWithUUID :objectUUID];
if(tmpObject) {
[tmpObject updateWithJSONContent:tmpDictionary];
} else {
if(nil == objectAPIType) return;
NSString *objectClass = [BOXBaseObject classNameForStringAPIType:objectAPIType];
if(nil == objectClass) return result;
tmpObject = [[NSClassFromString(objectClass) alloc] initWithJSONContent:tmpDictionary];
[_dataStore addObject:tmpObject replace:NO];
}
[tmpDictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
if([obj isKindOfClass:[NSArray class]] || [obj isKindOfClass:[NSDictionary class]]) {
_ParseAPIObjectWithExecutionBlock(obj,provider, task, block, objectUUID, key);
}
}];
}
}
}
•And inside calls a recursive function
Step 3 Object creation
• Base class does the basics
/* Designated initializer. Possible Variation:if uuid is nil one can be generated */
- (id)initWithUUID:(NSString *)uuid
{
self = [super init];
if(self) {
NSString *tmpClassString = NSStringFromClass([self class]);
self.uuid = uuid;
self.type = [_allObjectClasses[tmpClassString] unsignedIntegerValue];
}
return self;
}
/* JSON object initialization : first time */
- (id)initWithJSONContent:(NSDictionary *)contentObject
{
self = [super initWithUUID:contentObject[@"uuid"]];
! if (self != nil) {
! ! [self updateWithJSONContent:contentObject];
}
! return self;
}
•And subclass are defensive
/* JSON update */
- (void)updateWithJSONContent:(NSDictionary *)contentObject
{
if([contentObject[@”object_version”] intValue] > _objectVersion) {
id tmpObj = JSONContent[@"title"];
if(tmpObj && [tmpObj isKindOfClass:[NSString class]]) {
self.title = tmpObj;
}
}
}
Down the road
• Deal with a field based API (incomplete download)
• Deal with sliced data
• Gather all objects created in a HTTP call
• Handle relationship between objects
•Apply in an external framework (e.g. RestKit...)
ThankYou!

Contenu connexe

Tendances

Web Services with Objective-C
Web Services with Objective-CWeb Services with Objective-C
Web Services with Objective-CJuio Barros
 
Create a Core Data Observer in 10mins
Create a Core Data Observer in 10minsCreate a Core Data Observer in 10mins
Create a Core Data Observer in 10minszmcartor
 
Write code that writes code!
Write code that writes code!Write code that writes code!
Write code that writes code!Jason Feinstein
 
Servlet and JSP
Servlet and JSPServlet and JSP
Servlet and JSPGary Yeh
 
Concurrency Programming in Java - 07 - High-level Concurrency objects, Lock O...
Concurrency Programming in Java - 07 - High-level Concurrency objects, Lock O...Concurrency Programming in Java - 07 - High-level Concurrency objects, Lock O...
Concurrency Programming in Java - 07 - High-level Concurrency objects, Lock O...Sachintha Gunasena
 
Simpler Core Data with RubyMotion
Simpler Core Data with RubyMotionSimpler Core Data with RubyMotion
Simpler Core Data with RubyMotionStefan Haflidason
 
Servlet sessions
Servlet sessionsServlet sessions
Servlet sessionsvantinhkhuc
 
Multi-tenancy with Rails
Multi-tenancy with RailsMulti-tenancy with Rails
Multi-tenancy with RailsPaul Gallagher
 
In-Depth Model/View with QML
In-Depth Model/View with QMLIn-Depth Model/View with QML
In-Depth Model/View with QMLICS
 
Mac/iOS Design Patterns
Mac/iOS Design PatternsMac/iOS Design Patterns
Mac/iOS Design PatternsRobert Brown
 
OmniBase Object Database
OmniBase Object DatabaseOmniBase Object Database
OmniBase Object DatabaseESUG
 
DDD, CQRS and testing with ASP.Net MVC
DDD, CQRS and testing with ASP.Net MVCDDD, CQRS and testing with ASP.Net MVC
DDD, CQRS and testing with ASP.Net MVCAndy Butland
 
Java Concurrency, Memory Model, and Trends
Java Concurrency, Memory Model, and TrendsJava Concurrency, Memory Model, and Trends
Java Concurrency, Memory Model, and TrendsCarol McDonald
 
iPhone project - Wireless networks seminar
iPhone project - Wireless networks seminariPhone project - Wireless networks seminar
iPhone project - Wireless networks seminarSilvio Daminato
 
Node.js Enterprise Middleware
Node.js Enterprise MiddlewareNode.js Enterprise Middleware
Node.js Enterprise MiddlewareBehrad Zari
 
Introduction to CQRS - command and query responsibility segregation
Introduction to CQRS - command and query responsibility segregationIntroduction to CQRS - command and query responsibility segregation
Introduction to CQRS - command and query responsibility segregationAndrew Siemer
 
The Future of the Web
The Future of the WebThe Future of the Web
The Future of the WebRay Nicholus
 
Practicing Continuous Deployment
Practicing Continuous DeploymentPracticing Continuous Deployment
Practicing Continuous Deploymentzeeg
 

Tendances (20)

Web Services with Objective-C
Web Services with Objective-CWeb Services with Objective-C
Web Services with Objective-C
 
Create a Core Data Observer in 10mins
Create a Core Data Observer in 10minsCreate a Core Data Observer in 10mins
Create a Core Data Observer in 10mins
 
Write code that writes code!
Write code that writes code!Write code that writes code!
Write code that writes code!
 
Servlet and JSP
Servlet and JSPServlet and JSP
Servlet and JSP
 
Concurrency Programming in Java - 07 - High-level Concurrency objects, Lock O...
Concurrency Programming in Java - 07 - High-level Concurrency objects, Lock O...Concurrency Programming in Java - 07 - High-level Concurrency objects, Lock O...
Concurrency Programming in Java - 07 - High-level Concurrency objects, Lock O...
 
Simpler Core Data with RubyMotion
Simpler Core Data with RubyMotionSimpler Core Data with RubyMotion
Simpler Core Data with RubyMotion
 
Servlet sessions
Servlet sessionsServlet sessions
Servlet sessions
 
Multi-tenancy with Rails
Multi-tenancy with RailsMulti-tenancy with Rails
Multi-tenancy with Rails
 
In-Depth Model/View with QML
In-Depth Model/View with QMLIn-Depth Model/View with QML
In-Depth Model/View with QML
 
Igor Davydenko
Igor DavydenkoIgor Davydenko
Igor Davydenko
 
Bonjour, iCloud
Bonjour, iCloudBonjour, iCloud
Bonjour, iCloud
 
Mac/iOS Design Patterns
Mac/iOS Design PatternsMac/iOS Design Patterns
Mac/iOS Design Patterns
 
OmniBase Object Database
OmniBase Object DatabaseOmniBase Object Database
OmniBase Object Database
 
DDD, CQRS and testing with ASP.Net MVC
DDD, CQRS and testing with ASP.Net MVCDDD, CQRS and testing with ASP.Net MVC
DDD, CQRS and testing with ASP.Net MVC
 
Java Concurrency, Memory Model, and Trends
Java Concurrency, Memory Model, and TrendsJava Concurrency, Memory Model, and Trends
Java Concurrency, Memory Model, and Trends
 
iPhone project - Wireless networks seminar
iPhone project - Wireless networks seminariPhone project - Wireless networks seminar
iPhone project - Wireless networks seminar
 
Node.js Enterprise Middleware
Node.js Enterprise MiddlewareNode.js Enterprise Middleware
Node.js Enterprise Middleware
 
Introduction to CQRS - command and query responsibility segregation
Introduction to CQRS - command and query responsibility segregationIntroduction to CQRS - command and query responsibility segregation
Introduction to CQRS - command and query responsibility segregation
 
The Future of the Web
The Future of the WebThe Future of the Web
The Future of the Web
 
Practicing Continuous Deployment
Practicing Continuous DeploymentPracticing Continuous Deployment
Practicing Continuous Deployment
 

Similaire à Paris Tech Meetup talk : Troubles start at version 1.0

iOS 101 - Xcode, Objective-C, iOS APIs
iOS 101 - Xcode, Objective-C, iOS APIsiOS 101 - Xcode, Objective-C, iOS APIs
iOS 101 - Xcode, Objective-C, iOS APIsSubhransu Behera
 
Tech Talk: App Functionality (Android)
Tech Talk: App Functionality (Android)Tech Talk: App Functionality (Android)
Tech Talk: App Functionality (Android)Lifeparticle
 
High ROI Testing in Angular.pptx
High ROI Testing in Angular.pptxHigh ROI Testing in Angular.pptx
High ROI Testing in Angular.pptxChristian Lüdemann
 
Angular Unit Testing NDC Minn 2018
Angular Unit Testing NDC Minn 2018Angular Unit Testing NDC Minn 2018
Angular Unit Testing NDC Minn 2018Justin James
 
Annotation Processing
Annotation ProcessingAnnotation Processing
Annotation ProcessingJintin Lin
 
Junit in mule
Junit in muleJunit in mule
Junit in muleF K
 
Junit in mule demo
Junit in mule demo Junit in mule demo
Junit in mule demo javeed_mhd
 
The Ring programming language version 1.3 book - Part 7 of 88
The Ring programming language version 1.3 book - Part 7 of 88The Ring programming language version 1.3 book - Part 7 of 88
The Ring programming language version 1.3 book - Part 7 of 88Mahmoud Samir Fayed
 
Enjoy Munit with Mule
Enjoy Munit with MuleEnjoy Munit with Mule
Enjoy Munit with MuleBui Kiet
 
Intro To JavaScript Unit Testing - Ran Mizrahi
Intro To JavaScript Unit Testing - Ran MizrahiIntro To JavaScript Unit Testing - Ran Mizrahi
Intro To JavaScript Unit Testing - Ran MizrahiRan Mizrahi
 
Angular performance slides
Angular performance slidesAngular performance slides
Angular performance slidesDavid Barreto
 
303 TANSTAAFL: Using Open Source iPhone UI Code
303 TANSTAAFL: Using Open Source iPhone UI Code303 TANSTAAFL: Using Open Source iPhone UI Code
303 TANSTAAFL: Using Open Source iPhone UI Codejonmarimba
 

Similaire à Paris Tech Meetup talk : Troubles start at version 1.0 (20)

iOS 101 - Xcode, Objective-C, iOS APIs
iOS 101 - Xcode, Objective-C, iOS APIsiOS 101 - Xcode, Objective-C, iOS APIs
iOS 101 - Xcode, Objective-C, iOS APIs
 
Tech Talk: App Functionality (Android)
Tech Talk: App Functionality (Android)Tech Talk: App Functionality (Android)
Tech Talk: App Functionality (Android)
 
Backbone.js
Backbone.jsBackbone.js
Backbone.js
 
High ROI Testing in Angular.pptx
High ROI Testing in Angular.pptxHigh ROI Testing in Angular.pptx
High ROI Testing in Angular.pptx
 
Angular Unit Testing NDC Minn 2018
Angular Unit Testing NDC Minn 2018Angular Unit Testing NDC Minn 2018
Angular Unit Testing NDC Minn 2018
 
Annotation Processing
Annotation ProcessingAnnotation Processing
Annotation Processing
 
6. static keyword
6. static keyword6. static keyword
6. static keyword
 
OOP.pptx
OOP.pptxOOP.pptx
OOP.pptx
 
Junit in mule
Junit in muleJunit in mule
Junit in mule
 
Junit in mule
Junit in muleJunit in mule
Junit in mule
 
Junit in mule
Junit in muleJunit in mule
Junit in mule
 
Junit in mule demo
Junit in mule demo Junit in mule demo
Junit in mule demo
 
Introduction to node.js
Introduction to node.jsIntroduction to node.js
Introduction to node.js
 
The Ring programming language version 1.3 book - Part 7 of 88
The Ring programming language version 1.3 book - Part 7 of 88The Ring programming language version 1.3 book - Part 7 of 88
The Ring programming language version 1.3 book - Part 7 of 88
 
Enjoy Munit with Mule
Enjoy Munit with MuleEnjoy Munit with Mule
Enjoy Munit with Mule
 
Intro To JavaScript Unit Testing - Ran Mizrahi
Intro To JavaScript Unit Testing - Ran MizrahiIntro To JavaScript Unit Testing - Ran Mizrahi
Intro To JavaScript Unit Testing - Ran Mizrahi
 
Test driven development
Test driven developmentTest driven development
Test driven development
 
Angular performance slides
Angular performance slidesAngular performance slides
Angular performance slides
 
303 TANSTAAFL: Using Open Source iPhone UI Code
303 TANSTAAFL: Using Open Source iPhone UI Code303 TANSTAAFL: Using Open Source iPhone UI Code
303 TANSTAAFL: Using Open Source iPhone UI Code
 
JavaScript Refactoring
JavaScript RefactoringJavaScript Refactoring
JavaScript Refactoring
 

Dernier

08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking MenDelhi Call girls
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024The Digital Insurer
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)wesley chun
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUK Journal
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdfhans926745
 
A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024Results
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?Igalia
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfsudhanshuwaghmare1
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptxHampshireHUG
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationSafe Software
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Igalia
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024The Digital Insurer
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationMichael W. Hawkins
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking MenDelhi Call girls
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)Gabriella Davis
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Scriptwesley chun
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processorsdebabhi2
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking MenDelhi Call girls
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsMaria Levchenko
 

Dernier (20)

08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 

Paris Tech Meetup talk : Troubles start at version 1.0

  • 1. Troubles start at version 1 Laurent Cerveau lcerveau@nephorider.com
  • 2. Before version 1.0 “You’ll never gonna die you gonna make it if you try they gonna love you” Focus is naturally on this first version, get feature working and debugged
  • 3. After version 1.0 “And in the end the love you take is equal to the love you make” Some future changes may break what is already exist and _really_ annoy users
  • 4. So the topic is... • I am shipping a new version of a client , and its internal have changed. • I am deploying new server code I do not want to have application crash or behave weirdly • I need to ship a new client... in preparation for future server changes • I would like that users upgrade to a new version • I need to force users to upgrade to the latest version Make an often heard question more useful “Which version is it?”
  • 5. Configuration Code snippets and practice advices to manage evolution of versions on client and server Client Native application Server API, push notification 1.0 1.0.1 1.1 2.0 v1 v2 v3 v4
  • 6. 2 main topics • How to overuse versioning capabilities • Structure tips for safe client server exchange No complicated stuff, many little things to ease future developments
  • 7. It’s all about versioning
  • 8. Apple system •Avoid going out of those conventions (1.2b) CFBundleVersion “Int” e.g. 34 Development CFBundleShortVersion String “String” e.g.1.2.3 Marketing •Apple environment provides 2 versions • Use agvtool to modify them (in a build system) agvtool bump -all agvtool new-marketing-version “1.0.1” (For each build that is distributed) • Here, each component stays below 10
  • 9. In Code • Set up the XCode project •Translate marketing version easily in integer for easy manipulation int MMUtilityConvertMarketingVersionTo3Digit(NSString *version) { NSMutableArray *componentArray = [NSMutableArray arrayWithArray:[version componentsSeparatedByString:@"."]]; for(NSUInteger idx = [componentArray count]; idx < 3; idx++) { [componentArray addObject:@0]; } __block int result = 0; [componentArray enumerateObjectsUsingBlock:^(NSString *aComponent, NSUInteger idx, BOOL *stop) { result = 10*result+[aComponent intValue]; }]; return result; }
  • 10. Centralized management MMEnvironmentMonitor singleton or on the application delegate, created early @property @methods firstTime, firstTimeForCurrentVersion, previousVersion applicationVariant developmentStage Tutorial of the app, present new features When you have a lite and a full version alpha, beta, final -(void) runUpgradeScenario -(void) detectBoundariesVersions(EndBlock) -(BOOL) testRunability Set up defaults, upgrade internal data structure Time limit for beta, warn if wrong OS version Read those parameters from the server
  • 11. Actions • Detect first launch(es) _current3DigitVersion = MMUtilityConvertMarketingVersionTo3Digit([[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"]]); id tmpObj = [[NSUserDefaults standardUserDefaults] objectForKey:kNEPVersionRunDefaultsKey]; _firstTime = (nil == tmpObj); _firstTimeForCurrentVersion = (nil == [tmpObj objectForKey:[NSString stringWithFormat:@"%d",_current3DigitVersion]]); if(_firstTimeForCurrentVersion) { _previous3DigitVersion = [[[tmpObj keysSortedByValueUsingSelector:@selector(compare:)] lastObject] intValue]; [[NSUserDefaults standardUserDefaults] setObject:@{[NSString stringWithFormat:@"%d",_current3DigitVersion]:@YES} forKey:kNEPVersionRunDefaultsKey]; [[NSUserDefaults standardUserDefaults] synchronize]; } else { _previous3DigitVersion = _current3DigitVersion; } • Run Upgrade scenarios : convention naming /* Have all start and upgrade method named with the same scheme */ - (void) _startAt100; - (void) _startAt101; - (void) _upgradeFrom100To101; - (void) _upgradeFrom102To110;
  • 12. /* Use the Objective-C runtime */ - (BOOL) runUpgradeScenario { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" __block BOOL result = NO; if(NO == self.firstTimeForCurrentVersion && NO == self.firstTime) return result; } • Run Upgrade scenarios : apply the upgrade or start NSMutableDictionary *allUpgrades= [NSMutableDictionary dictionary]; NSMutableDictionary *allStarts= [NSMutableDictionary dictionary]; //Find all upgrade methods unsigned int outCount; Method * allMethods = class_copyMethodList([self class], &outCount); for(unsigned int idx = 0; idx < outCount; idx++) { Method aMethod = allMethods[idx]; NSString *aMethodName = NSStringFromSelector(method_getName(aMethod)); if([aMethodName hasPrefix:@"_upgradeFrom"]) { NSString *upgradeVersionString = [aMethodName substringWithRange:NSMakeRange([@"_upgradeFrom" length], 3)]; [allUpgrades setObject:aMethodName forKey:upgradeVersionString]; } else if ([aMethodName hasPrefix:@"_startAt"]) { NSString *startVersionString = [aMethodName substringWithRange:NSMakeRange([@"_startAt" length], 3)]; [allStarts setObject:aMethodName forKey:startVersionString]; } } if(allMethods) free(allMethods); if(self.firstTime) { //sort them and perform the most "recent" one SEL startSelector = NSSelectorFromString([allStarts[[[allStarts keysSortedByValueUsingSelector:@selector(compare:)]lastObject]]]); [self performSelector:startSelector withObject:nil]; result = YES; } else if(self.firstTimeForCurrentVersion) { //Sort them and apply the one that needs to be applied [[allUpgrades keysSortedByValueUsingSelector:@selector(compare:)] enumerateObjectsUsingBlock:^(NSString *obj, NSUInteger idx, BOOL *stop) { if([obj intValue] > _previous3DigitVersion) { result = YES; [self performSelector:NSSelectorFromString([allUpgrades objectForKey:obj]) withObject:nil]; } }]; } #pragma clang diagnostic pop return result;
  • 13. Runability • Beta lock : generate a .m file with limit date at each build. Put it in a Run script phase tmp_path = os.path.join(os.getcwd(),'Sources/frontend/NEPDevelopmentStage.m') tmp_now = time.strftime('%Y-%m-%d %X +0000', time.localtime(time.time() +24*3600*20)) f.write('NSString *const kMMLimitTimeForNonFinalVersion =@"') f.write(tmp_now) f.write('";') f.close() • Be nice and say goodbye
  • 14. Server can help • Have a server call returns information { last_version_in_apple_store:”1.2.3”, minimal_client_version:”1.0.1” } Run limited
  • 15. 2 Small server/client tips • It is always good to deal with HTTP 503 (service unavailable) instead of nothing happening. Useful for big (or non mastered) changes • Client limitation can be done with extra HTTP header or user agent change (be cautious) and send back 403 /* Change user agent */ userAgent = [NSString stringWithFormat:@"MyApp/%@ iOS CFNetwork/%@ Darwin/%s", [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"], cfNetworkVersion, kernelVersion]; [request setValue:userAgent forHTTPHeaderField:@"User-Agent"];
  • 17. •Whatever I send you, you should not crash on data (Obj-C nil insertion, receiving HTML instead of JSON...) •The user is not aware of what is not visible - spread the changes •The user may forgive missing data (if she/he has been prepared) •The user should always find back its environment Rules of thumb
  • 19. The topic of uuid • use directly “id” of DB table unique only in one table, dangerous in case of DB technology change • use full uuidgen “B238BC15-DF27-4538-9FDA-2F972FE24B59” • use something helpful in debugging “video_12345” • use something with a meaning (FQDN) “video.tv_episode.12345” NB: server may forget UUID in case of non persistent data
  • 20. Base object @interface MMBaseObject : NSObject { NSString *_uuid; int _objectVersion; int _dataVersion; MMObjectType _type; } • Every object created with server data derives from such • Parsing of data instantiates all objects, ...if understood • Each object is responsible to be defensive in its parsing • Base object class methods allows creation of a factory • Use of a enum-based type can be convenient
  • 21. Step 1 Registration • Each subclass register itself at load time /* Register towards to the base class */ + (void)load { [MMBaseObject registerClass:NSStringFromClass([self class]) forType:kMMObjectTypePerson JSONClassName:@"person"]; } /* Class registration: to be called by subclasses */ + (void) registerClass:(NSString *)className forType:(MMObjectType)type JSONClassName:(NSString *)jsonClassName { if(nil == _allObjectClasses) _allObjectClasses = [[NSMutableDictionary alloc] init]; if(nil == _jsonClassToObjectClass) _jsonClassToObjectClass = [[NSMutableDictionary alloc] init]; @autoreleasepool { [_allObjectClasses setObject:[NSNumber numberWithUnsignedInteger:type] forKey:className]; [_jsonClassToObjectClass setObject:className forKey:jsonClassName]; } } • Registration maintains the mapping • Class method on the Base class allows to retrieve class from JSON class name and so on...
  • 22. Step 2 Parsing • Starts on the base class /* Entry point for JSON parsing and MMObject instantiations */ + (void) createMMObjectsFromJSONResult:(id)jsonResult { _ParseAPIObjectWithExecutionBlock(jsonResult); return ; } /* Transform a Cocoa object JSON inspired representation into a real object */ void _ParseAPIObjectWithExecutionBlock(id inputObj) { if([inputObj isKindOfClass:[NSArray class]]) { [inputObj enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { _ParseAPIObjectWithExecutionBlock(obj); }]; } else if([inputObj isKindOfClass:[NSDictionary class]]) { NSDictionary *tmpDictionary = (NSDictionary *)inputObj; NSString *objectAPIType = tmpDictionary[@"__class_name"]; NSString *objectUUID = tmpDictionary[@"uuid"] ; if(objectUUID) { MMBaseObject *tmpObject = [_dataStore objectWithUUID :objectUUID]; if(tmpObject) { [tmpObject updateWithJSONContent:tmpDictionary]; } else { if(nil == objectAPIType) return; NSString *objectClass = [BOXBaseObject classNameForStringAPIType:objectAPIType]; if(nil == objectClass) return result; tmpObject = [[NSClassFromString(objectClass) alloc] initWithJSONContent:tmpDictionary]; [_dataStore addObject:tmpObject replace:NO]; } [tmpDictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { if([obj isKindOfClass:[NSArray class]] || [obj isKindOfClass:[NSDictionary class]]) { _ParseAPIObjectWithExecutionBlock(obj,provider, task, block, objectUUID, key); } }]; } } } •And inside calls a recursive function
  • 23. Step 3 Object creation • Base class does the basics /* Designated initializer. Possible Variation:if uuid is nil one can be generated */ - (id)initWithUUID:(NSString *)uuid { self = [super init]; if(self) { NSString *tmpClassString = NSStringFromClass([self class]); self.uuid = uuid; self.type = [_allObjectClasses[tmpClassString] unsignedIntegerValue]; } return self; } /* JSON object initialization : first time */ - (id)initWithJSONContent:(NSDictionary *)contentObject { self = [super initWithUUID:contentObject[@"uuid"]]; ! if (self != nil) { ! ! [self updateWithJSONContent:contentObject]; } ! return self; } •And subclass are defensive /* JSON update */ - (void)updateWithJSONContent:(NSDictionary *)contentObject { if([contentObject[@”object_version”] intValue] > _objectVersion) { id tmpObj = JSONContent[@"title"]; if(tmpObj && [tmpObj isKindOfClass:[NSString class]]) { self.title = tmpObj; } } }
  • 24. Down the road • Deal with a field based API (incomplete download) • Deal with sliced data • Gather all objects created in a HTTP call • Handle relationship between objects •Apply in an external framework (e.g. RestKit...)