Top Banner
Making Swift even safer
34

Making Swift even safer

Apr 15, 2017

Download

Technology

Denis Fileev
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Making Swift even safer

Making Swift even safer

Page 2: Making Swift even safer

18 months of development

120k+ lines of Swift

≈40 releases

6 months in production

Juno Rider iOS

Page 3: Making Swift even safer

Stricter interface

Page 4: Making Swift even safer

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

NonEmptyString

NonEmptyArray

NonNegativeDouble

Stronger types

Page 6: Making Swift even safer

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

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

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

extension TaggedValueType where ValueType: JSONEncoding {

func encodeJSON() -> AnyObject { return self.taggedValue.value.encodeJSON() }}

Phantom types

Page 10: Making Swift even safer

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

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

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

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

Changing code

Page 15: Making Swift even safer

Components

Page 16: Making Swift even safer

Context

App

Page 17: Making Swift even safer

ContextMay contain dirty things dealing with global state

Page 18: Making Swift even safer

ContextMay contain dirty things dealing with global state

Unit tests

Page 19: Making Swift even safer

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

App

Pure state machine

Page 21: Making Swift even safer

App

Pure state machine

- Unit tests- Integrated acceptance tests

Page 22: Making Swift even safer

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
Page 24: Making Swift even safer

View: screenshot testing

Page 25: Making Swift even safer

View: screenshot testing

Page 26: Making Swift even safer

View: screenshot testing

Page 27: Making Swift even safer

Detecting & investigating bugs in the field

- smart assertions- diligent logging- daily duty

Page 28: Making Swift even safer

junoAssert

in Debug - crash 🙀in Release - log.error(), trackNonFatal() and recover 🙏

Page 29: Making Swift even safer

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

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

Logging

Page 32: Making Swift even safer

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

But it brings satisfaction

Page 34: Making Swift even safer

Thank you