SlideShare une entreprise Scribd logo
1  sur  154
Télécharger pour lire hors ligne
Переход от Objective-c
на swift от идеи до
реализации
Кто мы?
• Superjob - сервис рекрутмента
Кто мы?
• Superjob - сервис рекрутмента
• 20 000 000 - резюме опубликовано
Кто мы?
• Superjob - сервис рекрутмента
• 20 000 000 - резюме опубликовано
• 1 000 000 - дневная аудитория портала
Кто мы?
• Superjob - сервис рекрутмента
• 20 000 000 - резюме опубликовано
• 1 000 000 - дневная аудитория портала
• 1 000 000 - приглашений на собеседования
Кто мы?
Приложения
Приложения
Приложения
Приложения
Зачем решили переходить?
• Более предсказуемый код
Зачем решили переходить?
• Более предсказуемый код
• Неизменяемость коллекций, свойств
Зачем решили переходить?
• Более предсказуемый код
• Неизменяемость коллекций, свойств
• Optionals
Модель фильтра
Модель фильтра
• > 20 @property
Модель фильтра
• > 20 @property
• реализация isEqual
Модель фильтра
• > 20 @property
• реализация isEqual
• много мест из которых возможны

изменения
Зачем решили переходить?
• Более предсказуемый код
• Неизменяемость коллекций, свойств
• Optionals
Зачем решили переходить?
• Более предсказуемый код
• Неизменяемость коллекций, свойств
• Optionals
• Protocol Oriented Programming
Зачем решили переходить?
• Более предсказуемый код
• Неизменяемость коллекций, свойств
• Optionals
• Protocol Oriented Programming
• Длина методов 

- (UITableViewCell *)tableView:(UITableView *)tableView 

