swift - qconsp · swift: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e...

46
SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado

Upload: others

Post on 27-May-2020

2 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado

SWIFT:um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado

Page 2: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado
Page 3: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado
Page 4: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado
Page 5: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado
Page 6: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado
Page 7: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado

NA PRÁTICA

Page 8: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado

ENTIDADESstruct Ticket: Equatable { typealias Id = Tagged<Ticket, Int> typealias Participant = (firstName: String, lastName: String)

let identifier: Id let kind: String let price: NSDecimalNumber let participant: Participant

static func == (lhs: Ticket, rhs: Ticket) -> Bool { return lhs.identifier == rhs.identifier && lhs.kind == rhs.kind && lhs.price == rhs.price && lhs.participant == rhs.participant }}

Page 9: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado

MODELOS DE ESTADOenum OrderListState: Equatable { case loading case refreshing case loaded(data: [Order]) case failed(error: OrderListError)

static func == (lhs: OrderListState, rhs: OrderListState) -> Bool { switch (lhs, rhs) { case (.loading, .loading): return true case (.refreshing, .refreshing): return true case let (.loaded(lhsData), .loaded(lhsData)): return lhsData == lhsData case let (.failed(lhsError), .failed(rhsError)): return lhsError == rhsError default: return false } }}

Page 10: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado

