2. COCOAHEADS NANTES
⸠MyScript: One year later
⸠UI testing with Push NotiďŹcations
⸠LâaccessibilitĂŠ pour les mal-voyants sur iOS 11
⸠đş + đŻ
FĂVRIER 2018
@cocoanantes@iTony_L
3. MYSCRIPT: ONE YEAR LATER
CALCULATOR 2
đ
Calculator 2
@cocoanantes@iTony_L
5. MYSCRIPT: ONE YEAR LATER
PRE-ORDER: FOR THE DEVELOPER
⸠Very easy with few rules:
⸠Like a normal submission
⸠Max 90 days
⸠As many updates you want
⸠Limitations:
⸠Only for the ďŹrst version
⸠No info except the number of pre-orders
@cocoanantes@iTony_L
6. MYSCRIPT: ONE YEAR LATER
PRE-ORDER: FOR THE USER
⸠The app is next to the other apps
⸠Can see descriptions, videos and screenshots
⸠Can cancel at any moment
⸠Will be charged when the app will be available
@cocoanantes@iTony_L
7. MYSCRIPT: ONE YEAR LATER
FASTLANE (VIVIEN CORMIER, MARS 2017)
⸠Before:
Build Test
Build Test
Build Test
.ipa
.ipa
.ipa
â
â
⌠âŚ
â
App Store
1. A developer did a
checkout of this
commit
2. ModiďŹed the
build number
3. Did an archive
and upload via
Xcode đ
@cocoanantes@iTony_L
8. MYSCRIPT: ONE YEAR LATER
FASTLANE (VIVIEN CORMIER, MARS 2017)
⸠After:
Build Test
Build Test
Build Test
.ipa
.ipa
.ipa
â
â
⌠âŚ
â
App Store
Pilot đ
@cocoanantes@iTony_L
9. MYSCRIPT: ONE YEAR LATER
Pilot đ
@cocoanantes@iTony_L
Or
lane :upload do
pilot(
ipa: âMyApp.ipa",
skip_submission: true,
skip_waiting_for_build_processing: true
)
end
+ $ fastlane itunesConnectFastďŹle
FASTLANE (VIVIEN CORMIER, MARS 2017)
$ fastlane pilot upload -u user@myscript.com -i MyApp.ipa -s -z
10. MYSCRIPT: ONE YEAR LATER
FASTLANE (VIVIEN CORMIER, MARS 2017)
desc "Build the app-store xcarchive"
lane :buildStore do |options|
gym(
workspace: "UnicornNote/UnicornNote.xcworkspace",
scheme: options[:scheme],
output_directory: './artifact',
output_name: "MyScript_Nebo.ipa",
archive_path: "./artifact/"+options[:scheme],
xcargs: "OTHER_SWIFT_FLAGS='$OTHER_SWIFT_FLAGS -DARCHIVE'",
clean: true
)
end
FastďŹle
$ fastlane buildStore scheme:${SCHEME}
How we call it
@cocoanantes@iTony_L
11. MYSCRIPT: ONE YEAR LATER
FASTLANE (VIVIEN CORMIER, MARS 2017)
⸠Now, we use:
⸠gym
⸠sigh
⸠pilot
⸠Next:
⸠deliver
@cocoanantes@iTony_L
12. MYSCRIPT: ONE YEAR LATER
BUNDLER (JULIEN QUĂRĂ, MAI 2017)
Bundler provides a consistent environment for Ruby projects by tracking and installing the
exact gems and versions that are needed.
Bundler is an exit from dependency hell, and ensures that the gems you need are present in
development, staging, and production. Starting work on a project is as simple as bundle
install.
@cocoanantes@iTony_L
13. MYSCRIPT: ONE YEAR LATER
BUNDLER (JULIEN QUĂRĂ, MAY 2017)
⸠Before:
'Cocoapods 1.1
(Cocoapods 1.1.1
(Cocoapods 1.2
'Cocoapods 0.9
'Cocoapods 1.3.1
đĽCocoapods 1.0
@cocoanantes@iTony_L
14. MYSCRIPT: ONE YEAR LATER
BUNDLER (JULIEN QUĂRĂ, MAY 2017)
⸠Inconvenient:
⸠PodďŹle.lock
⸠Incompatibility between versions
⸠Workspace integration
⸠CI: Ask to install a speciďŹc version on each Mac
@cocoanantes@iTony_L
15. MYSCRIPT: ONE YEAR LATER
BUNDLER (JULIEN QUĂRĂ, MAI 2017)
⸠After:
source 'https://rubygems.org'
gem 'cocoapods', '1.3.1'
gem 'fastlane', '~> 2', '>= 2.61.0'
'Cocoapods 1.3.1
đĽCocoapods 1.3.1
@cocoanantes@iTony_L
1. $ gem install bundler
2. GemďŹle
3. $ bundle install
4. $ bundle exec pod install
16. MYSCRIPT: ONE YEAR LATER
BUNDLER (JULIEN QUĂRĂ, MAY 2017)
⸠Inconvenients:
⸠PodďŹle.lock â
⸠Incompatibility between versions â
⸠Workspace integration â
⸠CI: Ask to install a speciďŹc version on each Mac â
â Everyone can have a different version of bundler
@cocoanantes@iTony_L
17. MYSCRIPT: ONE YEAR LATER
PROMISEKIT (JULIEN QUĂRĂ, JANUARY 2018)
A promise is an object that represents an asynchronous task.
Pass that object around, and write clean, ordered code; a
logical, simple, modular stream of progression from one
asynchronous task to another.
@cocoanantes@iTony_L
18. MYSCRIPT: ONE YEAR LATER
PROMISEKIT (JULIEN QUĂRĂ, JANUARY 2018)
APIManager.shared.fetchToken(withLogin: "dh", password: "dd",
completion: { (token, error) in
guard error == nil else {
print("Error:(error!)")
return
}
guard let token = token else {
print("Received empty token ÂŻ_(ă)_/ÂŻ")
return
}
APIManager.shared.fetchUser(withToken: token,
completion:{ (user, error) in
guard error == nil else {
print("Error:(error!)")
return
}
guard var user = user else {
print("Received nil user ÂŻ_(ă)_/ÂŻ")
return
}
APIManager.shared.fetchConversations(forUserWithId: user.identiďŹer,
completion: { (conversations, error) in
guard error == nil else {
print("Error:(error!)")
return
}
guard let conversations = conversations else {
print("Received nil conversations ÂŻ_(ă)_/ÂŻ")
return
}
APIManager.shared.fetchFriends(forUserWithId: user.identiďŹer,
completion: { (friends, error) in
guard error == nil else {
print("Error:(error!)")
return
}
guard let friends = friends else {
print("Received nil friend list ÂŻ_(ă)_/ÂŻ")
return
}
// Here we have all the friends, conversations and user.
// It's OVER !
// PS: If you can read this, raise your hands :)
})
})
})
})
19. MYSCRIPT: ONE YEAR LATER
PROMISEKIT (JULIEN QUĂRĂ, JANUARY 2018)
ďŹrstly {
APIManager.shared.fetchToken(withLogin: "dh", password: "dd")
}.then { token in
APIManager.shared.fetchUser(withToken: token)
}.then { user -> Promise<([User], [Conversation])> in
self.currentUser = user
let friendsPromise = APIManager.shared.fetchFriends(forUserWithId: user.identiďŹer)
let conversationsPromise = APIManager.shared.fetchConversations(forUserWithId: user.identiďŹer)
return when(fulďŹlled: friendsPromise, conversationsPromise)
}.then { (friends, conversations) -> Void in
print("Hello (self.currentUser!.fullname).")
print("You have (conversations.count) conversations:")
for conversation in conversations {
let friend = friends.ďŹrst(where: {$0.identiďŹer == conversation.otherPeerId})
print("[(friend?.fullname ?? "Unknow")] (conversation.title)")
}
}.catch { (error) in
print("We have an error, another failed experiment :( (error)")
}
20. MYSCRIPT: ONE YEAR LATER
PROMISEKIT (JULIEN QUĂRĂ, JANUARY 2018)
⸠We use it for the CloudKit integration in Nebo
⸠What we like:
⸠Very easy to integrate
⸠Code readability
⸠Simplify the workďŹow
@cocoanantes@iTony_L
21. MYSCRIPT: ONE YEAR LATER
PROMISEKIT (JULIEN QUĂRĂ, JANUARY 2018)
⸠Problems encountered:
⸠Debugging as Julien said
⸠Progress handler
⸠Promise in Promise
.then { variable -> Promise<TonType> in
if condition {
return otherPromise
} else {
return nil
}
}
Unsupported
@cocoanantes@iTony_L