Apple a sorti en grande pompe Swift l'année dernière. Depuis, le projet draine une communauté active, avec des milliers de bibliothèques publiées chaque mois et la récente publication de son code source. Mais qu'en est il de son application au delà du simple POC, dans une perspective plus industrielle ? Soyons francs : il faut que votre prochaine application iOS soit développée en Swift. En nous basant sur deux projets grand public en production développés à partir du début 2015, en surmontant les obstacles dûs à la jeunesse du langage, en étudiant les embellissements apportés à notre base de code, nous sommes sûrs de pouvoir vous convaincre. Ne ratez pas le train et adoptez Swift en 2016 !
4. Swift : Nouvelles du front
#Xebia #DevoxxFR
Swift ou pas ?
Septembre 2014
Novembre 2014Juin 2014
Apple annonce Swift
!
Swift 1.0
!
Première application Swift
Xebia sur l’App Store
?
Février 2015
Nouvelles applications ?
6. Swift : Nouvelles du front
#Xebia #DevoxxFR
Qu’est-ce que Swift ?
Nouveau langage de développement
Code plus lisible et moins verbeux
Donc plus simple à maintenir !
7. Swift : Nouvelles du front
#Xebia #DevoxxFR
Qu’est-ce que Swift ?
Transition Objective-C → Swift aisée
Moins de tolérance sur les erreurs de
programmation
Rapidité de développement accrue
9. Swift : Nouvelles du front
#Xebia #DevoxxFR
Swift ou pas ?
Juin 2014
Apple annonce Swift
!
Septembre 2014
Swift 1.0
Novembre 2014
!
Première application Swift
Xebia sur l’App Store
?
Février 2015
Nouvelles applications !
10. Swift : Nouvelles du front
#Xebia #DevoxxFR
Swift ou pas ?
Octobre 2014
Swift 1.1
September 2015
Swift 2.0
Octobre 2014
Swift 2.1
April 2015
Swift 1.2
Juin 2014
Apple annonce Swift
!
Septembre 2014
Swift 1.0
Novembre 2014
!
Première application Swift
Xebia sur l’App Store
?
Février 2015
Nouvelles applications !
Mars 2016
Swift 2.2
12. Swift : Nouvelles du front
#Xebia #DevoxxFR
TOC
Nos cas d’usage
Flux d’activité
Création du projet et configuration
Produits et synchronisation
Langues
Commentaires
Testing
Suivre son application
Et l’Objective-C ?
18. Swift : Nouvelles du front
#Xebia #DevoxxFR
Photos et vidéos
Fluidité
Des milliers de posts
Flux d’activités
19. Swift : Nouvelles du front
#Xebia #DevoxxFRFlux d’activités
Struct
Ne contient que ce qui a été défini
Meilleures performances
Stockage de type valeur et non référence
Passage par copie
20. Swift : Nouvelles du front
#Xebia #DevoxxFRFlux d’activités
struct
Attachment
{
let
id:
Int
let
url:
String
let
type:
String
let
name:
String
let
objectId:
Int
let
mimeType:
String
let
objectType:
String
}
Flux d’activités
Exemple de Struct
21. Swift : Nouvelles du front
#Xebia #DevoxxFRFlux d’activités
Struct : Performances
Avec une méthode de shuffle sur 1.000.000 d’objets
Secondes
0
0,325
0,65
0,975
1,3
NSObject Struct
0,064
1,152
22. Swift : Nouvelles du front
#Xebia #DevoxxFRFlux d’activités
Struct VS Classes
Ne peut pas hériter d’objets
Pas de deinitializer
Référence unique d’une instance
23. Swift : Nouvelles du front
#Xebia #DevoxxFRFlux d’activités
JSON -> Struct
func
buildUser(jsonData:[String:
AnyObject])
-‐>
User?
{
if
let
userData
=
jsonData["user"]
as?
[String:
AnyObject]
{
return
User(id:
userData["id"]!,
firstname:
userData["firstname"]!,
lastname:
userData["lastname"]!)
}
return
nil
}
24. Swift : Nouvelles du front
#Xebia #DevoxxFRFlux d’activités
Unbox
Conversion automatique
Génère des structs
Performances accrues
25. Swift : Nouvelles du front
#Xebia #DevoxxFRFlux d’activités
Unbox par l’exemple
func
buildUser(userData:[String:
AnyObject])
-‐>
User?
{
let
user:
User
=
Unbox(userData)
...
return
user
}
26. Swift : Nouvelles du front
#Xebia #DevoxxFRFlux d’activités
Performances
Struct
Héritage Rendu
28. Swift : Nouvelles du front
#Xebia #DevoxxFR
Analytics
Endpoints
Regroupement
Configurations
Profil
29. Swift : Nouvelles du front
#Xebia #DevoxxFRConfigurations
Toujours les Structs !
struct
Tags
{
struct
Page
{
static
let
Login
=
"Login"
static
let
Camera
=
"Camera"
static
let
Newsfeed
=
"Newsfeed"
static
let
Favorites
=
"Favorites"
static
let
MyProfile
=
"MyProfile"
}
}
Tags.Page.Login
//
Login
31. Swift : Nouvelles du front
#Xebia #DevoxxFR
600.000+ lignes!
Requêtes rapides!
Liste de produits
32. Swift : Nouvelles du front
#Xebia #DevoxxFRListe de produits
Stockage : Objective-C
@implementation
XBEntityStore
-‐
(void)addEntity:(id<XBEntity>)entity
{
//
Some
implementation
}
@end
...
Un peu d’Objective-C
-‐
(void)addSomeObjects
{
XBEntityStore
*productStore
=
[XBEntityStore
new];
//
XBProduct
conforms
to
XBEntity
XBProduct
*product
=
[XBProduct
new];
[productStore
addEntity:product];
}
33. Swift : Nouvelles du front
#Xebia #DevoxxFRListe de produits
Stockage : Objective-C
@implementation
XBEntityStore
-‐
(void)addEntity:(id<XBEntity>)entity
{
//
Some
implementation
}
@end
...
Un peu d’Objective-C
-‐
(void)addSomeObjects
{
XBEntityStore
*productStore
=
[XBEntityStore
new];
//
XBProduct
conforms
to
XBEntity
XBProduct
*product
=
[XBProduct
new];
[productStore
addEntity:product];
//
XBColor
conforms
to
XBEntity
XBColor
*color
=
[XBColor
new];
[productStore
addEntity:color];
}
34. Swift : Nouvelles du front
#Xebia #DevoxxFRListe de produits
Generics
class
EntityStore<T:
Entity>
{
func
addEntity(entity:
T)
{
//
Some
implementation
}
}
...
func
addSomeObjects()
{
let
productStore
=
EntityStore<Product>()
//
Product
conforms
to
Entity
let
product
=
Product()
productStore.addEntity(product)
//
Color
conforms
to
Entity
let
color
=
Color()
productStore.addEntity(color)
}
func
addSomeObjects()
{
let
productStore
=
EntityStore<Product>()
//
Product
conforms
to
Entity
let
product
=
Product()
productStore.addEntity(product)
//
Color
conforms
to
Entity
let
color
=
Color()
productStore.addEntity(color)
}
Un peu de Swift
35. Swift : Nouvelles du front
#Xebia #DevoxxFRListe de produits
Generics
Écrire du code facilement réutilisable
et sujet à des contraintes spécifiques
36. Swift : Nouvelles du front
#Xebia #DevoxxFRListe de produits
TypeAlias
typealias
ProductStore
=
EntityStore<Product>
func
addSomeObjects()
{
let
productStore
=
ProductStore()
//
...
}
Code plus expressif
37. Swift : Nouvelles du front
#Xebia #DevoxxFRListe de produits
Protocols
Swift est un “langage orienté protocoles”
Apple, 2015
protocol
Loadable
{
static
func
load(id:
String)
-‐>
Self?
}
38. Swift : Nouvelles du front
#Xebia #DevoxxFR
Protocols
protocol
Loadable
{
static
func
load(id:
String)
-‐>
Self?
}
class
Product
:
Loadable
{
static
func
load(id:
String)
-‐>
Self?
{
//
Open
the
default
database
store
//
Look
for
the
id
//
etc
}
}
Liste de produits
39. Swift : Nouvelles du front
#Xebia #DevoxxFRListe de produits
Protocols
protocol
Loadable
{
static
func
load(id:
String)
-‐>
Self?
}
class
Color
:
Loadable
{
static
func
load(id:
String)
-‐>
Self?
{
//
Open
the
default
database
store
//
Look
for
the
id
//
etc
}
}
40. Swift : Nouvelles du front
#Xebia #DevoxxFRListe de produits
Protocols Extensions
protocol
Loadable
{
static
func
load(id:
String)
-‐>
Self?
}
extension
Loadable
{
static
func
load(id:
String)
-‐>
Self?
{
//
Open
the
default
database
store
//
Look
for
the
id
//
etc
}
}
41. Swift : Nouvelles du front
#Xebia #DevoxxFRListe de produits
Protocols Extensions
protocol
Loadable
{
static
func
load(id:
String)
-‐>
Self?
}
extension
Loadable
{
static
func
load(id:
String)
-‐>
Self?
{
//
...
}
}
class
Product
:
Loadable
{
/*
...
*/
}
class
Color
:
Loadable
{
/*
...
*/
}
47. Swift : Nouvelles du front
#Xebia #DevoxxFR
Librairie locale
Tâches asynchrones
Ecran personnalisé
Animation diaphragme
Appareil photo
48. Swift : Nouvelles du front
#Xebia #DevoxxFRTâches asynchrones
Closure
Equivalent des blocks en Objective-C
Comprend un bloc de code à exécuter
Définition : {
()
-‐>
()
in
}
49. Swift : Nouvelles du front
#Xebia #DevoxxFRTâches asynchrones
Exemple de closure
self.closeDiaphragm
{
_
in
self.openDiaphragm()
}
50. Swift : Nouvelles du front
#Xebia #DevoxxFRTâches asynchrones
Optionals
Possède une valeur ou non
Rend le code plus expressif
Tester la non nullité : guard
let
Définition : var
delegate:CameraDelegate?
51. Swift : Nouvelles du front
#Xebia #DevoxxFRTâches asynchrones
Exemple d’optionals
func
setup(image:UIImage?)
{
guard
let
menuIcon
=
image
else
{
return
}
let
menuImageView
=
UIImageView(image:
menuIcon)
self.addSubview(menuImageView)
}
52. Swift : Nouvelles du front
#Xebia #DevoxxFRTâches asynchrones
Optionals : fiabilité !
Source : DevFee (Developers feeling)
53. Swift : Nouvelles du front
#Xebia #DevoxxFR
Appels asynchrones
Tâches asynchrones
Sur n’importe quel post
Gestion des retours serveur
Poster un commentaire
54. Swift : Nouvelles du front
#Xebia #DevoxxFRTâches asynchrones
Promise
Code plus lisible
Idéal pour les tâches asynchrones
Chaînage possible
55. Swift : Nouvelles du front
#Xebia #DevoxxFRTâches asynchrones
Exemple de Promise
//
Parameters
var
parameters
=
Parameters()
parameters["content"]
=
view.textView.text
PostRequest.getHeaders()
.success
{
token
in
return
PostRequest.postContent(token,
parameters)
}
.success
{
data
in
sendComplete()
}
.failure
{
(error,
isCancelled)
-‐>
Void
in
sendFailed()
}
56. Swift : Nouvelles du front
#Xebia #DevoxxFRTâches asynchrones
Sans Promise
//
Parameters
var
parameters
=
Parameters()
parameters["content"]
=
view.textView.text
PostRequest.getHeaders()
{
[weak
self]
(token:
String,
error:
NSError)
-‐>
Void
in
if
token
!=
nil
{
PostRequest.postContent(token,
parameters)
{
[weak
self]
(result:
Bool,
error:
NSError)
-‐>
Void
in
if
result
==
true
{
if
let
strongSelf
==
self
{
strongSelf.sendComplete()
}
}
else
{
if
let
strongSelf
==
self
{
strongSelf.sendFailed()
}
}
}
}
else
{
if
let
strongSelf
==
self
{
strongSelf.sendFailed()
}
}
}
57. Swift : Nouvelles du front
#Xebia #DevoxxFR
SwiftTasks
Une des librairies principales
Mise en place simple…
…mais des erreurs dans Xcode
58. Swift : Nouvelles du front
#Xebia #DevoxxFRTâches asynchrones
Erreurs avec Xcode
if
let
colorsFilePath
=
syncDataConfiguration.colorsFile,
data
=
NSData(contentsOfFile:colorsFilePath)
{
MetadataJSONDeserializer.deserialize(data,
keyPath:
"colors.color")
.success
{
color
-‐>
ColorDuplicateRemovalTask
in
return
self.removeDuplicateIdentifiers(colors)
}
.success
{
colors
-‐>
JSONInsertTask
in
return
entityStore.replaceAllObjects(colors,
transformer:
Color.tranform)
}
.success
{
JSONs
-‐>
Void
in
taskTuple.fulfill(JSONs)
}
.failure
{
(error,
isCancelled)
in
if
let
error
=
error
{
taskTuple.reject(error)
}
}
return
taskTuple.task
}
60. Swift : Nouvelles du front
#Xebia #DevoxxFR
Simple requête HTTP!
Mise à jour de la UI!
Disponibilité produit
61. Swift : Nouvelles du front
#Xebia #DevoxxFRDisponibilité produit
Les étapes
Récupération
Déserialisation
UI
62. Swift : Nouvelles du front
#Xebia #DevoxxFRDisponibilité produit
Functional Reactive Programming
Propagation de messages
Asynchronisme
Robustesse
63. Swift : Nouvelles du front
#Xebia #DevoxxFRDisponibilité produit
Functional Reactive Programming
^{
available
in
guard
let
available
=
available
as?
Bool
else
{
return
}
availabilitySpinner.stopAnimating()
availabilityIndicator.textColor
=
colorForStatus(available)
}
<~
KVO.stream(viewModel,
"status")
64. Swift : Nouvelles du front
#Xebia #DevoxxFR
ReactiveCocoa
github.com/ReactiveCocoa/
Disponibilité produit
65. Swift : Nouvelles du front
#Xebia #DevoxxFR
ReactKit
github.com/ReactKit/
Disponibilité produit
66. Swift : Nouvelles du front
#Xebia #DevoxxFR
[[[RACObserve(viewModel.status)
map:^id(NSString
*status)
{
if
([status
isKindOfClass:[NSString
class]])
{
return
status;
}
return
nil;
}]
map:^id(NSString
*status)
{
return
@([status
isEqualToString:@"available"]);
}]
delay:1.0];
[[[RACObserve(viewModel.status)
map:^id(NSString
*status)
{
if
([status
isKindOfClass:[NSString
class]])
{
return
status;
}
return
nil;
}]
map:^id(NSString
*status)
{
return
@([status
isEqualToString:@"available"]);
}]
delay:1.0];
FRP - Objective-C
Disponibilité produit
67. Swift : Nouvelles du front
#Xebia #DevoxxFR
FRP - Swift
let
availabilitySignal
=
KVO.signal(viewModel,
"status")
.map
{
status
-‐>
String?
in
return
status
as?
String
}
.map
{
status
-‐>
Bool
in
return
status
==
"available"
}
.delay(1.0)
Disponibilité produit
68. Swift : Nouvelles du front
#Xebia #DevoxxFRDisponibilité produit
Opérateurs Custom
^{
available
in
guard
let
available
=
available
as?
Bool
else
{
return
}
availabilitySpinner.stopAnimating()
availabilityIndicator.textColor
=
colorForStatus(available)
}
<~
KVO.stream(viewModel,
"status")<~
71. Swift : Nouvelles du front
#Xebia #DevoxxFR
Changement de la langue!
Traduction des contenus!
Traduction de l’IHM!
Internationalisation
72. Swift : Nouvelles du front
#Xebia #DevoxxFRInternationalisation
Enums
enum
Country
{
case
USA
case
China
case
Italy
}
Et finalement du Swift
73. Swift : Nouvelles du front
#Xebia #DevoxxFRInternationalisation
Enums
enum
Country
{
case
USA
case
China
case
Italy
static
var
preferred:
Country
{
return
Country.Italy
}
}
Country.preferred
//
.Italy
Et finalement du Swift
Valeurs associées
74. Swift : Nouvelles du front
#Xebia #DevoxxFRInternationalisation
Enums
enum
Country:
String
{
case
USA
case
Italy
var
name:
String
{
switch
self
{
case
USA:
return
"United
States"
case
Italy:
return
"Italia!"
}
}
}
Country.Italy.name
//
.Italia!
Et finalement du Swift
Propriétés
75. Swift : Nouvelles du front
#Xebia #DevoxxFRInternationalisation
Switch
enum
Language
{
case
English
case
Italian
//
...
var
language:
String
{
switch
self
{
//
...
}
}
}
Swift
76. Swift : Nouvelles du front
#Xebia #DevoxxFRInternationalisation
Switch
enum
Language
{
case
English(country:
Country)
case
Italian
var
language:
String
{
switch
self
{
case
.English(let
country):
return
"English
(country.name)"
//
...
}
}
Language.English(country:
.USA).name
//
English
(United
States)
Swift
Value
binding
77. Swift : Nouvelles du front
#Xebia #DevoxxFRInternationalisation
Enums : it’s over 9000!!!
First-Class types
Méthodes et propriétés
Vérifiés à la compilation
Valeurs associées
79. Swift : Nouvelles du front
#Xebia #DevoxxFR
Assurer le fonctionnement
de l’application
Se protéger des régressions
Tests
80. Swift : Nouvelles du front
#Xebia #DevoxxFRTests
Chargement des produits
Product.load("A00")
Comment tester ?
81. Swift : Nouvelles du front
#Xebia #DevoxxFRSlide Title
Protocols Extensions
protocol
Loadable
{
static
func
load(id:
String)
-‐>
Self?
}
extension
Loadable
{
static
func
load(id:
String)
-‐>
Self?
{
//
Open
the
default
database
store
//
Look
for
the
id
//
etc
}
}
82. Swift : Nouvelles du front
#Xebia #DevoxxFRSlide Title
Protocols Extensions
protocol
Loadable
{
static
func
load(id:
String,
store:
Store)
-‐>
Self?
}
extension
Loadable
{
static
func
load(id:
String,
store:
Store
=
Stores.DefaultStore)
-‐>
Self?
{
//
Open
the
default
database
store
//
Look
for
the
id
//
etc
}
}
83. Swift : Nouvelles du front
#Xebia #DevoxxFR
func
testDoSomething()
{
//
Setup
let
mockStore
=
Store(products:
[Product(id:
"A00")])
//
Run
let
product
=
Product.load("A00",
store:
mockStore)
//
Verify
XCTAssertNotNil(product)
}
Tests
Chargement des produits
Product.load("A00")
84. Swift : Nouvelles du front
#Xebia #DevoxxFRListe de produits
Swift VS Swift
class
Store
{
func
addEntity(entity:
Product)
{
//
Some
implementation
}
}
let
entityStore
=
EntityStore()
let
product
=
Product()
entityStore.addEntity(product)
Coder Swift != Penser Swift
class
Store<T:
Entity>
{
func
addEntity(entity:
T)
{
//
Some
implementation
}
}
let
productStore
=
ProductStore()
let
product
=
Product()
product.save(productStore)
Generics
Typealias
Protocol extensions
85. Swift : Nouvelles du front
#Xebia #DevoxxFRSlide Title
Title
#DevoxxFR
Swift : Nouvelles du front
86. Swift : Nouvelles du front
#Xebia #DevoxxFRSlide Title
Title
#DevoxxFR
Swift : Nouvelles du front
92. Swift : Nouvelles du front
#Xebia #DevoxxFRSuivre son application
Fabric
Déploiement centralisé et versionné
Remontées de crashs
Statistiques d’utilisation
93. Swift : Nouvelles du front
#Xebia #DevoxxFR
Fabric - Objective-C
RTSViewController.m
line
1310
-‐[RTSViewController
getCurrentUDPValues]
Fatal
Exception:
NSRangeException
***
-‐[__NSArrayM
objectAtIndex:]
index
4294967292
beyond
bounds
[0
..
511]
Suivre son application
94. Swift : Nouvelles du front
#Xebia #DevoxxFR
Fabric - Swift
WSClient.swift
line
31
static
MyLittlePony.WSClient.requestContent
(MyLittlePony.WSClient.Type)
parameters
:
Swift.Optional
<Swift.Dictionary
<Swift.String,
Swift.AnyObject>
Crashed:
com.apple.main-‐thread
SIGABRT
ABORT
at
0x312a8df0
Suivre son application
95. Swift : Nouvelles du front
#Xebia #DevoxxFRSuivre son application
Pourquoi ?
Évolution du langage constante
Rétrocompatibilité non assurée
97. Swift : Nouvelles du front
#Xebia #DevoxxFR
Et l’Objective-C dans tout ça ?
Toujours valable
Mais développement plus lent
Migration progressive conseillée
98. Swift : Nouvelles du front
#Xebia #DevoxxFR
Popularitédesrecherches
2014 2015 2016
Objective-C Swift Objective-C Swift Objective-C Swift
Source : TIOBE
3 ème
19 ème
14 ème
15 ème 15 ème
14 ème
Et l’Objective-C dans tout ça ?
99. Swift : Nouvelles du front
#Xebia #DevoxxFR
Les avantages de Swift
Plus facile à maintenir et plus robuste
Demande moins de code
Ouverture vers du développement back
Soyez acteur des évolutions d’Apple !
105. Swift : Nouvelles du front
#Xebia #DevoxxFR
Un peu de lecture
A Swift Kickstart
Daniel Steinberg
Functional Programming in Swift
Chris Eidhof, Florian Kugler, and Wouter Swierstra
http://blog.xebia.fr/tag/swift
L’équipe iOS Xebia
Advanced Swift
Chris Eidhof and Airspeed Velocity