INTERAÇÕES DE TELAenum OrderListAction: Action, Equatable { case startLoading case startRefreshing case load(data: [Order]) case append(data: [Order]) case fail(error: OrderListError)

static func == (lhs: OrderListAction, rhs: OrderListAction) -> Bool { // ... }}

Page 11: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado

TRANSIÇÕES DE ESTADOlet orderListReducer = Reducer<OrderListState, Action> { state, action in switch action { case OrderListAction.startLoading: return .loading case OrderListAction.load(let data): return .loaded(data: data) case OrderListAction.append(let data): if case .loaded(let previousData) = state { return .loaded(data: previousData + data) } else { return state } case OrderListAction.fail(let error): return .failed(error: error) default: return state }}

Page 12: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado

FLUXO DE DADO UNDIRECIONALstruct ApplicationState { // ...

let orderListState: OrderListState

// ...}

let applicationStore = Store<ApplicationState>( // A assinatura de `orderListReducer` precisa ser "promovida" // a Reducer<ApplicationState, Action> reducer: Reducer.combine( orderListReducer.lift(state: \ApplicationState.orderListState), // ... ))

// ...

let disposable = applicationStore.subscribe { applicationState in // ...}

Page 13: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado

TELAS DERIVADAS DO ESTADOclass OrderListViewController: UIViewController { // ... override func viewDidLoad() { super.viewDidLoad()

let disposable = applicationStore.subscribe { applicationState in self.orderListView.render(state: applicationState.orderListState) }

disposeBag.add(disposable) } // ...}

Page 14: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado

O QUE GANHAMOS COM ISSO?• Produ'vidade;

• Testabilidade;

• Previsibilidade;

Page 15: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado

INTEROPERABILIDADE

Page 16: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado

INTEROPERABILIDADE@interface ParticipantsSync : NSObject

@property (nonatomic, strong, readonly) NSArray<ParticipantChangeNew *> * _Nonnull participantsNew;@property (nonatomic, strong, readonly) NSArray<ParticipantChangeCancelled *> * _Nonnull participantsCancelled;@property (nonatomic, strong, readonly) NSArray<ParticipantChangeCheckIn *> * _Nonnull participantsCheckIn;@property (nonatomic, strong, readonly) NSArray<ParticipantChangeCheckOut *> * _Nonnull participantsCheckOut;@property (nonatomic, copy, readonly) NSDate * _Nonnull syncDate;

- (instancetype _Nullable)initWithJsonData:(NSData * _Nonnull)data;

@end

@interface ParticipantChangeNew : NSObject

@property (nonatomic, copy, readonly) NSString * _Nonnull ticketNumber;@property (nonatomic, copy, readonly) NSString * _Nonnull ticketTypeId;@property (nonatomic, copy, readonly) NSString * _Nonnull firstName;@property (nonatomic, copy, readonly) NSString * _Nullable lastName;@property (nonatomic, copy, readonly) NSString * _Nonnull email;@property (nonatomic, assign, readonly) BOOL checkIn;@property (nonatomic, copy, readonly) NSNumber * _Nullable time;

- (instancetype _Nullable)initWithDictionary:(NSDictionary * _Nonnull)dictionary;

@end

Page 17: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado

INTEROPERABILIDADEclass ParticipantSyncTests: XCTestCase { func test() { // ...

let sync = ParticipantsSync(jsonData: jsonData)

for change in sync.participantsNew { expect(change.checkIn).to(beTrue()) expect(change.time) > 1485956221 }

let firstParticipant = sync.participantsNew[0] expect(participant).toNot(beNil()) expect(participant!.ticketNumber).to(equal("PMHYYCAJ1X")) expect(participant!.ticketTypeId).to(equal("281600")) expect(participant!.firstName).to(equal("Guilherme")) expect(participant!.lastName).to(equal("De Andrade")) expect(participant!.checkIn).to(equal(false)) expect(participant!.time).to(beNil())

// ... }}

Page 18: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado

INTEROPERABILIDADE// Como é em Cstruct CGRect { CGPoint origin; CGSize size;};typedef struct CGRect CGRect;

Page 19: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado

INTEROPERABILIDADE// Como é importado em Swift (automaticamente)struct CGRect { var origin: CGPoint var size: CGSize

init() init(origin: CGPoint, size: CGSize)}

Page 20: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado

INTEROPERABILIDADE// Como é em CCGRect CGRectMake(CGFloat x, CGFloat y, CGFloat width, CGFloat height) CF_SWIFT_NAME(CGRect.init(x:y:width:height:));BOOL CGRectContains(CGRect r1, CGRect r2) CF_SWIFT_NAME(CGRect.contains(self:_));CGRect CGRectIntersection(CGRect r1, CGRect r2) CF_SWIFT_NAME(CGRect.intersection(self:_));

Page 21: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado

INTEROPERABILIDADE// Como é em CCGRect CGRectMake(CGFloat x, CGFloat y, CGFloat width, CGFloat height) CF_SWIFT_NAME(CGRect.init(x:y:width:height:));BOOL CGRectContains(CGRect r1, CGRect r2) CF_SWIFT_NAME(CGRect.contains(self:_));CGRect CGRectIntersection(CGRect r1, CGRect r2) CF_SWIFT_NAME(CGRect.intersection(self:_));

// Como é importado em Swiftextension CGRect { init (origin: CGPoint, size: CGSize) func contains(_ r2: CGRect) -> Bool func intersection(_ r2: CGRect) -> CGRect}

Page 22: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado

TYPE SAFETY

Page 23: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado

OPTIONALS

O #po Op#onal<T> é a representação em Swi7 da possibilidade de ausência de valor.

var optionalInt: Int? // Optional<Int>

Page 24: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado

OPTIONALS

Variáveis do ,po Int? podem receber valores do ,po Int mas também podem receber um valor especial nil que representa a ausência de valor.

optionalInt = nil // .noneoptionalInt = 1 // .some(1)

Page 25: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado

OPTIONALS

Os $pos Int? e Int estão relacionados, mas são dis$ntos para o compilador. Não é possível combinar diretamente valores de $po Int e $po Int? usando operadores built-in da linguagem, por exemplo, pois eles trabalham com valores de $pos iguais.

let nonOptionalInt: Int = 5

// Value of optional type 'Int?' not unwrapped; did you mean to use '!' or '?'?print(optionalInt + nonOptionalInt)

Page 26: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado

OPTIONALSUm valor do *po Int? pode ser interpretado como um valor do *po Int com um contexto semân*co adicional: a possibilidade de ausência. Para somar um valor do *po Int? a um valor do *po Int, o programador precisar "extrair" um valor do *po Int de dentro do contexto.

// Extração seguraif let safelyUnwrappedInt = optionalInt { print(safelyUnwrappedInt + nonOptionalInt)}

// Extração insegura e perigosalet explictlyUnwrappedInt = optionalInt!print(explictlyUnwrappedInt + nonOptionalInt)

Page 27: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado

OPTIONALSO #po Event da camada de modelo do app iOS da Sympla tem um campo image do #po URL?, pois alguns eventos não possuem imagem associada.

Na primeira do app, escrita em Objec2ve-C, este 2po foi definido sem verificações do compilador para a ausência do valor image em algumas instâncias de Event.

@interface Event: NSObject

// ...

@property (nonatomic, strong, readonly) NSString *name;@property (nonatomic, strong, readonly) NSURL * _Nonnull image;

// ...

@end

Page 28: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado

OPTIONALSA anotação _Nonnull é um hint para o compilador (e para programadores) que o valor pode estar ausente, mas esta ausência não é verificada formalmente em tempo de compilação.

@implementation EventTableViewCell

// ...

- (void) render:(Event *)event { // Possível bug [self.imageView setImageWithURL:event.image];}

// ...

@end

A primeira versão deste código não verificava se o campo event.image con4nha algum valor antes de ser usado. Neste caso, código não contemplou o requisito de negócio de mostrar um placeholder para eventos sem imagem.

Page 29: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado

OPTIONALSstruct Event { // ...

let title: String let image: URL?

// ...}

class EventTableViewCell: UITableViewCell { // ...

func render(event: Event) { // Erro de compilação! imageView.setImage(url: event.image) }

// ...}

Page 30: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado

TIPOS ALGÉBRICOS

Page 31: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado

TIPOS ALGÉBRICOS/* Structs são product types

|DateRange| = |DateRangeIdentifier| * |Date| * |Calendar| = 6 * |Date| * |Calendar|*/struct DateRange { private let identifier: DateRangeIdentifier private let base: Date private let calendar: Calendar}

// |DateRangeIdentifier| = 6enum DateRangeIdentifier { case today case tomorrow case thisWeek case thisWeekend case nextWeek case thisMonth}

Page 32: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado

TIPOS ALGÉBRICOS/* Enums são sum types

|FormFieldStatus| = 1 + |String?| + 1 = 1 + (1 + |String|) + 1 = 3 + |String|*/enum FormFieldStatus { case idle case invalid(message: String?) case valid}

// Lembrando que:typealias String? = Optional<String>

enum Optional<T> { case none case some(T)}

Page 33: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado

TIPOS ALGÉBRICOS// |SearchResultsState| = |[Event]| * |Bool| * |Error?| = |[Event]| * 2 * (1 + |Error|)struct SearchResultsState { let data: [Event] let isLoading: Bool let error: Error?}

Page 34: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado

TIPOS ALGÉBRICOSfunc render(state: SearchResultsState) { // ?}

• Sempre que o array de eventos for vazio, deve-se mostrar uma mensagem?

• Se o array não está vazio mas dados sendo carregados, o indicador de a<vidade tem precedência sobre a mensagem?

• A mensagem de erro deve ser exibida sempre que o carregamento falha ou somente se o array de eventos está vazio?

Page 35: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado

TIPOS ALGÉBRICOS// |SearchResultsState| = 1 + |[Event]| + |Error|// = 1 + |[]| + |[x:xs]| + |Error|enum SearchResultsState { case loading case loaded(data: [Event]) case failed(error: Error)}

func render(state: SearchResultsState) { switch state { case .loading: renderLoading() case .loaded(let data) where data.isEmpty: renderEmpty() case .loaded(let data): renderLoaded(events: data) case .failed(let error): renderFailed(error: error) }}

Page 36: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado

PHANTOM TYPES

Page 37: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado

PHANTOM TYPESstruct Tagged<Tag, RawValue> { let rawValue: RawValue

init (_ rawValue: RawValue) { self.rawValue = rawValue }}

struct Order { typealias Id = Tagged<Order, Int>

let identifier: Id let purchaseDate: Date let tickets: [Ticket] // ...}

Page 38: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado

PHANTOM TYPESstruct GetEditableParticipants: HTTPResource { private let orderIdentifier: Order.Id

init (orderIdentifier: Order.Id) { self.orderIdentifier = orderIdentifier }

var method: HTTPMethod { return .GET }

var parameters: [String: Any] { return [ "order": orderIdentifier.rawValue ] }

// ...}

Page 39: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado

PHANTOM TYPESvar editableParticipantsRequest: GetEditableParticipantslet order: Order = /* ... */let ticket: Ticket = /* ... */

/* error: cannot convert value of type 'Ticket.Id' (aka 'Tagged<Ticket, Int>') to expected argument type 'Order.Id' (aka 'Tagged<Order, Int>') */editableParticipants = GetEditableParticipants( orderIdentifier: ticket.identifier)

// OKeditableParticipants = GetEditableParticipants( orderIdentifier: order.identifier)

Page 40: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado

ALÉM DA APPLE

Page 41: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado

CLI#!/usr/bin/swift

import Foundationimport Socket

do { guard let serverSocket = try Socket.create(family: .inet6) else { fatalError("Impossible to create server socket") }

defer { serverSocket.close() }

guard let portArgument = CommandLine.arguments[1], let port = Int(portArgument) else { fatalError("\(portArgument) is not a valid port") }

try serverSocket.listen(port: port) print("Listening to port \(port)...")

let clientSocket = try serverSocket.acceptClientConnection() print("Accepted connection from: \(clientSocket.remoteHostname)")

defer { clientSocket.close() }

try clientSocket.write(from: "Hello, world!")} catch let error { print("Caught unexpected error: \(error.debugDescription)")}

Page 42: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado

PACOTESimport PostgreSQL

let database = PostgreSQL.Database( hostname: "localhost", database: "test", user: "root", password: "")

let results = try database.execute("SELECT * FROM users WHERE age >= $1", [.int(21)])

for result in results { print(result["first_name"]?.string) print(result["age"]?.int)}

Page 43: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado

APLICAÇÕES BACKEND// IBM Kitura

let router = Router()

router.get("/api/todos/:id") { request, response, next in guard let userId = request.parameters["id"] else { response.status(.badRequest) return }

todosRepository.get(userId: userId) { result in switch result { case .success(let todos): try response.status(.ok).send(json: JSON(todos.toDictionary())).end() case .error(let error): try response.status(.badRequest).end() Log.error(error.debugDescription) } }}

Kitura.addHTTPServer(onPort: 8080, with: router)Kitura.run()

Page 44: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado

CONCLUSÃO

Page 45: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado

PERGUNTAS

Page 46: SWIFT - QConSP · SWIFT: um equilíbrio sofis.cado entre segurança, ergonomia, produ.vidade e preservação de legado

OBRIGADO

Fellipe Caetano

! @fellipecaetano_"

@fellipecaetano

[email protected]