cellForRowAtIndexPath:(NSIndexPath *)indexPath
Зачем решили переходить?
• Более предсказуемый код
• Заблаговременное мигрирование с objc библиотек
Зачем решили переходить?
• Более предсказуемый код
• Заблаговременное мигрирование с objc библиотек
• Новые библиотеки и UI компоненты пишутся на Swift
Зачем решили переходить?
• Более предсказуемый код
• Заблаговременное мигрирование с objc библиотек
• Новые библиотеки и UI компоненты пишутся на Swift
• Старые Objc библиотеки не поддерживаются
Зачем решили переходить?
• Более предсказуемый код
• Заблаговременное мигрирование с objc библиотек
• Новые библиотеки и UI компоненты пишутся на Swift
• Старые Objc библиотеки не поддерживаются
NOTE: This is legacy introduction to the Objective-C ReactiveCocoa,
pod 'ReactiveCocoa', '~> 2.5.0'
Зачем решили переходить?
• Более предсказуемый код
• Заблаговременное мигрирование с objc библиотек
• Поиск новых сотрудников
Зачем решили переходить?
• Более предсказуемый код
• Заблаговременное мигрирование с objc библиотек
• Поиск новых сотрудников
• 70% собеседуемых хотели бы писать на swift
Зачем решили переходить?
• Более предсказуемый код
• Заблаговременное мигрирование с objc библиотек
• Поиск новых сотрудников
• 70% собеседуемых хотели бы писать на swift
• После перехода на swift удалось найти
Казалось бы
Но не так все просто
• Бизнес хочет постоянную поставку новых фич
Но не так все просто
• Бизнес хочет постоянную поставку новых фич
• поддержка того же уровня crash-free
Но не так все просто
• Бизнес хочет постоянную поставку новых фич
• поддержка того же уровня crash-free
• такая же скорость разработки
Но не так все просто
• Бизнес хочет постоянную поставку новых фич
• Приложения имею долгую историю
Но не так все просто
• Бизнес хочет постоянную поставку новых фич
• Приложения имею долгую историю
• 5 лет разработки
Но не так все просто
• Бизнес хочет постоянную поставку новых фич
• Приложения имею долгую историю
• 5 лет разработки
• разные архитектурные патерны
Но не так все просто
• Бизнес хочет постоянную поставку новых фич
• Приложения имею долгую историю
• Хочется избежать кучи “граблей”, чтобы потом
все снова не переписывать
Но не так все просто
• Бизнес хочет постоянную поставку новых фич
• Приложения имею долгую историю
• Хочется избежать кучи “граблей”, чтобы потом
все снова не переписывать
• архитектура swift ориентированная
Но не так все просто
• Бизнес хочет постоянную поставку новых фич
• Приложения имею долгую историю
• Хочется избежать кучи “граблей”, чтобы потом
все снова не переписывать
• архитектура swift ориентированная
• тестируемость
Но не так все просто
• Бизнес хочет постоянную поставку новых фич
• Приложения имею долгую историю
• Хочется избежать кучи “граблей”, чтобы потом
все снова не переписывать
• архитектура swift ориентированная
• тестируемость
• кодстай и т.д
Сели, подумали и сделали
• Много проблем с nullability
Nullability
current=`find . -type f -name "*.h" -exec grep -il NS_ASSUME_NONULL '{}' ; | wc -l`
all=`find . -type f -name "*.h" | wc -l`
percent=$(( $current * 100 ))
expr $percent / $all
Как оценить насколько все плохо?
Nullability
NS_ASSUME_NONNULL_BEGIN
NS_ASSUME_NONNULL_END
current=`find . -type f -name "*.h" -exec grep -il NS_ASSUME_NONULL '{}' ; | wc -l`
all=`find . -type f -name "*.h" | wc -l`
percent=$(( $current * 100 ))
expr $percent / $all
Как оценить насколько все плохо?
Nullability
NS_ASSUME_NONNULL_BEGIN
NS_ASSUME_NONNULL_END
~5%
current=`find . -type f -name "*.h" -exec grep -il NS_ASSUME_NONULL '{}' ; | wc -l`
all=`find . -type f -name "*.h" | wc -l`
percent=$(( $current * 100 ))
expr $percent / $all
Как оценить насколько все плохо?
Nullability
NS_ASSUME_NONNULL_BEGIN
NS_ASSUME_NONNULL_END
~5%
current=`find . -type f -name "*.h" -exec grep -il NS_ASSUME_NONULL '{}' ; | wc -l`
all=`find . -type f -name "*.h" | wc -l`
percent=$(( $current * 100 ))
expr $percent / $all
Как оценить насколько все плохо?
Nullability
Как улучшить показатель?
Nullability
• 3 месяца на каждый измененный файл добавляем Nullability
• Скрипт который не дает пройти PullRequest без меток :)
Как улучшить показатель?
Nullability
• 3 месяца на каждый измененный файл добавляем Nullability
• Скрипт который не дает пройти PullRequest без меток :)
60%
Как улучшить показатель?
Сели, подумали и сделали
• Много проблем с nullability
Сели, подумали и сделали
• Много проблем с nullability
• Проблемы со сторонними библиотеками
СocoaPods
СocoaPods
• Просто заменить
• Masonry -> SnapKit
• AFNetworking - Alamofire
СocoaPods
• Просто заменить
• Masonry -> SnapKit
• AFNetworking - Alamofire ✓
✓
СocoaPods
• Просто заменить
• Masonry -> SnapKit
• AFNetworking - Alamofire ✓
?
✓
СocoaPods
• Просто заменить
• Masonry -> SnapKit
• AFNetworking - Alamofire ✓
?
Objection ?
Mantle ?
Specta?
Expecta?
✓
Проблемы
profileFacade.authorize(withLogin: login,
password: pass).doNext(block: ((Any?) -> Void)!)
Проблемы
1. Нет понятия, что получаешь
profileFacade.authorize(withLogin: login,
password: pass).doNext(block: ((Any?) -> Void)!)
Проблемы
1. Нет понятия, что получаешь
profileFacade.authorize(withLogin: login,
password: pass).doNext(block: ((Any?) -> Void)!)
profileFacade
.authorize(withLogin: login, password: pass)
.subscribeNext { (response) in
if let user = response as? SJAProfileModel {
print("(String(describing: user.name))")
}
}
Проблемы
1. Нет понятия, что получаешь
2. Необходимо каждый раз “кастить”
profileFacade.authorize(withLogin: login,
password: pass).doNext(block: ((Any?) -> Void)!)
profileFacade
.authorize(withLogin: login, password: pass)
.subscribeNext { (response) in
if let user = response as? SJAProfileModel {
print("(String(describing: user.name))")
}
}
Проблемы
1. Нет понятия, что получаешь
2. Необходимо каждый раз “кастить”
Критерии для решения
• Хочется, чтобы можно было легко пользоваться
Критерии для решения
• Хочется, чтобы можно было легко пользоваться
• Чтобы была строгая типизация
Критерии для решения
• Хочется, чтобы можно было легко пользоваться
• Чтобы была строгая типизация
• Swift like API
Критерии для решения
• Хочется, чтобы можно было легко пользоваться
• Чтобы была строгая типизация
• Swift like API
Критерии для решения
• Хочется, чтобы можно было легко пользоваться
• Чтобы была строгая типизация
• Swift like API
Критерии для решения
• Хочется, чтобы можно было легко пользоваться
• Чтобы была строгая типизация
• Swift like API
Критерии для решения
?
• Хочется, чтобы можно было легко пользоваться
• Чтобы была строгая типизация
• Swift like API
Критерии для решения
extension RACSignal {
private func rxMapBody<T>(convertBlock: @escaping (Any?) -> T?) -> Observable<T> {
return Observable.create() { observer in
self.subscribeNext(
{ anyValue in
if let converted = convertBlock(anyValue) {
observer.onNext(converted)
} else {
observer.onError(RxCastError.cannotConvertTypes)
}
},
...
...
...
return Disposables.create() {
}
}
}
+1. Подписываемся на новые значения RACSignal
extension RACSignal {
private func rxMapBody<T>(convertBlock: @escaping (Any?) -> T?) -> Observable<T> {
return Observable.create() { observer in
self.subscribeNext(
{ anyValue in
if let converted = convertBlock(anyValue) {
observer.onNext(converted)
} else {
observer.onError(RxCastError.cannotConvertTypes)
}
},
...
...
...
return Disposables.create() {
}
}
}
+1. Подписываемся на новые значения RACSignal
extension RACSignal {
private func rxMapBody<T>(convertBlock: @escaping (Any?) -> T?) -> Observable<T> {
return Observable.create() { observer in
self.subscribeNext(
{ anyValue in
if let converted = convertBlock(anyValue) {
observer.onNext(converted)
} else {
observer.onError(RxCastError.cannotConvertTypes)
}
},
...
...
...
return Disposables.create() {
}
}
}
+1. Подписываемся на новые значения RACSignal
extension RACSignal {
private func rxMapBody<T>(convertBlock: @escaping (Any?) -> T?) -> Observable<T> {
return Observable.create() { observer in
self.subscribeNext(
{ anyValue in
if let converted = convertBlock(anyValue) {
observer.onNext(converted)
} else {
observer.onError(RxCastError.cannotConvertTypes)
}
},
...
...
...
return Disposables.create() {
}
}
}
+1. Подписываемся на новые значения RACSignal
extension RACSignal {
private func rxMapBody<T>(convertBlock: @escaping (Any?) -> T?) -> Observable<T> {
return Observable.create() { observer in
self.subscribeNext(
{ anyValue in
if let converted = convertBlock(anyValue) {
observer.onNext(converted)
} else {
observer.onError(RxCastError.cannotConvertTypes)
}
},
...
...
...
return Disposables.create() {
}
}
}
+1. Подписываемся на новые значения RACSignal
extension RACSignal {
private func rxMapBody<T>(convertBlock: @escaping (Any?) -> T?) -> Observable<T> {
return Observable.create() { observer in
self.subscribeNext(
{ anyValue in
if let converted = convertBlock(anyValue) {
observer.onNext(converted)
} else {
observer.onError(RxCastError.cannotConvertTypes)
}
},
...
...
...
return Disposables.create() {
}
}
}
+1. Подписываемся на новые значения RACSignal
extension RACSignal {
private func rxMapBody<T>(convertBlock: @escaping (Any?) -> T?) -> Observable<T> {
return Observable.create() { observer in
self.subscribeNext(
{ anyValue in
if let converted = convertBlock(anyValue) {
observer.onNext(converted)
} else {
observer.onError(RxCastError.cannotConvertTypes)
}
},
...
...
...
return Disposables.create() {
}
}
}
+1. Подписываемся на новые значения RACSignal
extension RACSignal {
public func rxMap<T>(_ type: T.Type = T.self) -> Observable<T> {
return rxMapBody() { anyValue in
if let value: T = rx_cast(anyValue) {
return value
} else {
return nil
}
}
}
}
+2. Приводим не типизированное к типизированному
extension RACSignal {
public func rxMap<T>(_ type: T.Type = T.self) -> Observable<T> {
return rxMapBody() { anyValue in
if let value: T = rx_cast(anyValue) {
return value
} else {
return nil
}
}
}
}
+2. Приводим не типизированное к типизированному
extension RACSignal {
public func rxMap<T>(_ type: T.Type = T.self) -> Observable<T> {
return rxMapBody() { anyValue in
if let value: T = rx_cast(anyValue) {
return value
} else {
return nil
}
}
}
}
+2. Приводим не типизированное к типизированному
extension RACSignal {
public func rxMap<T>(_ type: T.Type = T.self) -> Observable<T> {
return rxMapBody() { anyValue in
if let value: T = rx_cast(anyValue) {
return value
} else {
return nil
}
}
}
}
+2. Приводим не типизированное к типизированному
extension RACSignal {
public func rxMap<T>(_ type: T.Type = T.self) -> Observable<T> {
return rxMapBody() { anyValue in
if let value: T = rx_cast(anyValue) {
return value
} else {
return nil
}
}
}
}
+2. Приводим не типизированное к типизированному
+
internal func rx_cast<T>(_ value: Any?) -> T? {
if let v = value as? T {
return v
} else if let E = T.self as? ExpressibleByNilLiteral.Type {
return E.init(nilLiteral: ()) as? T
}
return nil
}
3. “Кастим” к нужному типу T
+
internal func rx_cast<T>(_ value: Any?) -> T? {
if let v = value as? T {
return v
} else if let E = T.self as? ExpressibleByNilLiteral.Type {
return E.init(nilLiteral: ()) as? T
}
return nil
}
3. “Кастим” к нужному типу T
+
internal func rx_cast<T>(_ value: Any?) -> T? {
if let v = value as? T {
return v
} else if let E = T.self as? ExpressibleByNilLiteral.Type {
return E.init(nilLiteral: ()) as? T
}
return nil
}
3. “Кастим” к нужному типу T
+
internal func rx_cast<T>(_ value: Any?) -> T? {
if let v = value as? T {
return v
} else if let E = T.self as? ExpressibleByNilLiteral.Type {
return E.init(nilLiteral: ()) as? T
}
return nil
}
3. “Кастим” к нужному типу T
+
internal func rx_cast<T>(_ value: Any?) -> T? {
if let v = value as? T {
return v
} else if let E = T.self as? ExpressibleByNilLiteral.Type {
return E.init(nilLiteral: ()) as? T
}
return nil
}
3. “Кастим” к нужному типу T
profileFacade
.authorize(withLogin: login, password: pass)
.rxMap(SJAProfileModel.self)
.subscribe(onNext: { (user) in
})
.addDisposableTo(disposeBag)
+ =
• Получилось
profileFacade
.authorize(withLogin: login, password: pass)
.rxMap(SJAProfileModel.self)
.subscribe(onNext: { (user) in
})
.addDisposableTo(disposeBag)
+ =
• Получилось
extension SJAProfileFacade {
func authorize(login: String, passwrod: String) -> Observable<SJAProfileModel> {
return self.authorize(withLogin: login, password: passwrod).rxMap()
}
}
• Можно лучше
+ =
extension SJAProfileFacade {
func authorize(login: String, passwrod: String) -> Observable<SJAProfileModel> {
return self.authorize(withLogin: login, password: passwrod).rxMap()
}
}
• Можно лучше
+ =
extension SJAProfileFacade {
func authorize(login: String, passwrod: String) -> Observable<SJAProfileModel> {
return self.authorize(withLogin: login, password: passwrod).rxMap()
}
}
• Можно лучше
+ =
extension SJAProfileFacade {
func authorize(login: String, passwrod: String) -> Observable<SJAProfileModel> {
return self.authorize(withLogin: login, password: passwrod).rxMap()
}
}
• Можно лучше
+ =
profileFacade
.authorize(login: login, passwrod: pass)
.subscribe(onNext: { (user) in
})
.addDisposableTo(disposeBag)
Сели, подумали и сделали
• Много проблем с nullability
• Проблемы со сторонними библиотеками
Сели, подумали и сделали
• Много проблем с nullability
• Проблемы со сторонними библиотеками
• Не все возможности swift доступны в Objective-c
Не все возможности Swift доступны в
Objective-c
Не все возможности Swift доступны в
Objective-c
• struct
Не все возможности Swift доступны в
Objective-c
• struct • enum
Не все возможности Swift доступны в
Objective-c
• struct • enum • моки для тестов
Не все возможности Swift доступны в
Objective-c
• struct • enum • моки для тестов
?
Не все возможности Swift доступны в
Objective-c
• struct • enum • моки для тестов
Sourcery
https://goo.gl/qgnH9D
?
Sourcery scans your source code, applies your personal
templates and generates Swift code for you, allowing you to use
meta-programming techniques to save time and decrease
potential mistakes.
Sourcery scans your source code, applies your personal
templates and generates Swift code for you, allowing you to use
meta-programming techniques to save time and decrease
potential mistakes.
• equatable/hashable
• NSCoding
• JSON serialization
Автогенерация
struct Resume:
AutoHashable,
AutoEquatable {
var key: Int?
let name: String?
var firstName: String?
var lastName: String?
var middleName: String?
var birthDate: Date?
}
extension Resume: Hashable {
internal var hashValue: Int {
return combineHashes([key?.hashValue ?? 0,
name?.hashValue ?? 0,
firstName?.hashValue ?? 0,
lastName?.hashValue ?? 0,
middleName?.hashValue ?? 0,
birthDate?.hashValue ?? 0, 0])
}
}
• Hashable
struct Resume:
AutoHashable,
AutoEquatable {
var key: Int?
let name: String?
var firstName: String?
var lastName: String?
var middleName: String?
var birthDate: Date?
}
extension Resume: Hashable {
internal var hashValue: Int {
return combineHashes([key?.hashValue ?? 0,
name?.hashValue ?? 0,
firstName?.hashValue ?? 0,
lastName?.hashValue ?? 0,
middleName?.hashValue ?? 0,
birthDate?.hashValue ?? 0, 0])
}
}
• Hashable
struct Resume:
AutoHashable,
AutoEquatable {
var key: Int?
let name: String?
var firstName: String?
var lastName: String?
var middleName: String?
var birthDate: Date?
}
extension Resume: Equatable {}
internal func == (lhs: Resume, rhs: Resume) -> Bool {
guard compareOptionals(lhs: lhs.key,
rhs: rhs.key,
compare: ==) else { return false }
guard compareOptionals(lhs: lhs.name,
rhs: rhs.name,
compare: ==) else { return false }
guard compareOptionals(lhs: lhs.firstName,
rhs: rhs.firstName,
compare: ==) else { return false }
...
...
return true
}
• Equtable
struct Resume:
AutoHashable,
AutoEquatable {
var key: Int?
let name: String?
var firstName: String?
var lastName: String?
var middleName: String?
var birthDate: Date?
}
extension Resume: Equatable {}
internal func == (lhs: Resume, rhs: Resume) -> Bool {
guard compareOptionals(lhs: lhs.key,
rhs: rhs.key,
compare: ==) else { return false }
guard compareOptionals(lhs: lhs.name,
rhs: rhs.name,
compare: ==) else { return false }
guard compareOptionals(lhs: lhs.firstName,
rhs: rhs.firstName,
compare: ==) else { return false }
...
...
return true
}
• Equtable
+
enum Conf {
case Apps
case Backend
case WebScale
}
• Пример
+
enum Conf {
case Apps
case Backend
case WebScale
}
+
• Пример
+
enum Conf {
case Apps
case Backend
case WebScale
}
+
{% for enum in types.enums %}
extension {{ enum.name }} {
static var numberOfCases:Int = {{ enum.cases.count}}
}
{% endfor %}
• Пример
+
enum Conf {
case Apps
case Backend
case WebScale
}
+
{% for enum in types.enums %}
extension {{ enum.name }} {
static var numberOfCases:Int = {{ enum.cases.count}}
}
{% endfor %}
• Пример
+
enum Conf {
case Apps
case Backend
case WebScale
}
+
{% for enum in types.enums %}
extension {{ enum.name }} {
static var numberOfCases:Int = {{ enum.cases.count}}
}
{% endfor %}
• Пример
+
enum Conf {
case Apps
case Backend
case WebScale
}
+
{% for enum in types.enums %}
extension {{ enum.name }} {
static var numberOfCases:Int = {{ enum.cases.count}}
}
{% endfor %}
• Пример
+
enum Conf {
case Apps
case Backend
case WebScale
}
+
=
{% for enum in types.enums %}
extension {{ enum.name }} {
static var numberOfCases:Int = {{ enum.cases.count}}
}
{% endfor %}
• Пример
+
enum Conf {
case Apps
case Backend
case WebScale
}
+
=
{% for enum in types.enums %}
extension {{ enum.name }} {
static var numberOfCases:Int = {{ enum.cases.count}}
}
{% endfor %}
extension Conf {
static var numberOfCases:Int = 3
}
• Пример
+
+
=
{% for enum in types.enums %}
extension {{ enum.name }} {
static var numberOfCases:Int = {{ enum.cases.count}}
}
{% endfor %}
extension Conf {
static var numberOfCases:Int = 3
}
enum Conf {
case Apps
case Backend
case WebScale
case Awesome
}
• Пример
+
+
=
{% for enum in types.enums %}
extension {{ enum.name }} {
static var numberOfCases:Int = {{ enum.cases.count}}
}
{% endfor %}
enum Conf {
case Apps
case Backend
case WebScale
case Awesome
}
extension Conf {
static var numberOfCases:Int = 4
}
• Пример
Sourcery
Как же это помогло нам?
Sourcery
Как же это помогло нам?
Struct
Sourcery
Как же это помогло нам?
Struct Objective-c
struct Resume:
AutoHashable,
AutoEquatable,
AutoObjc {
var key: Int?
let name: String?
var firstName: String?
var lastName: String?
var middleName: String?
var birthDate: Date?
}
Sourcery
Как же это помогло нам?
Struct Objective-c
struct Resume:
AutoHashable,
AutoEquatable,
AutoObjc {
var key: Int?
let name: String?
var firstName: String?
var lastName: String?
var middleName: String?
var birthDate: Date?
}
Sourcery
Как же это помогло нам?
Struct Objective-c
struct Resume:
AutoHashable,
AutoEquatable,
AutoObjc {
var key: Int?
let name: String?
var firstName: String?
var lastName: String?
var middleName: String?
var birthDate: Date?
}
Sourcery
Как же это помогло нам?
Struct Objective-c
class ResumeObjc: NSObject {
private(set) var key: Int?
private(set) var name: String?
private(set) var firstName: String?
private(set) var lastName: String?
private(set) var middleName: String?
private(set) var birthDate: Date?
}
struct Resume:
AutoHashable,
AutoEquatable,
AutoObjc {
var key: Int?
let name: String?
var firstName: String?
var lastName: String?
var middleName: String?
var birthDate: Date?
}
Sourcery
Как же это помогло нам?
Struct Objective-c
class ResumeObjc: NSObject {
private(set) var key: Int?
private(set) var name: String?
private(set) var firstName: String?
private(set) var lastName: String?
private(set) var middleName: String?
private(set) var birthDate: Date?
}
struct Resume:
AutoHashable,
AutoEquatable,
AutoObjc {
var key: Int?
let name: String?
var firstName: String?
var lastName: String?
var middleName: String?
var birthDate: Date?
}
Sourcery
Как же это помогло нам?
Struct Objective-c
class ResumeObjc: NSObject {
private(set) var key: Int?
private(set) var name: String?
private(set) var firstName: String?
private(set) var lastName: String?
private(set) var middleName: String?
private(set) var birthDate: Date?
}
struct Resume:
AutoHashable,
AutoEquatable,
AutoObjc {
var key: Int?
let name: String?
var firstName: String?
var lastName: String?
var middleName: String?
var birthDate: Date?
}
Sourcery
Как же это помогло нам?
Struct Objective-c
✓
class ResumeObjc: NSObject {
private(set) var key: Int?
private(set) var name: String?
private(set) var firstName: String?
private(set) var lastName: String?
private(set) var middleName: String?
private(set) var birthDate: Date?
}
SourceryДля тестов нужно самостоятельно
писать моки
protocol VacancyDetailViewModelDelegate:
class,
AutoMockable {
func vmReloadData(animated: Bool)
func vmShareItems(items: [Any])
}
SourceryДля тестов нужно самостоятельно
писать моки
protocol VacancyDetailViewModelDelegate:
class,
AutoMockable {
func vmReloadData(animated: Bool)
func vmShareItems(items: [Any])
}
SourceryДля тестов нужно самостоятельно
писать моки
protocol VacancyDetailViewModelDelegate:
class,
AutoMockable {
func vmReloadData(animated: Bool)
func vmShareItems(items: [Any])
}
SourceryДля тестов нужно самостоятельно
писать моки
protocol VacancyDetailViewModelDelegate:
class,
AutoMockable {
func vmReloadData(animated: Bool)
func vmShareItems(items: [Any])
}
SourceryДля тестов нужно самостоятельно
писать моки
class VacancyDetailViewModelDelegateMock:
VacancyDetailViewModelDelegate {
var vmReloadDataCalled = false
var vmReloadDataReceivedAnimated: Bool?
func vmReloadData(animated: Bool) {
vmReloadDataCalled = true
vmReloadDataReceivedAnimated = animated
}
var vmShareItemsCalled = false
var vmShareItemsReceivedItems: [Any]?
func vmShareItems(items: [Any]) {
vmShareItemsCalled = true
vmShareItemsReceivedItems = items
}
}
protocol VacancyDetailViewModelDelegate:
class,
AutoMockable {
func vmReloadData(animated: Bool)
func vmShareItems(items: [Any])
}
SourceryДля тестов нужно самостоятельно
писать моки
class VacancyDetailViewModelDelegateMock:
VacancyDetailViewModelDelegate {
var vmReloadDataCalled = false
var vmReloadDataReceivedAnimated: Bool?
func vmReloadData(animated: Bool) {
vmReloadDataCalled = true
vmReloadDataReceivedAnimated = animated
}
var vmShareItemsCalled = false
var vmShareItemsReceivedItems: [Any]?
func vmShareItems(items: [Any]) {
vmShareItemsCalled = true
vmShareItemsReceivedItems = items
}
}
Сели, подумали и сделали
• Много проблем с nullability
• Проблемы со сторонними библиотеками
• Не все возможности swift доступны в Objective-c
Сели, подумали и сделали
• Много проблем с nullability
• Проблемы со сторонними библиотеками
• Не все возможности swift доступны в Objective-c
• Организационные моменты внутри команды
SwiftLint
A tool to enforce Swift style and conventions
SwiftLint
• Помог ввести одинаковый стиль на всю команду
A tool to enforce Swift style and conventions
SwiftLint
• Помог ввести одинаковый стиль на всю команду
A tool to enforce Swift style and conventions
SwiftLint
• Свои правила
SwiftLint
• Свои правила
SwiftLint
• Свои правила
SwiftGen
SwiftGen
• Assets Catalogs
SwiftGen
• Assets Catalogs
enum Asset: String {
case contactEmailIcon = "contactEmailIcon"
case contactFbIcon = "contactFbIcon"
case contactGitIcon = "contactGitIcon"
case contactLnIcon = "contactLnIcon"
case contactOKIcon = "contactOKIcon"
}
SwiftGen
• Assets Catalogs
enum Asset: String {
case contactEmailIcon = "contactEmailIcon"
case contactFbIcon = "contactFbIcon"
case contactGitIcon = "contactGitIcon"
case contactLnIcon = "contactLnIcon"
case contactOKIcon = "contactOKIcon"
}
let icon = Asset.contactFbIcon.image
SwiftGen
• Assets Catalogs
• Localizable.strings
SwiftGen
• Assets Catalogs
• Localizable.strings
let string = L10n.vacancyCellTitleExpiriens.string
SwiftGen
• Assets Catalogs
• Localizable.strings
let string = tr(.vacancyCellTitleExpiriens)
let string = L10n.vacancyCellTitleExpiriens.string
SwiftGen
• Assets Catalogs
• Localizable.strings
• UIStoryboards and their Scenes
let string = tr(.vacancyCellTitleExpiriens)
let string = L10n.vacancyCellTitleExpiriens.string
SwiftGen
• Assets Catalogs
• Localizable.strings
• UIStoryboards and their Scenes
• NSStoryboards and their Scenes
let string = tr(.vacancyCellTitleExpiriens)
let string = L10n.vacancyCellTitleExpiriens.string
SwiftGen
• Assets Catalogs
• Localizable.strings
• UIStoryboards and their Scenes
• NSStoryboards and their Scenes
• Colors
let string = tr(.vacancyCellTitleExpiriens)
let string = L10n.vacancyCellTitleExpiriens.string
SwiftGen
• Assets Catalogs
• Localizable.strings
• UIStoryboards and their Scenes
• NSStoryboards and their Scenes
• Colors
• Fonts
let string = tr(.vacancyCellTitleExpiriens)
let string = L10n.vacancyCellTitleExpiriens.string
Сели, подумали и сделали
• Много проблем с nullability
• Проблемы со сторонними библиотеками
• Не все возможности swift доступны в Objective-c
• Организационные моменты внутри команды
Сели, подумали и сделали
• Много проблем с nullability
• Проблемы со сторонними библиотеками
• Не все возможности swift доступны в Objective-c
• Организационные моменты внутри команды
✓
Сели, подумали и сделали
• Много проблем с nullability
• Проблемы со сторонними библиотеками
• Не все возможности swift доступны в Objective-c
• Организационные моменты внутри команды
✓
✓
Сели, подумали и сделали
• Много проблем с nullability
• Проблемы со сторонними библиотеками
• Не все возможности swift доступны в Objective-c
• Организационные моменты внутри команды
✓
✓
✓
Сели, подумали и сделали
• Много проблем с nullability
• Проблемы со сторонними библиотеками
• Не все возможности swift доступны в Objective-c
• Организационные моменты внутри команды
✓
✓
✓
✓
Что нам дал переход
на swift
• Статическая типизация дала более предсказуемое
поведение и меньше багов в бизнес логике
• Более быструю скорость разработки фич
• Наняли людей в команду
Спасибо
alekoleg@gmail.com
fb.com/alekoleg
RxSwift
Sourcery
SwiftLint
SwiftGen
https://goo.gl/qgnH9D
https://goo.gl/JZ4cvF
https://goo.gl/FwYs8s
https://goo.gl/yqSqQb

Contenu connexe

Tendances

DevOps-трансформация Альфа-Банка / Антон Исанин (Альфа-Банк)
DevOps-трансформация Альфа-Банка / Антон Исанин (Альфа-Банк)DevOps-трансформация Альфа-Банка / Антон Исанин (Альфа-Банк)
DevOps-трансформация Альфа-Банка / Антон Исанин (Альфа-Банк)Ontico
 
High load для начинающих
High load для начинающихHigh load для начинающих
High load для начинающихAndrew Minkin
 
Самые частые проблемы и пути решения при росте нагрузки и масштабировании про...
Самые частые проблемы и пути решения при росте нагрузки и масштабировании про...Самые частые проблемы и пути решения при росте нагрузки и масштабировании про...
Самые частые проблемы и пути решения при росте нагрузки и масштабировании про...Andrew Minkin
 
"Посмотрим на Акку-Джаву" Дмитрий Мантула
"Посмотрим на Акку-Джаву" Дмитрий Мантула"Посмотрим на Акку-Джаву" Дмитрий Мантула
"Посмотрим на Акку-Джаву" Дмитрий МантулаFwdays
 
Сергей Крапивенский
Сергей КрапивенскийСергей Крапивенский
Сергей КрапивенскийCodeFest
 
Максим Пугачев
Максим ПугачевМаксим Пугачев
Максим ПугачевCodeFest
 
Implement your own profiler with blackjack and fun
Implement your own profiler with blackjack and funImplement your own profiler with blackjack and fun
Implement your own profiler with blackjack and funVladimir Sitnikov
 
Трудовые будни инженера производительности
Трудовые будни инженера производительностиТрудовые будни инженера производительности
Трудовые будни инженера производительностиVladimir Sitnikov
 
Илья Кудинов «Развитие процессов тестирования в Badoo за три года, или как мы...
Илья Кудинов «Развитие процессов тестирования в Badoo за три года, или как мы...Илья Кудинов «Развитие процессов тестирования в Badoo за три года, или как мы...
Илья Кудинов «Развитие процессов тестирования в Badoo за три года, или как мы...WrikeTechClub
 
О фреймворках / Роман Ивлиев (Банки.ру)
О фреймворках / Роман Ивлиев (Банки.ру)О фреймворках / Роман Ивлиев (Банки.ру)
О фреймворках / Роман Ивлиев (Банки.ру)Ontico
 
UICov - инструмент анализа покрытия UI-тестами
UICov - инструмент анализа покрытия UI-тестамиUICov - инструмент анализа покрытия UI-тестами
UICov - инструмент анализа покрытия UI-тестамиSQALab
 
CodeFest 2011. Макаров А. — Как разрабатывается Yii
CodeFest 2011. Макаров А. — Как разрабатывается YiiCodeFest 2011. Макаров А. — Как разрабатывается Yii
CodeFest 2011. Макаров А. — Как разрабатывается YiiCodeFest
 
Тестирование и мониторинг производительности фронтенда с помощью sitespeed.io...
Тестирование и мониторинг производительности фронтенда с помощью sitespeed.io...Тестирование и мониторинг производительности фронтенда с помощью sitespeed.io...
Тестирование и мониторинг производительности фронтенда с помощью sitespeed.io...Alexander Nedeliaev
 
QA Fes 2016. Александр Неделяев. Система мониторинга производительности своим...
QA Fes 2016. Александр Неделяев. Система мониторинга производительности своим...QA Fes 2016. Александр Неделяев. Система мониторинга производительности своим...
QA Fes 2016. Александр Неделяев. Система мониторинга производительности своим...QAFest
 
как строить архитектуру для отказоустойчивой службы такси
как строить архитектуру для отказоустойчивой службы таксикак строить архитектуру для отказоустойчивой службы такси
как строить архитектуру для отказоустойчивой службы таксиAndrew Minkin
 
Эксплуатация container-based-инфраструктур / Николай Сивко (okmeter.io)
Эксплуатация container-based-инфраструктур / Николай Сивко (okmeter.io)Эксплуатация container-based-инфраструктур / Николай Сивко (okmeter.io)
Эксплуатация container-based-инфраструктур / Николай Сивко (okmeter.io)Ontico
 
Сергей Константинов (Яндекс)
Сергей Константинов (Яндекс)Сергей Константинов (Яндекс)
Сергей Константинов (Яндекс)Ontico
 
Браузерные помощники тестировщика (QA Fest 2016)
Браузерные помощники тестировщика (QA Fest 2016)Браузерные помощники тестировщика (QA Fest 2016)
Браузерные помощники тестировщика (QA Fest 2016)Alexander Nedeliaev
 
CodeFest 2012. Ивлиев Р. — Аккуратно переезжаем, или тестирование в проектах ...
CodeFest 2012. Ивлиев Р. — Аккуратно переезжаем, или тестирование в проектах ...CodeFest 2012. Ивлиев Р. — Аккуратно переезжаем, или тестирование в проектах ...
CodeFest 2012. Ивлиев Р. — Аккуратно переезжаем, или тестирование в проектах ...CodeFest
 

Tendances (19)

DevOps-трансформация Альфа-Банка / Антон Исанин (Альфа-Банк)
DevOps-трансформация Альфа-Банка / Антон Исанин (Альфа-Банк)DevOps-трансформация Альфа-Банка / Антон Исанин (Альфа-Банк)
DevOps-трансформация Альфа-Банка / Антон Исанин (Альфа-Банк)
 
High load для начинающих
High load для начинающихHigh load для начинающих
High load для начинающих
 
Самые частые проблемы и пути решения при росте нагрузки и масштабировании про...
Самые частые проблемы и пути решения при росте нагрузки и масштабировании про...Самые частые проблемы и пути решения при росте нагрузки и масштабировании про...
Самые частые проблемы и пути решения при росте нагрузки и масштабировании про...
 
"Посмотрим на Акку-Джаву" Дмитрий Мантула
"Посмотрим на Акку-Джаву" Дмитрий Мантула"Посмотрим на Акку-Джаву" Дмитрий Мантула
"Посмотрим на Акку-Джаву" Дмитрий Мантула
 
Сергей Крапивенский
Сергей КрапивенскийСергей Крапивенский
Сергей Крапивенский
 
Максим Пугачев
Максим ПугачевМаксим Пугачев
Максим Пугачев
 
Implement your own profiler with blackjack and fun
Implement your own profiler with blackjack and funImplement your own profiler with blackjack and fun
Implement your own profiler with blackjack and fun
 
Трудовые будни инженера производительности
Трудовые будни инженера производительностиТрудовые будни инженера производительности
Трудовые будни инженера производительности
 
Илья Кудинов «Развитие процессов тестирования в Badoo за три года, или как мы...
Илья Кудинов «Развитие процессов тестирования в Badoo за три года, или как мы...Илья Кудинов «Развитие процессов тестирования в Badoo за три года, или как мы...
Илья Кудинов «Развитие процессов тестирования в Badoo за три года, или как мы...
 
О фреймворках / Роман Ивлиев (Банки.ру)
О фреймворках / Роман Ивлиев (Банки.ру)О фреймворках / Роман Ивлиев (Банки.ру)
О фреймворках / Роман Ивлиев (Банки.ру)
 
UICov - инструмент анализа покрытия UI-тестами
UICov - инструмент анализа покрытия UI-тестамиUICov - инструмент анализа покрытия UI-тестами
UICov - инструмент анализа покрытия UI-тестами
 
CodeFest 2011. Макаров А. — Как разрабатывается Yii
CodeFest 2011. Макаров А. — Как разрабатывается YiiCodeFest 2011. Макаров А. — Как разрабатывается Yii
CodeFest 2011. Макаров А. — Как разрабатывается Yii
 
Тестирование и мониторинг производительности фронтенда с помощью sitespeed.io...
Тестирование и мониторинг производительности фронтенда с помощью sitespeed.io...Тестирование и мониторинг производительности фронтенда с помощью sitespeed.io...
Тестирование и мониторинг производительности фронтенда с помощью sitespeed.io...
 
QA Fes 2016. Александр Неделяев. Система мониторинга производительности своим...
QA Fes 2016. Александр Неделяев. Система мониторинга производительности своим...QA Fes 2016. Александр Неделяев. Система мониторинга производительности своим...
QA Fes 2016. Александр Неделяев. Система мониторинга производительности своим...
 
как строить архитектуру для отказоустойчивой службы такси
как строить архитектуру для отказоустойчивой службы таксикак строить архитектуру для отказоустойчивой службы такси
как строить архитектуру для отказоустойчивой службы такси
 
Эксплуатация container-based-инфраструктур / Николай Сивко (okmeter.io)
Эксплуатация container-based-инфраструктур / Николай Сивко (okmeter.io)Эксплуатация container-based-инфраструктур / Николай Сивко (okmeter.io)
Эксплуатация container-based-инфраструктур / Николай Сивко (okmeter.io)
 
Сергей Константинов (Яндекс)
Сергей Константинов (Яндекс)Сергей Константинов (Яндекс)
Сергей Константинов (Яндекс)
 
Браузерные помощники тестировщика (QA Fest 2016)
Браузерные помощники тестировщика (QA Fest 2016)Браузерные помощники тестировщика (QA Fest 2016)
Браузерные помощники тестировщика (QA Fest 2016)
 
CodeFest 2012. Ивлиев Р. — Аккуратно переезжаем, или тестирование в проектах ...
CodeFest 2012. Ивлиев Р. — Аккуратно переезжаем, или тестирование в проектах ...CodeFest 2012. Ивлиев Р. — Аккуратно переезжаем, или тестирование в проектах ...
CodeFest 2012. Ивлиев Р. — Аккуратно переезжаем, или тестирование в проектах ...
 

Similaire à Переход с Objective-C на Swift — все ли так просто? / Олег Алексеенко (SuperJob)

Цикл разработки и внедрения функционала в Мамбе (Михаил Буйлов)
Цикл разработки и внедрения функционала в Мамбе (Михаил Буйлов)Цикл разработки и внедрения функционала в Мамбе (Михаил Буйлов)
Цикл разработки и внедрения функционала в Мамбе (Михаил Буйлов)Ontico
 
Построение аналитического хранилища на 100 петабайт
Построение аналитического хранилища на 100 петабайтПостроение аналитического хранилища на 100 петабайт
Построение аналитического хранилища на 100 петабайтAlexander Mazurov
 
SECON'2016. Мухаметов Андрей, RxSwift && Apple TV - так ли хорошо всё новое?
SECON'2016. Мухаметов Андрей, RxSwift && Apple TV - так ли хорошо всё новое?SECON'2016. Мухаметов Андрей, RxSwift && Apple TV - так ли хорошо всё новое?
SECON'2016. Мухаметов Андрей, RxSwift && Apple TV - так ли хорошо всё новое?SECON
 
Путь от монолита на PHP к микросервисам на Scala / Денис Иванов (2GIS)
Путь от монолита на PHP к микросервисам на Scala  / Денис Иванов (2GIS)Путь от монолита на PHP к микросервисам на Scala  / Денис Иванов (2GIS)
Путь от монолита на PHP к микросервисам на Scala / Денис Иванов (2GIS)Ontico
 
Как превратить Openstack Swift в хранилище для высоких нагрузок разных типов,...
Как превратить Openstack Swift в хранилище для высоких нагрузок разных типов,...Как превратить Openstack Swift в хранилище для высоких нагрузок разных типов,...
Как превратить Openstack Swift в хранилище для высоких нагрузок разных типов,...Ontico
 
ORM технологии в .NET (Nhibernate, Linq To SQL, Entity Framework)
ORM технологии в .NET (Nhibernate, Linq To SQL, Entity Framework)ORM технологии в .NET (Nhibernate, Linq To SQL, Entity Framework)
ORM технологии в .NET (Nhibernate, Linq To SQL, Entity Framework)Pavel Tsukanov
 
Денис Иванов
Денис ИвановДенис Иванов
Денис ИвановCodeFest
 
Андрей Чебукин "Построение успешных API"
Андрей Чебукин "Построение успешных API"Андрей Чебукин "Построение успешных API"
Андрей Чебукин "Построение успешных API"Fwdays
 
Прогрессивный рендеринг и Catberry.js / Михаил Реенко (2GIS / Flamp)
Прогрессивный рендеринг и Catberry.js / Михаил Реенко (2GIS / Flamp)Прогрессивный рендеринг и Catberry.js / Михаил Реенко (2GIS / Flamp)
Прогрессивный рендеринг и Catberry.js / Михаил Реенко (2GIS / Flamp)Ontico
 
Архитектура HAWQ / Алексей Грищенко (Pivotal)
Архитектура HAWQ / Алексей Грищенко (Pivotal)Архитектура HAWQ / Алексей Грищенко (Pivotal)
Архитектура HAWQ / Алексей Грищенко (Pivotal)Ontico
 
Архитектура Apache HAWQ Highload++ 2015
Архитектура Apache HAWQ Highload++ 2015Архитектура Apache HAWQ Highload++ 2015
Архитектура Apache HAWQ Highload++ 2015Alexey Grishchenko
 
DUMP-2015 «Микросервисная архитектура в теории и на практике» Иван Бурмистров...
DUMP-2015 «Микросервисная архитектура в теории и на практике» Иван Бурмистров...DUMP-2015 «Микросервисная архитектура в теории и на практике» Иван Бурмистров...
DUMP-2015 «Микросервисная архитектура в теории и на практике» Иван Бурмистров...it-people
 
How to cook a blockchain and not get burned
How to cook a blockchain and not get burned How to cook a blockchain and not get burned
How to cook a blockchain and not get burned Alexander Syrotenko
 
Микросервисы: опыт использования в нагруженном проекте / Вадим Мадисон (М-Тех)
Микросервисы: опыт использования в нагруженном проекте / Вадим Мадисон (М-Тех)Микросервисы: опыт использования в нагруженном проекте / Вадим Мадисон (М-Тех)
Микросервисы: опыт использования в нагруженном проекте / Вадим Мадисон (М-Тех)Ontico
 
DevOps в Agile среде. Как, почему и когда инструменты помогают.
DevOps в Agile среде. Как, почему и когда инструменты помогают.DevOps в Agile среде. Как, почему и когда инструменты помогают.
DevOps в Agile среде. Как, почему и когда инструменты помогают.Alexander Titov
 
Кирилл Комлев. О реализации continuous integration для web проектов
Кирилл Комлев. О реализации continuous integration для web проектовКирилл Комлев. О реализации continuous integration для web проектов
Кирилл Комлев. О реализации continuous integration для web проектовOlesya_V
 
Решения сообщества для SharePoint
Решения сообщества для SharePointРешения сообщества для SharePoint
Решения сообщества для SharePointVitaly Baum
 
Protrarctor and Angular
Protrarctor and AngularProtrarctor and Angular
Protrarctor and AngularSQALab
 

Similaire à Переход с Objective-C на Swift — все ли так просто? / Олег Алексеенко (SuperJob) (20)

Цикл разработки и внедрения функционала в Мамбе (Михаил Буйлов)
Цикл разработки и внедрения функционала в Мамбе (Михаил Буйлов)Цикл разработки и внедрения функционала в Мамбе (Михаил Буйлов)
Цикл разработки и внедрения функционала в Мамбе (Михаил Буйлов)
 
Построение аналитического хранилища на 100 петабайт
Построение аналитического хранилища на 100 петабайтПостроение аналитического хранилища на 100 петабайт
Построение аналитического хранилища на 100 петабайт
 
SECON'2016. Мухаметов Андрей, RxSwift && Apple TV - так ли хорошо всё новое?
SECON'2016. Мухаметов Андрей, RxSwift && Apple TV - так ли хорошо всё новое?SECON'2016. Мухаметов Андрей, RxSwift && Apple TV - так ли хорошо всё новое?
SECON'2016. Мухаметов Андрей, RxSwift && Apple TV - так ли хорошо всё новое?
 
Путь от монолита на PHP к микросервисам на Scala / Денис Иванов (2GIS)
Путь от монолита на PHP к микросервисам на Scala  / Денис Иванов (2GIS)Путь от монолита на PHP к микросервисам на Scala  / Денис Иванов (2GIS)
Путь от монолита на PHP к микросервисам на Scala / Денис Иванов (2GIS)
 
Как превратить Openstack Swift в хранилище для высоких нагрузок разных типов,...
Как превратить Openstack Swift в хранилище для высоких нагрузок разных типов,...Как превратить Openstack Swift в хранилище для высоких нагрузок разных типов,...
Как превратить Openstack Swift в хранилище для высоких нагрузок разных типов,...
 
Sivko
SivkoSivko
Sivko
 
ORM технологии в .NET (Nhibernate, Linq To SQL, Entity Framework)
ORM технологии в .NET (Nhibernate, Linq To SQL, Entity Framework)ORM технологии в .NET (Nhibernate, Linq To SQL, Entity Framework)
ORM технологии в .NET (Nhibernate, Linq To SQL, Entity Framework)
 
Денис Иванов
Денис ИвановДенис Иванов
Денис Иванов
 
Андрей Чебукин "Построение успешных API"
Андрей Чебукин "Построение успешных API"Андрей Чебукин "Построение успешных API"
Андрей Чебукин "Построение успешных API"
 
Прогрессивный рендеринг и Catberry.js / Михаил Реенко (2GIS / Flamp)
Прогрессивный рендеринг и Catberry.js / Михаил Реенко (2GIS / Flamp)Прогрессивный рендеринг и Catberry.js / Михаил Реенко (2GIS / Flamp)
Прогрессивный рендеринг и Catberry.js / Михаил Реенко (2GIS / Flamp)
 
Архитектура HAWQ / Алексей Грищенко (Pivotal)
Архитектура HAWQ / Алексей Грищенко (Pivotal)Архитектура HAWQ / Алексей Грищенко (Pivotal)
Архитектура HAWQ / Алексей Грищенко (Pivotal)
 
Архитектура Apache HAWQ Highload++ 2015
Архитектура Apache HAWQ Highload++ 2015Архитектура Apache HAWQ Highload++ 2015
Архитектура Apache HAWQ Highload++ 2015
 
DUMP-2015 «Микросервисная архитектура в теории и на практике» Иван Бурмистров...
DUMP-2015 «Микросервисная архитектура в теории и на практике» Иван Бурмистров...DUMP-2015 «Микросервисная архитектура в теории и на практике» Иван Бурмистров...
DUMP-2015 «Микросервисная архитектура в теории и на практике» Иван Бурмистров...
 
How to cook a blockchain and not get burned
How to cook a blockchain and not get burned How to cook a blockchain and not get burned
How to cook a blockchain and not get burned
 
Микросервисы: опыт использования в нагруженном проекте / Вадим Мадисон (М-Тех)
Микросервисы: опыт использования в нагруженном проекте / Вадим Мадисон (М-Тех)Микросервисы: опыт использования в нагруженном проекте / Вадим Мадисон (М-Тех)
Микросервисы: опыт использования в нагруженном проекте / Вадим Мадисон (М-Тех)
 
DevOps в Agile среде. Как, почему и когда инструменты помогают.
DevOps в Agile среде. Как, почему и когда инструменты помогают.DevOps в Agile среде. Как, почему и когда инструменты помогают.
DevOps в Agile среде. Как, почему и когда инструменты помогают.
 
Кирилл Комлев. О реализации continuous integration для web проектов
Кирилл Комлев. О реализации continuous integration для web проектовКирилл Комлев. О реализации continuous integration для web проектов
Кирилл Комлев. О реализации continuous integration для web проектов
 
Решения сообщества для SharePoint
Решения сообщества для SharePointРешения сообщества для SharePoint
Решения сообщества для SharePoint
 
Breaking logs
Breaking logsBreaking logs
Breaking logs
 
Protrarctor and Angular
Protrarctor and AngularProtrarctor and Angular
Protrarctor and Angular
 

Plus de Ontico

One-cloud — система управления дата-центром в Одноклассниках / Олег Анастасье...
One-cloud — система управления дата-центром в Одноклассниках / Олег Анастасье...One-cloud — система управления дата-центром в Одноклассниках / Олег Анастасье...
One-cloud — система управления дата-центром в Одноклассниках / Олег Анастасье...Ontico
 
Масштабируя DNS / Артем Гавриченков (Qrator Labs)
Масштабируя DNS / Артем Гавриченков (Qrator Labs)Масштабируя DNS / Артем Гавриченков (Qrator Labs)
Масштабируя DNS / Артем Гавриченков (Qrator Labs)Ontico
 
Создание BigData-платформы для ФГУП Почта России / Андрей Бащенко (Luxoft)
Создание BigData-платформы для ФГУП Почта России / Андрей Бащенко (Luxoft)Создание BigData-платформы для ФГУП Почта России / Андрей Бащенко (Luxoft)
Создание BigData-платформы для ФГУП Почта России / Андрей Бащенко (Luxoft)Ontico
 
Готовим тестовое окружение, или сколько тестовых инстансов вам нужно / Алекса...
Готовим тестовое окружение, или сколько тестовых инстансов вам нужно / Алекса...Готовим тестовое окружение, или сколько тестовых инстансов вам нужно / Алекса...
Готовим тестовое окружение, или сколько тестовых инстансов вам нужно / Алекса...Ontico
 
Новые технологии репликации данных в PostgreSQL / Александр Алексеев (Postgre...
Новые технологии репликации данных в PostgreSQL / Александр Алексеев (Postgre...Новые технологии репликации данных в PostgreSQL / Александр Алексеев (Postgre...
Новые технологии репликации данных в PostgreSQL / Александр Алексеев (Postgre...Ontico
 
PostgreSQL Configuration for Humans / Alvaro Hernandez (OnGres)
PostgreSQL Configuration for Humans / Alvaro Hernandez (OnGres)PostgreSQL Configuration for Humans / Alvaro Hernandez (OnGres)
PostgreSQL Configuration for Humans / Alvaro Hernandez (OnGres)Ontico
 
Inexpensive Datamasking for MySQL with ProxySQL — Data Anonymization for Deve...
Inexpensive Datamasking for MySQL with ProxySQL — Data Anonymization for Deve...Inexpensive Datamasking for MySQL with ProxySQL — Data Anonymization for Deve...
Inexpensive Datamasking for MySQL with ProxySQL — Data Anonymization for Deve...Ontico
 
Опыт разработки модуля межсетевого экранирования для MySQL / Олег Брославский...
Опыт разработки модуля межсетевого экранирования для MySQL / Олег Брославский...Опыт разработки модуля межсетевого экранирования для MySQL / Олег Брославский...
Опыт разработки модуля межсетевого экранирования для MySQL / Олег Брославский...Ontico
 
ProxySQL Use Case Scenarios / Alkin Tezuysal (Percona)
ProxySQL Use Case Scenarios / Alkin Tezuysal (Percona)ProxySQL Use Case Scenarios / Alkin Tezuysal (Percona)
ProxySQL Use Case Scenarios / Alkin Tezuysal (Percona)Ontico
 
MySQL Replication — Advanced Features / Петр Зайцев (Percona)
MySQL Replication — Advanced Features / Петр Зайцев (Percona)MySQL Replication — Advanced Features / Петр Зайцев (Percona)
MySQL Replication — Advanced Features / Петр Зайцев (Percona)Ontico
 
Внутренний open-source. Как разрабатывать мобильное приложение большим количе...
Внутренний open-source. Как разрабатывать мобильное приложение большим количе...Внутренний open-source. Как разрабатывать мобильное приложение большим количе...
Внутренний open-source. Как разрабатывать мобильное приложение большим количе...Ontico
 
Подробно о том, как Causal Consistency реализовано в MongoDB / Михаил Тюленев...
Подробно о том, как Causal Consistency реализовано в MongoDB / Михаил Тюленев...Подробно о том, как Causal Consistency реализовано в MongoDB / Михаил Тюленев...
Подробно о том, как Causal Consistency реализовано в MongoDB / Михаил Тюленев...Ontico
 
Балансировка на скорости проводов. Без ASIC, без ограничений. Решения NFWare ...
Балансировка на скорости проводов. Без ASIC, без ограничений. Решения NFWare ...Балансировка на скорости проводов. Без ASIC, без ограничений. Решения NFWare ...
Балансировка на скорости проводов. Без ASIC, без ограничений. Решения NFWare ...Ontico
 
Перехват трафика — мифы и реальность / Евгений Усков (Qrator Labs)
Перехват трафика — мифы и реальность / Евгений Усков (Qrator Labs)Перехват трафика — мифы и реальность / Евгений Усков (Qrator Labs)
Перехват трафика — мифы и реальность / Евгений Усков (Qrator Labs)Ontico
 
И тогда наверняка вдруг запляшут облака! / Алексей Сушков (ПЕТЕР-СЕРВИС)
И тогда наверняка вдруг запляшут облака! / Алексей Сушков (ПЕТЕР-СЕРВИС)И тогда наверняка вдруг запляшут облака! / Алексей Сушков (ПЕТЕР-СЕРВИС)
И тогда наверняка вдруг запляшут облака! / Алексей Сушков (ПЕТЕР-СЕРВИС)Ontico
 
Как мы заставили Druid работать в Одноклассниках / Юрий Невиницин (OK.RU)
Как мы заставили Druid работать в Одноклассниках / Юрий Невиницин (OK.RU)Как мы заставили Druid работать в Одноклассниках / Юрий Невиницин (OK.RU)
Как мы заставили Druid работать в Одноклассниках / Юрий Невиницин (OK.RU)Ontico
 
Разгоняем ASP.NET Core / Илья Вербицкий (WebStoating s.r.o.)
Разгоняем ASP.NET Core / Илья Вербицкий (WebStoating s.r.o.)Разгоняем ASP.NET Core / Илья Вербицкий (WebStoating s.r.o.)
Разгоняем ASP.NET Core / Илья Вербицкий (WebStoating s.r.o.)Ontico
 
100500 способов кэширования в Oracle Database или как достичь максимальной ск...
100500 способов кэширования в Oracle Database или как достичь максимальной ск...100500 способов кэширования в Oracle Database или как достичь максимальной ск...
100500 способов кэширования в Oracle Database или как достичь максимальной ск...Ontico
 
Apache Ignite Persistence: зачем Persistence для In-Memory, и как он работает...
Apache Ignite Persistence: зачем Persistence для In-Memory, и как он работает...Apache Ignite Persistence: зачем Persistence для In-Memory, и как он работает...
Apache Ignite Persistence: зачем Persistence для In-Memory, и как он работает...Ontico
 
Механизмы мониторинга баз данных: взгляд изнутри / Дмитрий Еманов (Firebird P...
Механизмы мониторинга баз данных: взгляд изнутри / Дмитрий Еманов (Firebird P...Механизмы мониторинга баз данных: взгляд изнутри / Дмитрий Еманов (Firebird P...
Механизмы мониторинга баз данных: взгляд изнутри / Дмитрий Еманов (Firebird P...Ontico
 

Plus de Ontico (20)

One-cloud — система управления дата-центром в Одноклассниках / Олег Анастасье...
One-cloud — система управления дата-центром в Одноклассниках / Олег Анастасье...One-cloud — система управления дата-центром в Одноклассниках / Олег Анастасье...
One-cloud — система управления дата-центром в Одноклассниках / Олег Анастасье...
 
Масштабируя DNS / Артем Гавриченков (Qrator Labs)
Масштабируя DNS / Артем Гавриченков (Qrator Labs)Масштабируя DNS / Артем Гавриченков (Qrator Labs)
Масштабируя DNS / Артем Гавриченков (Qrator Labs)
 
Создание BigData-платформы для ФГУП Почта России / Андрей Бащенко (Luxoft)
Создание BigData-платформы для ФГУП Почта России / Андрей Бащенко (Luxoft)Создание BigData-платформы для ФГУП Почта России / Андрей Бащенко (Luxoft)
Создание BigData-платформы для ФГУП Почта России / Андрей Бащенко (Luxoft)
 
Готовим тестовое окружение, или сколько тестовых инстансов вам нужно / Алекса...
Готовим тестовое окружение, или сколько тестовых инстансов вам нужно / Алекса...Готовим тестовое окружение, или сколько тестовых инстансов вам нужно / Алекса...
Готовим тестовое окружение, или сколько тестовых инстансов вам нужно / Алекса...
 
Новые технологии репликации данных в PostgreSQL / Александр Алексеев (Postgre...
Новые технологии репликации данных в PostgreSQL / Александр Алексеев (Postgre...Новые технологии репликации данных в PostgreSQL / Александр Алексеев (Postgre...
Новые технологии репликации данных в PostgreSQL / Александр Алексеев (Postgre...
 
PostgreSQL Configuration for Humans / Alvaro Hernandez (OnGres)
PostgreSQL Configuration for Humans / Alvaro Hernandez (OnGres)PostgreSQL Configuration for Humans / Alvaro Hernandez (OnGres)
PostgreSQL Configuration for Humans / Alvaro Hernandez (OnGres)
 
Inexpensive Datamasking for MySQL with ProxySQL — Data Anonymization for Deve...
Inexpensive Datamasking for MySQL with ProxySQL — Data Anonymization for Deve...Inexpensive Datamasking for MySQL with ProxySQL — Data Anonymization for Deve...
Inexpensive Datamasking for MySQL with ProxySQL — Data Anonymization for Deve...
 
Опыт разработки модуля межсетевого экранирования для MySQL / Олег Брославский...
Опыт разработки модуля межсетевого экранирования для MySQL / Олег Брославский...Опыт разработки модуля межсетевого экранирования для MySQL / Олег Брославский...
Опыт разработки модуля межсетевого экранирования для MySQL / Олег Брославский...
 
ProxySQL Use Case Scenarios / Alkin Tezuysal (Percona)
ProxySQL Use Case Scenarios / Alkin Tezuysal (Percona)ProxySQL Use Case Scenarios / Alkin Tezuysal (Percona)
ProxySQL Use Case Scenarios / Alkin Tezuysal (Percona)
 
MySQL Replication — Advanced Features / Петр Зайцев (Percona)
MySQL Replication — Advanced Features / Петр Зайцев (Percona)MySQL Replication — Advanced Features / Петр Зайцев (Percona)
MySQL Replication — Advanced Features / Петр Зайцев (Percona)
 
Внутренний open-source. Как разрабатывать мобильное приложение большим количе...
Внутренний open-source. Как разрабатывать мобильное приложение большим количе...Внутренний open-source. Как разрабатывать мобильное приложение большим количе...
Внутренний open-source. Как разрабатывать мобильное приложение большим количе...
 
Подробно о том, как Causal Consistency реализовано в MongoDB / Михаил Тюленев...
Подробно о том, как Causal Consistency реализовано в MongoDB / Михаил Тюленев...Подробно о том, как Causal Consistency реализовано в MongoDB / Михаил Тюленев...
Подробно о том, как Causal Consistency реализовано в MongoDB / Михаил Тюленев...
 
Балансировка на скорости проводов. Без ASIC, без ограничений. Решения NFWare ...
Балансировка на скорости проводов. Без ASIC, без ограничений. Решения NFWare ...Балансировка на скорости проводов. Без ASIC, без ограничений. Решения NFWare ...
Балансировка на скорости проводов. Без ASIC, без ограничений. Решения NFWare ...
 
Перехват трафика — мифы и реальность / Евгений Усков (Qrator Labs)
Перехват трафика — мифы и реальность / Евгений Усков (Qrator Labs)Перехват трафика — мифы и реальность / Евгений Усков (Qrator Labs)
Перехват трафика — мифы и реальность / Евгений Усков (Qrator Labs)
 
И тогда наверняка вдруг запляшут облака! / Алексей Сушков (ПЕТЕР-СЕРВИС)
И тогда наверняка вдруг запляшут облака! / Алексей Сушков (ПЕТЕР-СЕРВИС)И тогда наверняка вдруг запляшут облака! / Алексей Сушков (ПЕТЕР-СЕРВИС)
И тогда наверняка вдруг запляшут облака! / Алексей Сушков (ПЕТЕР-СЕРВИС)
 
Как мы заставили Druid работать в Одноклассниках / Юрий Невиницин (OK.RU)
Как мы заставили Druid работать в Одноклассниках / Юрий Невиницин (OK.RU)Как мы заставили Druid работать в Одноклассниках / Юрий Невиницин (OK.RU)
Как мы заставили Druid работать в Одноклассниках / Юрий Невиницин (OK.RU)
 
Разгоняем ASP.NET Core / Илья Вербицкий (WebStoating s.r.o.)
Разгоняем ASP.NET Core / Илья Вербицкий (WebStoating s.r.o.)Разгоняем ASP.NET Core / Илья Вербицкий (WebStoating s.r.o.)
Разгоняем ASP.NET Core / Илья Вербицкий (WebStoating s.r.o.)
 
100500 способов кэширования в Oracle Database или как достичь максимальной ск...
100500 способов кэширования в Oracle Database или как достичь максимальной ск...100500 способов кэширования в Oracle Database или как достичь максимальной ск...
100500 способов кэширования в Oracle Database или как достичь максимальной ск...
 
Apache Ignite Persistence: зачем Persistence для In-Memory, и как он работает...
Apache Ignite Persistence: зачем Persistence для In-Memory, и как он работает...Apache Ignite Persistence: зачем Persistence для In-Memory, и как он работает...
Apache Ignite Persistence: зачем Persistence для In-Memory, и как он работает...
 
Механизмы мониторинга баз данных: взгляд изнутри / Дмитрий Еманов (Firebird P...
Механизмы мониторинга баз данных: взгляд изнутри / Дмитрий Еманов (Firebird P...Механизмы мониторинга баз данных: взгляд изнутри / Дмитрий Еманов (Firebird P...
Механизмы мониторинга баз данных: взгляд изнутри / Дмитрий Еманов (Firebird P...
 

Переход с Objective-C на Swift — все ли так просто? / Олег Алексеенко (SuperJob)

  • 1. Переход от Objective-c на swift от идеи до реализации
  • 3. • Superjob - сервис рекрутмента Кто мы?
  • 4. • Superjob - сервис рекрутмента • 20 000 000 - резюме опубликовано Кто мы?
  • 5. • Superjob - сервис рекрутмента • 20 000 000 - резюме опубликовано • 1 000 000 - дневная аудитория портала Кто мы?
  • 6. • Superjob - сервис рекрутмента • 20 000 000 - резюме опубликовано • 1 000 000 - дневная аудитория портала • 1 000 000 - приглашений на собеседования Кто мы?
  • 11. Зачем решили переходить? • Более предсказуемый код
  • 12. Зачем решили переходить? • Более предсказуемый код • Неизменяемость коллекций, свойств
  • 13. Зачем решили переходить? • Более предсказуемый код • Неизменяемость коллекций, свойств • Optionals
  • 16. Модель фильтра • > 20 @property • реализация isEqual
  • 17. Модель фильтра • > 20 @property • реализация isEqual • много мест из которых возможны
 изменения
  • 18. Зачем решили переходить? • Более предсказуемый код • Неизменяемость коллекций, свойств • Optionals
  • 19. Зачем решили переходить? • Более предсказуемый код • Неизменяемость коллекций, свойств • Optionals • Protocol Oriented Programming
  • 20. Зачем решили переходить? • Более предсказуемый код • Неизменяемость коллекций, свойств • Optionals • Protocol Oriented Programming • Длина методов 
 - (UITableViewCell *)tableView:(UITableView *)tableView 
 cellForRowAtIndexPath:(NSIndexPath *)indexPath
  • 21. Зачем решили переходить? • Более предсказуемый код • Заблаговременное мигрирование с objc библиотек
  • 22. Зачем решили переходить? • Более предсказуемый код • Заблаговременное мигрирование с objc библиотек • Новые библиотеки и UI компоненты пишутся на Swift
  • 23. Зачем решили переходить? • Более предсказуемый код • Заблаговременное мигрирование с objc библиотек • Новые библиотеки и UI компоненты пишутся на Swift • Старые Objc библиотеки не поддерживаются
  • 24. Зачем решили переходить? • Более предсказуемый код • Заблаговременное мигрирование с objc библиотек • Новые библиотеки и UI компоненты пишутся на Swift • Старые Objc библиотеки не поддерживаются NOTE: This is legacy introduction to the Objective-C ReactiveCocoa, pod 'ReactiveCocoa', '~> 2.5.0'
  • 25. Зачем решили переходить? • Более предсказуемый код • Заблаговременное мигрирование с objc библиотек • Поиск новых сотрудников
  • 26. Зачем решили переходить? • Более предсказуемый код • Заблаговременное мигрирование с objc библиотек • Поиск новых сотрудников • 70% собеседуемых хотели бы писать на swift
  • 27. Зачем решили переходить? • Более предсказуемый код • Заблаговременное мигрирование с objc библиотек • Поиск новых сотрудников • 70% собеседуемых хотели бы писать на swift • После перехода на swift удалось найти
  • 29. Но не так все просто • Бизнес хочет постоянную поставку новых фич
  • 30. Но не так все просто • Бизнес хочет постоянную поставку новых фич • поддержка того же уровня crash-free
  • 31. Но не так все просто • Бизнес хочет постоянную поставку новых фич • поддержка того же уровня crash-free • такая же скорость разработки
  • 32. Но не так все просто • Бизнес хочет постоянную поставку новых фич • Приложения имею долгую историю
  • 33. Но не так все просто • Бизнес хочет постоянную поставку новых фич • Приложения имею долгую историю • 5 лет разработки
  • 34. Но не так все просто • Бизнес хочет постоянную поставку новых фич • Приложения имею долгую историю • 5 лет разработки • разные архитектурные патерны
  • 35. Но не так все просто • Бизнес хочет постоянную поставку новых фич • Приложения имею долгую историю • Хочется избежать кучи “граблей”, чтобы потом все снова не переписывать
  • 36. Но не так все просто • Бизнес хочет постоянную поставку новых фич • Приложения имею долгую историю • Хочется избежать кучи “граблей”, чтобы потом все снова не переписывать • архитектура swift ориентированная
  • 37. Но не так все просто • Бизнес хочет постоянную поставку новых фич • Приложения имею долгую историю • Хочется избежать кучи “граблей”, чтобы потом все снова не переписывать • архитектура swift ориентированная • тестируемость
  • 38. Но не так все просто • Бизнес хочет постоянную поставку новых фич • Приложения имею долгую историю • Хочется избежать кучи “граблей”, чтобы потом все снова не переписывать • архитектура swift ориентированная • тестируемость • кодстай и т.д
  • 39. Сели, подумали и сделали • Много проблем с nullability
  • 40. Nullability current=`find . -type f -name "*.h" -exec grep -il NS_ASSUME_NONULL '{}' ; | wc -l` all=`find . -type f -name "*.h" | wc -l` percent=$(( $current * 100 )) expr $percent / $all Как оценить насколько все плохо?
  • 41. Nullability NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_END current=`find . -type f -name "*.h" -exec grep -il NS_ASSUME_NONULL '{}' ; | wc -l` all=`find . -type f -name "*.h" | wc -l` percent=$(( $current * 100 )) expr $percent / $all Как оценить насколько все плохо?
  • 42. Nullability NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_END ~5% current=`find . -type f -name "*.h" -exec grep -il NS_ASSUME_NONULL '{}' ; | wc -l` all=`find . -type f -name "*.h" | wc -l` percent=$(( $current * 100 )) expr $percent / $all Как оценить насколько все плохо?
  • 43. Nullability NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_END ~5% current=`find . -type f -name "*.h" -exec grep -il NS_ASSUME_NONULL '{}' ; | wc -l` all=`find . -type f -name "*.h" | wc -l` percent=$(( $current * 100 )) expr $percent / $all Как оценить насколько все плохо?
  • 45. Nullability • 3 месяца на каждый измененный файл добавляем Nullability • Скрипт который не дает пройти PullRequest без меток :) Как улучшить показатель?
  • 46. Nullability • 3 месяца на каждый измененный файл добавляем Nullability • Скрипт который не дает пройти PullRequest без меток :) 60% Как улучшить показатель?
  • 47. Сели, подумали и сделали • Много проблем с nullability
  • 48. Сели, подумали и сделали • Много проблем с nullability • Проблемы со сторонними библиотеками
  • 50. СocoaPods • Просто заменить • Masonry -> SnapKit • AFNetworking - Alamofire
  • 51. СocoaPods • Просто заменить • Masonry -> SnapKit • AFNetworking - Alamofire ✓ ✓
  • 52. СocoaPods • Просто заменить • Masonry -> SnapKit • AFNetworking - Alamofire ✓ ? ✓
  • 53. СocoaPods • Просто заменить • Masonry -> SnapKit • AFNetworking - Alamofire ✓ ? Objection ? Mantle ? Specta? Expecta? ✓
  • 55. profileFacade.authorize(withLogin: login, password: pass).doNext(block: ((Any?) -> Void)!) Проблемы 1. Нет понятия, что получаешь
  • 56. profileFacade.authorize(withLogin: login, password: pass).doNext(block: ((Any?) -> Void)!) Проблемы 1. Нет понятия, что получаешь
  • 57. profileFacade.authorize(withLogin: login, password: pass).doNext(block: ((Any?) -> Void)!) profileFacade .authorize(withLogin: login, password: pass) .subscribeNext { (response) in if let user = response as? SJAProfileModel { print("(String(describing: user.name))") } } Проблемы 1. Нет понятия, что получаешь 2. Необходимо каждый раз “кастить”
  • 58. profileFacade.authorize(withLogin: login, password: pass).doNext(block: ((Any?) -> Void)!) profileFacade .authorize(withLogin: login, password: pass) .subscribeNext { (response) in if let user = response as? SJAProfileModel { print("(String(describing: user.name))") } } Проблемы 1. Нет понятия, что получаешь 2. Необходимо каждый раз “кастить”
  • 60. • Хочется, чтобы можно было легко пользоваться Критерии для решения
  • 61. • Хочется, чтобы можно было легко пользоваться • Чтобы была строгая типизация Критерии для решения
  • 62. • Хочется, чтобы можно было легко пользоваться • Чтобы была строгая типизация • Swift like API Критерии для решения
  • 63. • Хочется, чтобы можно было легко пользоваться • Чтобы была строгая типизация • Swift like API Критерии для решения
  • 64. • Хочется, чтобы можно было легко пользоваться • Чтобы была строгая типизация • Swift like API Критерии для решения
  • 65. • Хочется, чтобы можно было легко пользоваться • Чтобы была строгая типизация • Swift like API Критерии для решения
  • 66. ? • Хочется, чтобы можно было легко пользоваться • Чтобы была строгая типизация • Swift like API Критерии для решения
  • 67. extension RACSignal { private func rxMapBody<T>(convertBlock: @escaping (Any?) -> T?) -> Observable<T> { return Observable.create() { observer in self.subscribeNext( { anyValue in if let converted = convertBlock(anyValue) { observer.onNext(converted) } else { observer.onError(RxCastError.cannotConvertTypes) } }, ... ... ... return Disposables.create() { } } } +1. Подписываемся на новые значения RACSignal
  • 68. extension RACSignal { private func rxMapBody<T>(convertBlock: @escaping (Any?) -> T?) -> Observable<T> { return Observable.create() { observer in self.subscribeNext( { anyValue in if let converted = convertBlock(anyValue) { observer.onNext(converted) } else { observer.onError(RxCastError.cannotConvertTypes) } }, ... ... ... return Disposables.create() { } } } +1. Подписываемся на новые значения RACSignal
  • 69. extension RACSignal { private func rxMapBody<T>(convertBlock: @escaping (Any?) -> T?) -> Observable<T> { return Observable.create() { observer in self.subscribeNext( { anyValue in if let converted = convertBlock(anyValue) { observer.onNext(converted) } else { observer.onError(RxCastError.cannotConvertTypes) } }, ... ... ... return Disposables.create() { } } } +1. Подписываемся на новые значения RACSignal
  • 70. extension RACSignal { private func rxMapBody<T>(convertBlock: @escaping (Any?) -> T?) -> Observable<T> { return Observable.create() { observer in self.subscribeNext( { anyValue in if let converted = convertBlock(anyValue) { observer.onNext(converted) } else { observer.onError(RxCastError.cannotConvertTypes) } }, ... ... ... return Disposables.create() { } } } +1. Подписываемся на новые значения RACSignal
  • 71. extension RACSignal { private func rxMapBody<T>(convertBlock: @escaping (Any?) -> T?) -> Observable<T> { return Observable.create() { observer in self.subscribeNext( { anyValue in if let converted = convertBlock(anyValue) { observer.onNext(converted) } else { observer.onError(RxCastError.cannotConvertTypes) } }, ... ... ... return Disposables.create() { } } } +1. Подписываемся на новые значения RACSignal
  • 72. extension RACSignal { private func rxMapBody<T>(convertBlock: @escaping (Any?) -> T?) -> Observable<T> { return Observable.create() { observer in self.subscribeNext( { anyValue in if let converted = convertBlock(anyValue) { observer.onNext(converted) } else { observer.onError(RxCastError.cannotConvertTypes) } }, ... ... ... return Disposables.create() { } } } +1. Подписываемся на новые значения RACSignal
  • 73. extension RACSignal { private func rxMapBody<T>(convertBlock: @escaping (Any?) -> T?) -> Observable<T> { return Observable.create() { observer in self.subscribeNext( { anyValue in if let converted = convertBlock(anyValue) { observer.onNext(converted) } else { observer.onError(RxCastError.cannotConvertTypes) } }, ... ... ... return Disposables.create() { } } } +1. Подписываемся на новые значения RACSignal
  • 74. extension RACSignal { public func rxMap<T>(_ type: T.Type = T.self) -> Observable<T> { return rxMapBody() { anyValue in if let value: T = rx_cast(anyValue) { return value } else { return nil } } } } +2. Приводим не типизированное к типизированному
  • 75. extension RACSignal { public func rxMap<T>(_ type: T.Type = T.self) -> Observable<T> { return rxMapBody() { anyValue in if let value: T = rx_cast(anyValue) { return value } else { return nil } } } } +2. Приводим не типизированное к типизированному
  • 76. extension RACSignal { public func rxMap<T>(_ type: T.Type = T.self) -> Observable<T> { return rxMapBody() { anyValue in if let value: T = rx_cast(anyValue) { return value } else { return nil } } } } +2. Приводим не типизированное к типизированному
  • 77. extension RACSignal { public func rxMap<T>(_ type: T.Type = T.self) -> Observable<T> { return rxMapBody() { anyValue in if let value: T = rx_cast(anyValue) { return value } else { return nil } } } } +2. Приводим не типизированное к типизированному
  • 78. extension RACSignal { public func rxMap<T>(_ type: T.Type = T.self) -> Observable<T> { return rxMapBody() { anyValue in if let value: T = rx_cast(anyValue) { return value } else { return nil } } } } +2. Приводим не типизированное к типизированному
  • 79. + internal func rx_cast<T>(_ value: Any?) -> T? { if let v = value as? T { return v } else if let E = T.self as? ExpressibleByNilLiteral.Type { return E.init(nilLiteral: ()) as? T } return nil } 3. “Кастим” к нужному типу T
  • 80. + internal func rx_cast<T>(_ value: Any?) -> T? { if let v = value as? T { return v } else if let E = T.self as? ExpressibleByNilLiteral.Type { return E.init(nilLiteral: ()) as? T } return nil } 3. “Кастим” к нужному типу T
  • 81. + internal func rx_cast<T>(_ value: Any?) -> T? { if let v = value as? T { return v } else if let E = T.self as? ExpressibleByNilLiteral.Type { return E.init(nilLiteral: ()) as? T } return nil } 3. “Кастим” к нужному типу T
  • 82. + internal func rx_cast<T>(_ value: Any?) -> T? { if let v = value as? T { return v } else if let E = T.self as? ExpressibleByNilLiteral.Type { return E.init(nilLiteral: ()) as? T } return nil } 3. “Кастим” к нужному типу T
  • 83. + internal func rx_cast<T>(_ value: Any?) -> T? { if let v = value as? T { return v } else if let E = T.self as? ExpressibleByNilLiteral.Type { return E.init(nilLiteral: ()) as? T } return nil } 3. “Кастим” к нужному типу T
  • 84. profileFacade .authorize(withLogin: login, password: pass) .rxMap(SJAProfileModel.self) .subscribe(onNext: { (user) in }) .addDisposableTo(disposeBag) + = • Получилось
  • 85. profileFacade .authorize(withLogin: login, password: pass) .rxMap(SJAProfileModel.self) .subscribe(onNext: { (user) in }) .addDisposableTo(disposeBag) + = • Получилось
  • 86. extension SJAProfileFacade { func authorize(login: String, passwrod: String) -> Observable<SJAProfileModel> { return self.authorize(withLogin: login, password: passwrod).rxMap() } } • Можно лучше + =
  • 87. extension SJAProfileFacade { func authorize(login: String, passwrod: String) -> Observable<SJAProfileModel> { return self.authorize(withLogin: login, password: passwrod).rxMap() } } • Можно лучше + =
  • 88. extension SJAProfileFacade { func authorize(login: String, passwrod: String) -> Observable<SJAProfileModel> { return self.authorize(withLogin: login, password: passwrod).rxMap() } } • Можно лучше + =
  • 89. extension SJAProfileFacade { func authorize(login: String, passwrod: String) -> Observable<SJAProfileModel> { return self.authorize(withLogin: login, password: passwrod).rxMap() } } • Можно лучше + = profileFacade .authorize(login: login, passwrod: pass) .subscribe(onNext: { (user) in }) .addDisposableTo(disposeBag)
  • 90. Сели, подумали и сделали • Много проблем с nullability • Проблемы со сторонними библиотеками
  • 91. Сели, подумали и сделали • Много проблем с nullability • Проблемы со сторонними библиотеками • Не все возможности swift доступны в Objective-c
  • 92. Не все возможности Swift доступны в Objective-c
  • 93. Не все возможности Swift доступны в Objective-c • struct
  • 94. Не все возможности Swift доступны в Objective-c • struct • enum
  • 95. Не все возможности Swift доступны в Objective-c • struct • enum • моки для тестов
  • 96. Не все возможности Swift доступны в Objective-c • struct • enum • моки для тестов ?
  • 97. Не все возможности Swift доступны в Objective-c • struct • enum • моки для тестов Sourcery https://goo.gl/qgnH9D ?
  • 98. Sourcery scans your source code, applies your personal templates and generates Swift code for you, allowing you to use meta-programming techniques to save time and decrease potential mistakes.
  • 99. Sourcery scans your source code, applies your personal templates and generates Swift code for you, allowing you to use meta-programming techniques to save time and decrease potential mistakes. • equatable/hashable • NSCoding • JSON serialization Автогенерация
  • 100. struct Resume: AutoHashable, AutoEquatable { var key: Int? let name: String? var firstName: String? var lastName: String? var middleName: String? var birthDate: Date? } extension Resume: Hashable { internal var hashValue: Int { return combineHashes([key?.hashValue ?? 0, name?.hashValue ?? 0, firstName?.hashValue ?? 0, lastName?.hashValue ?? 0, middleName?.hashValue ?? 0, birthDate?.hashValue ?? 0, 0]) } } • Hashable
  • 101. struct Resume: AutoHashable, AutoEquatable { var key: Int? let name: String? var firstName: String? var lastName: String? var middleName: String? var birthDate: Date? } extension Resume: Hashable { internal var hashValue: Int { return combineHashes([key?.hashValue ?? 0, name?.hashValue ?? 0, firstName?.hashValue ?? 0, lastName?.hashValue ?? 0, middleName?.hashValue ?? 0, birthDate?.hashValue ?? 0, 0]) } } • Hashable
  • 102. struct Resume: AutoHashable, AutoEquatable { var key: Int? let name: String? var firstName: String? var lastName: String? var middleName: String? var birthDate: Date? } extension Resume: Equatable {} internal func == (lhs: Resume, rhs: Resume) -> Bool { guard compareOptionals(lhs: lhs.key, rhs: rhs.key, compare: ==) else { return false } guard compareOptionals(lhs: lhs.name, rhs: rhs.name, compare: ==) else { return false } guard compareOptionals(lhs: lhs.firstName, rhs: rhs.firstName, compare: ==) else { return false } ... ... return true } • Equtable
  • 103. struct Resume: AutoHashable, AutoEquatable { var key: Int? let name: String? var firstName: String? var lastName: String? var middleName: String? var birthDate: Date? } extension Resume: Equatable {} internal func == (lhs: Resume, rhs: Resume) -> Bool { guard compareOptionals(lhs: lhs.key, rhs: rhs.key, compare: ==) else { return false } guard compareOptionals(lhs: lhs.name, rhs: rhs.name, compare: ==) else { return false } guard compareOptionals(lhs: lhs.firstName, rhs: rhs.firstName, compare: ==) else { return false } ... ... return true } • Equtable
  • 104. + enum Conf { case Apps case Backend case WebScale } • Пример
  • 105. + enum Conf { case Apps case Backend case WebScale } + • Пример
  • 106. + enum Conf { case Apps case Backend case WebScale } + {% for enum in types.enums %} extension {{ enum.name }} { static var numberOfCases:Int = {{ enum.cases.count}} } {% endfor %} • Пример
  • 107. + enum Conf { case Apps case Backend case WebScale } + {% for enum in types.enums %} extension {{ enum.name }} { static var numberOfCases:Int = {{ enum.cases.count}} } {% endfor %} • Пример
  • 108. + enum Conf { case Apps case Backend case WebScale } + {% for enum in types.enums %} extension {{ enum.name }} { static var numberOfCases:Int = {{ enum.cases.count}} } {% endfor %} • Пример
  • 109. + enum Conf { case Apps case Backend case WebScale } + {% for enum in types.enums %} extension {{ enum.name }} { static var numberOfCases:Int = {{ enum.cases.count}} } {% endfor %} • Пример
  • 110. + enum Conf { case Apps case Backend case WebScale } + = {% for enum in types.enums %} extension {{ enum.name }} { static var numberOfCases:Int = {{ enum.cases.count}} } {% endfor %} • Пример
  • 111. + enum Conf { case Apps case Backend case WebScale } + = {% for enum in types.enums %} extension {{ enum.name }} { static var numberOfCases:Int = {{ enum.cases.count}} } {% endfor %} extension Conf { static var numberOfCases:Int = 3 } • Пример
  • 112. + + = {% for enum in types.enums %} extension {{ enum.name }} { static var numberOfCases:Int = {{ enum.cases.count}} } {% endfor %} extension Conf { static var numberOfCases:Int = 3 } enum Conf { case Apps case Backend case WebScale case Awesome } • Пример
  • 113. + + = {% for enum in types.enums %} extension {{ enum.name }} { static var numberOfCases:Int = {{ enum.cases.count}} } {% endfor %} enum Conf { case Apps case Backend case WebScale case Awesome } extension Conf { static var numberOfCases:Int = 4 } • Пример
  • 114. Sourcery Как же это помогло нам?
  • 115. Sourcery Как же это помогло нам? Struct
  • 116. Sourcery Как же это помогло нам? Struct Objective-c
  • 117. struct Resume: AutoHashable, AutoEquatable, AutoObjc { var key: Int? let name: String? var firstName: String? var lastName: String? var middleName: String? var birthDate: Date? } Sourcery Как же это помогло нам? Struct Objective-c
  • 118. struct Resume: AutoHashable, AutoEquatable, AutoObjc { var key: Int? let name: String? var firstName: String? var lastName: String? var middleName: String? var birthDate: Date? } Sourcery Как же это помогло нам? Struct Objective-c
  • 119. struct Resume: AutoHashable, AutoEquatable, AutoObjc { var key: Int? let name: String? var firstName: String? var lastName: String? var middleName: String? var birthDate: Date? } Sourcery Как же это помогло нам? Struct Objective-c class ResumeObjc: NSObject { private(set) var key: Int? private(set) var name: String? private(set) var firstName: String? private(set) var lastName: String? private(set) var middleName: String? private(set) var birthDate: Date? }
  • 120. struct Resume: AutoHashable, AutoEquatable, AutoObjc { var key: Int? let name: String? var firstName: String? var lastName: String? var middleName: String? var birthDate: Date? } Sourcery Как же это помогло нам? Struct Objective-c class ResumeObjc: NSObject { private(set) var key: Int? private(set) var name: String? private(set) var firstName: String? private(set) var lastName: String? private(set) var middleName: String? private(set) var birthDate: Date? }
  • 121. struct Resume: AutoHashable, AutoEquatable, AutoObjc { var key: Int? let name: String? var firstName: String? var lastName: String? var middleName: String? var birthDate: Date? } Sourcery Как же это помогло нам? Struct Objective-c class ResumeObjc: NSObject { private(set) var key: Int? private(set) var name: String? private(set) var firstName: String? private(set) var lastName: String? private(set) var middleName: String? private(set) var birthDate: Date? }
  • 122. struct Resume: AutoHashable, AutoEquatable, AutoObjc { var key: Int? let name: String? var firstName: String? var lastName: String? var middleName: String? var birthDate: Date? } Sourcery Как же это помогло нам? Struct Objective-c ✓ class ResumeObjc: NSObject { private(set) var key: Int? private(set) var name: String? private(set) var firstName: String? private(set) var lastName: String? private(set) var middleName: String? private(set) var birthDate: Date? }
  • 123. SourceryДля тестов нужно самостоятельно писать моки
  • 124. protocol VacancyDetailViewModelDelegate: class, AutoMockable { func vmReloadData(animated: Bool) func vmShareItems(items: [Any]) } SourceryДля тестов нужно самостоятельно писать моки
  • 125. protocol VacancyDetailViewModelDelegate: class, AutoMockable { func vmReloadData(animated: Bool) func vmShareItems(items: [Any]) } SourceryДля тестов нужно самостоятельно писать моки
  • 126. protocol VacancyDetailViewModelDelegate: class, AutoMockable { func vmReloadData(animated: Bool) func vmShareItems(items: [Any]) } SourceryДля тестов нужно самостоятельно писать моки
  • 127. protocol VacancyDetailViewModelDelegate: class, AutoMockable { func vmReloadData(animated: Bool) func vmShareItems(items: [Any]) } SourceryДля тестов нужно самостоятельно писать моки class VacancyDetailViewModelDelegateMock: VacancyDetailViewModelDelegate { var vmReloadDataCalled = false var vmReloadDataReceivedAnimated: Bool? func vmReloadData(animated: Bool) { vmReloadDataCalled = true vmReloadDataReceivedAnimated = animated } var vmShareItemsCalled = false var vmShareItemsReceivedItems: [Any]? func vmShareItems(items: [Any]) { vmShareItemsCalled = true vmShareItemsReceivedItems = items } }
  • 128. protocol VacancyDetailViewModelDelegate: class, AutoMockable { func vmReloadData(animated: Bool) func vmShareItems(items: [Any]) } SourceryДля тестов нужно самостоятельно писать моки class VacancyDetailViewModelDelegateMock: VacancyDetailViewModelDelegate { var vmReloadDataCalled = false var vmReloadDataReceivedAnimated: Bool? func vmReloadData(animated: Bool) { vmReloadDataCalled = true vmReloadDataReceivedAnimated = animated } var vmShareItemsCalled = false var vmShareItemsReceivedItems: [Any]? func vmShareItems(items: [Any]) { vmShareItemsCalled = true vmShareItemsReceivedItems = items } }
  • 129. Сели, подумали и сделали • Много проблем с nullability • Проблемы со сторонними библиотеками • Не все возможности swift доступны в Objective-c
  • 130. Сели, подумали и сделали • Много проблем с nullability • Проблемы со сторонними библиотеками • Не все возможности swift доступны в Objective-c • Организационные моменты внутри команды
  • 131. SwiftLint A tool to enforce Swift style and conventions
  • 132. SwiftLint • Помог ввести одинаковый стиль на всю команду A tool to enforce Swift style and conventions
  • 133. SwiftLint • Помог ввести одинаковый стиль на всю команду A tool to enforce Swift style and conventions
  • 139. SwiftGen • Assets Catalogs enum Asset: String { case contactEmailIcon = "contactEmailIcon" case contactFbIcon = "contactFbIcon" case contactGitIcon = "contactGitIcon" case contactLnIcon = "contactLnIcon" case contactOKIcon = "contactOKIcon" }
  • 140. SwiftGen • Assets Catalogs enum Asset: String { case contactEmailIcon = "contactEmailIcon" case contactFbIcon = "contactFbIcon" case contactGitIcon = "contactGitIcon" case contactLnIcon = "contactLnIcon" case contactOKIcon = "contactOKIcon" } let icon = Asset.contactFbIcon.image
  • 141. SwiftGen • Assets Catalogs • Localizable.strings
  • 142. SwiftGen • Assets Catalogs • Localizable.strings let string = L10n.vacancyCellTitleExpiriens.string
  • 143. SwiftGen • Assets Catalogs • Localizable.strings let string = tr(.vacancyCellTitleExpiriens) let string = L10n.vacancyCellTitleExpiriens.string
  • 144. SwiftGen • Assets Catalogs • Localizable.strings • UIStoryboards and their Scenes let string = tr(.vacancyCellTitleExpiriens) let string = L10n.vacancyCellTitleExpiriens.string
  • 145. SwiftGen • Assets Catalogs • Localizable.strings • UIStoryboards and their Scenes • NSStoryboards and their Scenes let string = tr(.vacancyCellTitleExpiriens) let string = L10n.vacancyCellTitleExpiriens.string
  • 146. SwiftGen • Assets Catalogs • Localizable.strings • UIStoryboards and their Scenes • NSStoryboards and their Scenes • Colors let string = tr(.vacancyCellTitleExpiriens) let string = L10n.vacancyCellTitleExpiriens.string
  • 147. SwiftGen • Assets Catalogs • Localizable.strings • UIStoryboards and their Scenes • NSStoryboards and their Scenes • Colors • Fonts let string = tr(.vacancyCellTitleExpiriens) let string = L10n.vacancyCellTitleExpiriens.string
  • 148. Сели, подумали и сделали • Много проблем с nullability • Проблемы со сторонними библиотеками • Не все возможности swift доступны в Objective-c • Организационные моменты внутри команды
  • 149. Сели, подумали и сделали • Много проблем с nullability • Проблемы со сторонними библиотеками • Не все возможности swift доступны в Objective-c • Организационные моменты внутри команды ✓
  • 150. Сели, подумали и сделали • Много проблем с nullability • Проблемы со сторонними библиотеками • Не все возможности swift доступны в Objective-c • Организационные моменты внутри команды ✓ ✓
  • 151. Сели, подумали и сделали • Много проблем с nullability • Проблемы со сторонними библиотеками • Не все возможности swift доступны в Objective-c • Организационные моменты внутри команды ✓ ✓ ✓
  • 152. Сели, подумали и сделали • Много проблем с nullability • Проблемы со сторонними библиотеками • Не все возможности swift доступны в Objective-c • Организационные моменты внутри команды ✓ ✓ ✓ ✓
  • 153. Что нам дал переход на swift • Статическая типизация дала более предсказуемое поведение и меньше багов в бизнес логике • Более быструю скорость разработки фич • Наняли людей в команду