making swift even safer
TRANSCRIPT
![Page 1: Making Swift even safer](https://reader035.vdocuments.us/reader035/viewer/2022062503/58f13d9e1a28ab8e668b45c3/html5/thumbnails/1.jpg)
Making Swift even safer
![Page 2: Making Swift even safer](https://reader035.vdocuments.us/reader035/viewer/2022062503/58f13d9e1a28ab8e668b45c3/html5/thumbnails/2.jpg)
18 months of development
120k+ lines of Swift
≈40 releases
6 months in production
Juno Rider iOS
![Page 3: Making Swift even safer](https://reader035.vdocuments.us/reader035/viewer/2022062503/58f13d9e1a28ab8e668b45c3/html5/thumbnails/3.jpg)
Stricter interface
![Page 4: Making Swift even safer](https://reader035.vdocuments.us/reader035/viewer/2022062503/58f13d9e1a28ab8e668b45c3/html5/thumbnails/4.jpg)
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) { … }}
!
![Page 5: Making Swift even safer](https://reader035.vdocuments.us/reader035/viewer/2022062503/58f13d9e1a28ab8e668b45c3/html5/thumbnails/5.jpg)
NonEmptyString
NonEmptyArray
NonNegativeDouble
Stronger types
![Page 6: Making Swift even safer](https://reader035.vdocuments.us/reader035/viewer/2022062503/58f13d9e1a28ab8e668b45c3/html5/thumbnails/6.jpg)
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
![Page 7: Making Swift even safer](https://reader035.vdocuments.us/reader035/viewer/2022062503/58f13d9e1a28ab8e668b45c3/html5/thumbnails/7.jpg)
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
![Page 8: Making Swift even safer](https://reader035.vdocuments.us/reader035/viewer/2022062503/58f13d9e1a28ab8e668b45c3/html5/thumbnails/8.jpg)
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
![Page 9: Making Swift even safer](https://reader035.vdocuments.us/reader035/viewer/2022062503/58f13d9e1a28ab8e668b45c3/html5/thumbnails/9.jpg)
extension TaggedValueType where ValueType: JSONEncoding {
func encodeJSON() -> AnyObject { return self.taggedValue.value.encodeJSON() }}
Phantom types
![Page 10: Making Swift even safer](https://reader035.vdocuments.us/reader035/viewer/2022062503/58f13d9e1a28ab8e668b45c3/html5/thumbnails/10.jpg)
enum StringKey { case AccessibilityHomeConfirmButton case DialogInsufficientFundsTitle
...}
func localized(value: Strings) -> String {switch value {case .AccessibilityHomeConfirmButton:
return String.localized(“Accessibility.Home.Confirm.Button”)
...
}}
Static resources
![Page 11: Making Swift even safer](https://reader035.vdocuments.us/reader035/viewer/2022062503/58f13d9e1a28ab8e668b45c3/html5/thumbnails/11.jpg)
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
![Page 12: Making Swift even safer](https://reader035.vdocuments.us/reader035/viewer/2022062503/58f13d9e1a28ab8e668b45c3/html5/thumbnails/12.jpg)
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
![Page 13: Making Swift even safer](https://reader035.vdocuments.us/reader035/viewer/2022062503/58f13d9e1a28ab8e668b45c3/html5/thumbnails/13.jpg)
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
![Page 14: Making Swift even safer](https://reader035.vdocuments.us/reader035/viewer/2022062503/58f13d9e1a28ab8e668b45c3/html5/thumbnails/14.jpg)
Changing code
![Page 15: Making Swift even safer](https://reader035.vdocuments.us/reader035/viewer/2022062503/58f13d9e1a28ab8e668b45c3/html5/thumbnails/15.jpg)
Components
![Page 16: Making Swift even safer](https://reader035.vdocuments.us/reader035/viewer/2022062503/58f13d9e1a28ab8e668b45c3/html5/thumbnails/16.jpg)
Context
App
![Page 17: Making Swift even safer](https://reader035.vdocuments.us/reader035/viewer/2022062503/58f13d9e1a28ab8e668b45c3/html5/thumbnails/17.jpg)
ContextMay contain dirty things dealing with global state
![Page 18: Making Swift even safer](https://reader035.vdocuments.us/reader035/viewer/2022062503/58f13d9e1a28ab8e668b45c3/html5/thumbnails/18.jpg)
ContextMay contain dirty things dealing with global state
Unit tests
![Page 19: Making Swift even safer](https://reader035.vdocuments.us/reader035/viewer/2022062503/58f13d9e1a28ab8e668b45c3/html5/thumbnails/19.jpg)
Contexttypealias AppContext = protocol< StringsServiceContainer, StaticImageServicesContainer, BundleImagesServiceContainer, ReachabilityServiceContainer, AnalyticsServiceContainer, SchedulerContainer, RemoteNotificationsContainer, RemoteNotificationsPermissionContainer, RemoteNotificationClearActionContainer, LocationServiceContainer, ApplicationServiceContainer, DeviceServiceContainer,
…>
class ElDependor: AppContext { … }
class MockContext: AppContext { … }
![Page 20: Making Swift even safer](https://reader035.vdocuments.us/reader035/viewer/2022062503/58f13d9e1a28ab8e668b45c3/html5/thumbnails/20.jpg)
App
Pure state machine
![Page 21: Making Swift even safer](https://reader035.vdocuments.us/reader035/viewer/2022062503/58f13d9e1a28ab8e668b45c3/html5/thumbnails/21.jpg)
App
Pure state machine
- Unit tests- Integrated acceptance tests
![Page 22: Making Swift even safer](https://reader035.vdocuments.us/reader035/viewer/2022062503/58f13d9e1a28ab8e668b45c3/html5/thumbnails/22.jpg)
Acceptance tests via TestAppclass 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() }
![Page 23: Making Swift even safer](https://reader035.vdocuments.us/reader035/viewer/2022062503/58f13d9e1a28ab8e668b45c3/html5/thumbnails/23.jpg)
![Page 24: Making Swift even safer](https://reader035.vdocuments.us/reader035/viewer/2022062503/58f13d9e1a28ab8e668b45c3/html5/thumbnails/24.jpg)
View: screenshot testing
![Page 25: Making Swift even safer](https://reader035.vdocuments.us/reader035/viewer/2022062503/58f13d9e1a28ab8e668b45c3/html5/thumbnails/25.jpg)
View: screenshot testing
![Page 26: Making Swift even safer](https://reader035.vdocuments.us/reader035/viewer/2022062503/58f13d9e1a28ab8e668b45c3/html5/thumbnails/26.jpg)
View: screenshot testing
![Page 27: Making Swift even safer](https://reader035.vdocuments.us/reader035/viewer/2022062503/58f13d9e1a28ab8e668b45c3/html5/thumbnails/27.jpg)
Detecting & investigating bugs in the field
- smart assertions- diligent logging- daily duty
![Page 28: Making Swift even safer](https://reader035.vdocuments.us/reader035/viewer/2022062503/58f13d9e1a28ab8e668b45c3/html5/thumbnails/28.jpg)
junoAssert
in Debug - crash 🙀in Release - log.error(), trackNonFatal() and recover 🙏
![Page 29: Making Swift even safer](https://reader035.vdocuments.us/reader035/viewer/2022062503/58f13d9e1a28ab8e668b45c3/html5/thumbnails/29.jpg)
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 ] )
…
![Page 30: Making Swift even safer](https://reader035.vdocuments.us/reader035/viewer/2022062503/58f13d9e1a28ab8e668b45c3/html5/thumbnails/30.jpg)
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
![Page 31: Making Swift even safer](https://reader035.vdocuments.us/reader035/viewer/2022062503/58f13d9e1a28ab8e668b45c3/html5/thumbnails/31.jpg)
Logging
![Page 32: Making Swift even safer](https://reader035.vdocuments.us/reader035/viewer/2022062503/58f13d9e1a28ab8e668b45c3/html5/thumbnails/32.jpg)
Production quality comes at a price
- takes up to x2 dev effort- challenging for new team members- performance considerations
![Page 33: Making Swift even safer](https://reader035.vdocuments.us/reader035/viewer/2022062503/58f13d9e1a28ab8e668b45c3/html5/thumbnails/33.jpg)
But it brings satisfaction
![Page 34: Making Swift even safer](https://reader035.vdocuments.us/reader035/viewer/2022062503/58f13d9e1a28ab8e668b45c3/html5/thumbnails/34.jpg)
Thank you