Making Swift even safer
18 months of development
120k+ lines of Swift
≈40 releases
6 months in production
Juno Rider iOS
Stricter interface
extension Array {
public subscript (safe index: Int) -> Element? { … }
}
extension Int {
public init?(safe value: Float) { … }
public init?(safe value: Double) { … }
public init?(safe value: UInt) { … }
}
!
NonEmptyString
NonEmptyArray
NonNegativeDouble
Stronger types
struct LocationCoordinate<A> {
let value: Double
init(_ value: Double) { self.value = value }
}
enum Lat {}
enum Lon {}
struct Location {
let latitude: LocationCoordinate<Lat>
let longitude: LocationCoordinate<Lon>
}
Phantom types
struct TaggedValue<ValueType, Tag> {
let value: ValueType
init(_ value: ValueType) { self.value = value }
}
enum PickupTimeTag {}
typealias PickupTime = TaggedValue<NSDate, PickupTimeTag>
enum DropoffTimeTag {}
typealias DropoffTime = TaggedValue<NSDate, DropoffTimeTag>
Phantom types
enum PhoneTag {}
typealias Phone = TaggedValue<String, PhoneTag>
extension TaggedValueType where Tag == PhoneTag, ValueType == String {
func trimCountryCode() -> Phone {
return Phone(trimCountryCode(self.taggedValue.value))
}
}
private func trimCountryCode(phone: String) -> String { … }
Phantom types
extension TaggedValueType where ValueType: JSONEncoding {
func encodeJSON() -> AnyObject {
return self.taggedValue.value.encodeJSON()
}
}
Phantom types
enum StringKey {
case AccessibilityHomeConfirmButton
case DialogInsufficientFundsTitle
...
}
func localized(value: Strings) -> String {
switch value {
case .AccessibilityHomeConfirmButton:
return String.localized(“Accessibility.Home.Confirm.Button”)
...
}
}
Static resources
extension HTTP {
enum Error: ErrorType {
case InvalidResponse(request: NSURLRequest, response: NSURLResponse?)
case TransportError(request: NSURLRequest, error: NSError)
case HTTPError(request: NSURLRequest, response: NSHTTPURLResponse, responseData: NSData?)
case CannotCreateURL(components: NSURLComponents)
case InvalidURL(urlString: String)
case AuthServiceFailure
case CannotBindStreamPair(request: NSURLRequest)
case StreamWriting(request: NSURLRequest, error: NSError?)
case StreamGzipEncoding(request: NSURLRequest, operation: HTTP.Error.GzipOperation)
}
}
Strong ErrorType
extension JSON.Error {
struct Encode: ErrorType {
public let error: NSError
public let source: Any
}
enum Decode: ErrorType {
case Unexpected
case Serialization(error: NSError, data: NSData)
case SchemeMismatch(error: JSON.Error.SchemeMismatch, body: AnyObject?)
}
struct SchemeMismatch: ErrorType {
public let pathComponents: [String]
public let reason: String
}
}
Strong ErrorType
public enum JSONTaskError: ErrorType {
case Task(error: HTTP.Error)
case Request(error: JSON.Error.Encode)
case Response(response: HTTP.Response, error: JSON.Error.Decode)
}
Strong ErrorType
Changing code
Components
Context
App
Context
May contain dirty things dealing with global state
Context
May contain dirty things dealing with global state
Unit tests
Context
typealias AppContext = protocol<
StringsServiceContainer,
StaticImageServicesContainer,
BundleImagesServiceContainer,
ReachabilityServiceContainer,
AnalyticsServiceContainer,
SchedulerContainer,
RemoteNotificationsContainer,
RemoteNotificationsPermissionContainer,
RemoteNotificationClearActionContainer,
LocationServiceContainer,
ApplicationServiceContainer,
DeviceServiceContainer,
…
>
class ElDependor: AppContext { … }
class MockContext: AppContext { … }
App
Pure state machine
App
Pure state machine
- Unit tests
- Integrated acceptance tests
Acceptance tests via TestApp
class Allow_Rider_to_Have_Max_X_Cards_per_Account_Spec: QuickSpec {
override func spec() {
var app: TestApp!
beforeEach { app = TestApp() }
given("rider is entering CC details on Add CC screen") {
beforeEach {
app.login()
app.goToHomeScreen()
app.receiveSomePaymentMethods()
app.openPayments()
app.payments.paymentMethods.tapAddPayment()
app.payments.addPayment.enterSomeCC()
app.payments.addPayment.tapNext()
app.payments.addPayment.enterSomeZipCode()
}
when("rider taps Add CC button") {
beforeEach { app.payments.addPayment.tapDone() }
and("BE returns the message about max number of active cards") {
beforeEach { app._context.addCreditCard.receive(.Failed(.TooManyPaymentMethods)) }
then("present the Max Cards Added alert") {
app.payments.addPayment.expectToPresentAlert()
}
when("rider taps Ok button") {
beforeEach { app.payments.addPayment.alert.tapOK() }
then("dismiss the alert") {
app.payments.addPayment.expectToNotPresentAlert()
}
Making Swift even safer
View: screenshot testing
View: screenshot testing
View: screenshot testing
Detecting & investigating bugs in the field
- smart assertions
- diligent logging
- daily duty
junoAssert
in Debug - crash 🙀
in Release - log.error(), trackNonFatal() and recover 🙏
Logging
switch error {
case let .InvalidResponse(value):
log.warn(
category: logCategory,
message: "Unexpected response",
payload: [
.RequestIdentifier: error.requestIdentifier,
.ResponseIdentifier: error.responseIdentifier,
.Description: "(value.response)",
.Path: value.request.path,
.URL: value.request.absoluteURL
]
)
…
Logging
Analytics junoAssert
↓ ↓
log.verbose log.info log.warn log.error
↓ ↓ ↓ ↓
{'key0':'value0','key1':‘value1'}
↓
append to txt file
↓
roll & upload to AWS
↓
Elasticsearch + Kibana
Logging
Production quality comes at a price
- takes up to x2 dev effort
- challenging for new team members
- performance considerations
But it brings satisfaction
Thank you

More Related Content

PDF
"Kotlin и rx в android" Дмитрий Воронин (Avito)
PDF
​"Delegates, Delegates everywhere" Владимир Миронов
PPTX
Improving Correctness with Types
PDF
Develop your next app with kotlin @ AndroidMakersFr 2017
PDF
Reactive cocoa made Simple with Swift
PDF
Kotlin cheat sheet by ekito
PDF
How do you create a programming language for the JVM?
PDF
Kotlin hands on - MorningTech ekito 2017
"Kotlin и rx в android" Дмитрий Воронин (Avito)
​"Delegates, Delegates everywhere" Владимир Миронов
Improving Correctness with Types
Develop your next app with kotlin @ AndroidMakersFr 2017
Reactive cocoa made Simple with Swift
Kotlin cheat sheet by ekito
How do you create a programming language for the JVM?
Kotlin hands on - MorningTech ekito 2017

What's hot (20)

PDF
JavaScript for real men
PDF
Introduction to typescript
PDF
ReactiveCocoa and Swift, Better Together
PPTX
Compile time polymorphism
PDF
(How) can we benefit from adopting scala?
PDF
Kotlin, smarter development for the jvm
PDF
One Year of Clean Architecture - The Good, The Bad and The Bob
PPT
Overloading
PDF
Intro to ReactiveCocoa
PDF
Introduction to kotlin
PDF
Introduction to idris
PDF
Logging in code
PDF
What make Swift Awesome
PDF
Spring Transaction
PDF
ADG Poznań - Kotlin for Android developers
PDF
Introduction to kotlin for android app development gdg ahmedabad dev fest 2017
PDF
An Introduction to Reactive Cocoa
ODP
AST Transformations
PDF
Kotlin Developer Starter in Android projects
PDF
ConFess Vienna 2015 - Metaprogramming with Groovy
JavaScript for real men
Introduction to typescript
ReactiveCocoa and Swift, Better Together
Compile time polymorphism
(How) can we benefit from adopting scala?
Kotlin, smarter development for the jvm
One Year of Clean Architecture - The Good, The Bad and The Bob
Overloading
Intro to ReactiveCocoa
Introduction to kotlin
Introduction to idris
Logging in code
What make Swift Awesome
Spring Transaction
ADG Poznań - Kotlin for Android developers
Introduction to kotlin for android app development gdg ahmedabad dev fest 2017
An Introduction to Reactive Cocoa
AST Transformations
Kotlin Developer Starter in Android projects
ConFess Vienna 2015 - Metaprogramming with Groovy
Ad

Similar to Making Swift even safer (20)

PDF
Sane Sharding with Akka Cluster
PPTX
Reactive programming every day
PPTX
Improving Correctness with Types Kats Conf
PPTX
Rx workshop
PDF
Taming Core Data by Arek Holko, Macoscope
PDF
Clean coding-practices
PPT
Lo Mejor Del Pdc2008 El Futrode C#
PDF
Scalable Angular 2 Application Architecture
PDF
Reactive, component 그리고 angular2
PDF
mobl
PPTX
Codestrong 2012 breakout session hacking titanium
PDF
The Swift Compiler and Standard Library
PPTX
Codable routing
PPTX
Is your C# optimized
KEY
Restful Web Service
PDF
CAVE Overview
PDF
“iOS 11 в App in the Air”, Пронин Сергей, App in the Air
PPTX
Improving Correctness With Type - Goto Con Berlin
PDF
TDD and mobile development: some forgotten techniques, illustrated with Android
PPT
Java Performance Tuning
Sane Sharding with Akka Cluster
Reactive programming every day
Improving Correctness with Types Kats Conf
Rx workshop
Taming Core Data by Arek Holko, Macoscope
Clean coding-practices
Lo Mejor Del Pdc2008 El Futrode C#
Scalable Angular 2 Application Architecture
Reactive, component 그리고 angular2
mobl
Codestrong 2012 breakout session hacking titanium
The Swift Compiler and Standard Library
Codable routing
Is your C# optimized
Restful Web Service
CAVE Overview
“iOS 11 в App in the Air”, Пронин Сергей, App in the Air
Improving Correctness With Type - Goto Con Berlin
TDD and mobile development: some forgotten techniques, illustrated with Android
Java Performance Tuning
Ad

Recently uploaded (20)

PPTX
O2C Customer Invoices to Receipt V15A.pptx
PDF
sustainability-14-14877-v2.pddhzftheheeeee
PDF
NewMind AI Weekly Chronicles – August ’25 Week III
PDF
Taming the Chaos: How to Turn Unstructured Data into Decisions
PDF
Univ-Connecticut-ChatGPT-Presentaion.pdf
PDF
CloudStack 4.21: First Look Webinar slides
PDF
1 - Historical Antecedents, Social Consideration.pdf
PDF
Microsoft Solutions Partner Drive Digital Transformation with D365.pdf
PDF
Assigned Numbers - 2025 - Bluetooth® Document
PPT
What is a Computer? Input Devices /output devices
PDF
A Late Bloomer's Guide to GenAI: Ethics, Bias, and Effective Prompting - Boha...
PDF
August Patch Tuesday
PPTX
Benefits of Physical activity for teenagers.pptx
PPTX
Chapter 5: Probability Theory and Statistics
PDF
TrustArc Webinar - Click, Consent, Trust: Winning the Privacy Game
PDF
Architecture types and enterprise applications.pdf
PPTX
The various Industrial Revolutions .pptx
PDF
A comparative study of natural language inference in Swahili using monolingua...
PDF
Unlock new opportunities with location data.pdf
PDF
ENT215_Completing-a-large-scale-migration-and-modernization-with-AWS.pdf
O2C Customer Invoices to Receipt V15A.pptx
sustainability-14-14877-v2.pddhzftheheeeee
NewMind AI Weekly Chronicles – August ’25 Week III
Taming the Chaos: How to Turn Unstructured Data into Decisions
Univ-Connecticut-ChatGPT-Presentaion.pdf
CloudStack 4.21: First Look Webinar slides
1 - Historical Antecedents, Social Consideration.pdf
Microsoft Solutions Partner Drive Digital Transformation with D365.pdf
Assigned Numbers - 2025 - Bluetooth® Document
What is a Computer? Input Devices /output devices
A Late Bloomer's Guide to GenAI: Ethics, Bias, and Effective Prompting - Boha...
August Patch Tuesday
Benefits of Physical activity for teenagers.pptx
Chapter 5: Probability Theory and Statistics
TrustArc Webinar - Click, Consent, Trust: Winning the Privacy Game
Architecture types and enterprise applications.pdf
The various Industrial Revolutions .pptx
A comparative study of natural language inference in Swahili using monolingua...
Unlock new opportunities with location data.pdf
ENT215_Completing-a-large-scale-migration-and-modernization-with-AWS.pdf

Making Swift even safer

  • 2. 18 months of development 120k+ lines of Swift ≈40 releases 6 months in production Juno Rider iOS
  • 4. extension Array { public subscript (safe index: Int) -> Element? { … } } extension Int { public init?(safe value: Float) { … } public init?(safe value: Double) { … } public init?(safe value: UInt) { … } } !
  • 6. struct LocationCoordinate<A> { let value: Double init(_ value: Double) { self.value = value } } enum Lat {} enum Lon {} struct Location { let latitude: LocationCoordinate<Lat> let longitude: LocationCoordinate<Lon> } Phantom types
  • 7. struct TaggedValue<ValueType, Tag> { let value: ValueType init(_ value: ValueType) { self.value = value } } enum PickupTimeTag {} typealias PickupTime = TaggedValue<NSDate, PickupTimeTag> enum DropoffTimeTag {} typealias DropoffTime = TaggedValue<NSDate, DropoffTimeTag> Phantom types
  • 8. enum PhoneTag {} typealias Phone = TaggedValue<String, PhoneTag> extension TaggedValueType where Tag == PhoneTag, ValueType == String { func trimCountryCode() -> Phone { return Phone(trimCountryCode(self.taggedValue.value)) } } private func trimCountryCode(phone: String) -> String { … } Phantom types
  • 9. extension TaggedValueType where ValueType: JSONEncoding { func encodeJSON() -> AnyObject { return self.taggedValue.value.encodeJSON() } } Phantom types
  • 10. enum StringKey { case AccessibilityHomeConfirmButton case DialogInsufficientFundsTitle ... } func localized(value: Strings) -> String { switch value { case .AccessibilityHomeConfirmButton: return String.localized(“Accessibility.Home.Confirm.Button”) ... } } Static resources
  • 11. extension HTTP { enum Error: ErrorType { case InvalidResponse(request: NSURLRequest, response: NSURLResponse?) case TransportError(request: NSURLRequest, error: NSError) case HTTPError(request: NSURLRequest, response: NSHTTPURLResponse, responseData: NSData?) case CannotCreateURL(components: NSURLComponents) case InvalidURL(urlString: String) case AuthServiceFailure case CannotBindStreamPair(request: NSURLRequest) case StreamWriting(request: NSURLRequest, error: NSError?) case StreamGzipEncoding(request: NSURLRequest, operation: HTTP.Error.GzipOperation) } } Strong ErrorType
  • 12. extension JSON.Error { struct Encode: ErrorType { public let error: NSError public let source: Any } enum Decode: ErrorType { case Unexpected case Serialization(error: NSError, data: NSData) case SchemeMismatch(error: JSON.Error.SchemeMismatch, body: AnyObject?) } struct SchemeMismatch: ErrorType { public let pathComponents: [String] public let reason: String } } Strong ErrorType
  • 13. public enum JSONTaskError: ErrorType { case Task(error: HTTP.Error) case Request(error: JSON.Error.Encode) case Response(response: HTTP.Response, error: JSON.Error.Decode) } Strong ErrorType
  • 17. Context May contain dirty things dealing with global state
  • 18. Context May contain dirty things dealing with global state Unit tests
  • 19. Context typealias AppContext = protocol< StringsServiceContainer, StaticImageServicesContainer, BundleImagesServiceContainer, ReachabilityServiceContainer, AnalyticsServiceContainer, SchedulerContainer, RemoteNotificationsContainer, RemoteNotificationsPermissionContainer, RemoteNotificationClearActionContainer, LocationServiceContainer, ApplicationServiceContainer, DeviceServiceContainer, … > class ElDependor: AppContext { … } class MockContext: AppContext { … }
  • 21. App Pure state machine - Unit tests - Integrated acceptance tests
  • 22. Acceptance tests via TestApp class Allow_Rider_to_Have_Max_X_Cards_per_Account_Spec: QuickSpec { override func spec() { var app: TestApp! beforeEach { app = TestApp() } given("rider is entering CC details on Add CC screen") { beforeEach { app.login() app.goToHomeScreen() app.receiveSomePaymentMethods() app.openPayments() app.payments.paymentMethods.tapAddPayment() app.payments.addPayment.enterSomeCC() app.payments.addPayment.tapNext() app.payments.addPayment.enterSomeZipCode() } when("rider taps Add CC button") { beforeEach { app.payments.addPayment.tapDone() } and("BE returns the message about max number of active cards") { beforeEach { app._context.addCreditCard.receive(.Failed(.TooManyPaymentMethods)) } then("present the Max Cards Added alert") { app.payments.addPayment.expectToPresentAlert() } when("rider taps Ok button") { beforeEach { app.payments.addPayment.alert.tapOK() } then("dismiss the alert") { app.payments.addPayment.expectToNotPresentAlert() }
  • 27. Detecting & investigating bugs in the field - smart assertions - diligent logging - daily duty
  • 28. junoAssert in Debug - crash 🙀 in Release - log.error(), trackNonFatal() and recover 🙏
  • 29. Logging switch error { case let .InvalidResponse(value): log.warn( category: logCategory, message: "Unexpected response", payload: [ .RequestIdentifier: error.requestIdentifier, .ResponseIdentifier: error.responseIdentifier, .Description: "(value.response)", .Path: value.request.path, .URL: value.request.absoluteURL ] ) …
  • 30. Logging Analytics junoAssert ↓ ↓ log.verbose log.info log.warn log.error ↓ ↓ ↓ ↓ {'key0':'value0','key1':‘value1'} ↓ append to txt file ↓ roll & upload to AWS ↓ Elasticsearch + Kibana
  • 32. Production quality comes at a price - takes up to x2 dev effort - challenging for new team members - performance considerations
  • 33. But it brings satisfaction