Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Кодогенерация в Swift

CocoaHeads
April 02, 2018
430

Кодогенерация в Swift

Swift - великолепный язык, имеющий выразительный синтаксис и безопасную систему типов. Но это не избавляет нас от таких проблем как написание вечно повторяющегося рутинного кода и использование ненадежного string-based API.

В докладе мы разберем некоторые популярные инструменты для кодогенерации в Swift. Посмотрим на устройство их работы, примеры использования и на то, как сделать ваш код безопаснее и сэкономить драгоценное время разработки благодаря этим инструментам.

CocoaHeads

April 02, 2018
Tweet

More Decks by CocoaHeads

Transcript

  1. 2 Daily routine VIPER MVP MVVM YARCH Fonts Localizable.strings Storyborads

    Colors Assets Equatable Hashable JSON Serialize JSON Deserialize Mocks/Stubs Type Erasure
  2. Xcode template, Generamba SwiftGen, R.swift, Natalie Sourcery Codegen for project

    resources Meta-programming for Swift Codegen for boilerplate modules/files 5
  3. 6

  4. 8

  5. 9

  6. 10

  7. 11 // // ___FILENAME___ // ___PROJECTNAME___ // // Created by

    ___FULLUSERNAME___ on ___DATE___. // ___COPYRIGHT___ // import Foundation protocol ___VARIABLE_moduleName___PresenterInterface: class { } protocol ___VARIABLE_moduleName___PresenterControl: class { } class ___VARIABLE_moduleName___Presenter: ___VARIABLE_moduleName___PresenterInterface, ModuleInput { weak var view: ___VARIABLE_moduleName___ViewInterface? var interactor: ___VARIABLE_moduleName___InteractorInterface? var router: ___VARIABLE_moduleName___RouterInterface? } extension ___VARIABLE_moduleName___Presenter: ___VARIABLE_moduleName___PresenterControl { } Проблема 1
  8. Шаблонные движки? Количество статей: {{ articles.count }}. {% for article

    in articles %} {{ article.title }} от {{ article.author|uppercase }}\n {% endfor %} { “articles”: [ { “title”: “Статья 1”, “author”: “Иван Иванов” “content”: “Много текста” }, { “title”: “Статья 2”, “author”: “Петр Петров” “content”: “Много текста” } ] } Контекст Шаблон 16 Количество статей: 2. Статья 1 от ИВАН ИВАНОВ Статья 2 от ПЕТР ПЕТРОВ Результат
  9. 17 // // {{ module_info.name }}{{ module_info.file_name }} // {{

    module_info.project_name }} // // Created by {{ developer.name }} on {{ date }}. // Copyright {{ year }} {{ developer.company }}. All rights reserved. // import Foundation protocol {{module_info.name}}PresenterInterface: class { } protocol {{module_info.name}}PresenterControl: class { } class {{module_info.name}}Presenter: {{module_info.name}}PresenterInterface, ModuleInput { weak var view: {{module_info.name}}ViewInterface? var interactor: {{module_info.name}}InteractorInterface? var router: {{module_info.name}}RouterInterface? } extension {{module_info.name}}Presenter: {{module_info.name}}PresenterControl { }
  10. 18 generamba setup - создаем файл конфигурации Rambafile generamba template

    install - добавляем шаблоны generamba gen [module_name] [template_name] - создаем модуль в одну строку
  11. 19 Возможность выборочно добавить сгенерированный файл к разным таргетам "

    code_files: - {name: View/ViewController.swift, path: Code/View/viewcontroller.swift.liquid} - {name: Presenter/Presenter.swift, path: Code/Presenter/presenter.swift.liquid} test_files: - {name: View/ViewTests.swift, path: Tests/View/view_tests.swift.liquid} - {name: Presenter/PresenterTests.swift, path: Tests/Presenter/presenter_tests.swift.liquid}
  12. В итоге 20 Минимизируем потенциальные ошибки при создании модуля Никакой

    монотонной ручной работы Generamba There are some things XCode template can do, for everything else there’s Generamba.
  13. extension UIFont { enum FontType { case gtWalsheimBold(CGFloat) case gtWalsheimRegular(CGFloat)

    } static func appFont(_ fontType: FontType) -> UIFont { switch fontType { case let .gtWalsheimBold(size): return UIFont(name: "GTWalsheim-Bold", size: size) ?? UIFont.boldSystemFont(ofSize: size) case let .gtWalsheimRegular(size): return UIFont(name: "GTWalsheim-Regular", size: size) ?? UIFont.systemFont(ofSize: size) } } } Fixed! 23
  14. 25 R.swift SwiftGen Natalie Сгенерируй мне Картинки Шрифты Локализация строк

    Цвета(Pallets) Storyboards Storyboard scenes Segues Xibs Reusable cell identifiers Другие ресурсы(xml, json, plist) 4220 4560 1066
  15. Project’s resources Assets Catalogs Fonts Storyboards Parser Internal representation JSON

    … Stencil Template Template engine Generated code 26 SWIFT
  16. Natalie var output = "" output += header.description output +=

    "import \(os.framework)\n" for module in storyboardCustomModules { output += "import \(module)\n" } output += "\n" output += "// MARK: - Storyboards\n" output += "\n" output += "extension \(os.storyboardType) {\n" for (signatureType, returnType) in os.storyboardInstantiationInfo { output += " func instantiateViewController<T: \(returnType)>(ofType type: T.Type) -> T? where T: IdentifiableProtocol {\n" output += " let instance = type.init()\n" output += " if let identifier = instance.storyboardIdentifier {\n" output += " return self.instantiate\(signatureType)(withIdentifier: identifier) as? T\n" output += " }\n" output += " return nil\n" output += " }\n" output += "\n" } output += "}\n" output += "\n" output += "protocol Storyboard {\n" output += " static var storyboard: \(os.storyboardType) { get }\n" output += " static var identifier: \(os.storyboardIdentifierType) { get }\n" output += "}\n" output += "\n" output += "struct Storyboards {\n" for file in storyboards { output += file.storyboard.processStoryboard(storyboardName: file.storyboardName, os: os) } output += "}\n" output += "\n"
  17. 28 let icon = UIImage(named: "settings-icon") let font = UIFont(name:

    "San Francisco", size: 42) let color = UIColor(named: "indictator highlight") let viewController = CustomViewController(nibName: "CustomView", bundle: nil) let string = String(format: NSLocalizedString("welcome.withName", comment: ""), locale: NSLocale.current, "Arthur Dent") let icon = R.image.settingsIcon() let font = R.font.sanFrancisco(size: 42) let color = R.color.indicatorHighlight() let viewController = CustomViewController(nib: R.nib.customView) let string = R.string.localizable.welcomeWithName("Arthur Dent") Когда не знал про кодогенерацию: Сейчас:
  18. 31 import SwiftyJSON class Video { var title: String var

    description: Int var likesCount: String var isLiked: Bool init(_ json: JSON) { title = json["title"].stringValue description = json["description"].intValue likesCount = json[“likes_count"].stringValue isLiked = json[“is_liked”].boolValue } }
  19. class StreetAddress { let number: String let street: String let

    unit: String? init(_ number: String, _ street: String, unit: String? = nil) { self.number = number self.street = street self.unit = unit } } extension StreetAddress: Equatable { static func == (lhs: StreetAddress, rhs: StreetAddress) -> Bool { return lhs.number == rhs.number && lhs.street == rhs.street && lhs.unit == rhs.unit } } 32
  20. Sourcery 33 Sourcery сканирует ваш код, применяет к нему вами

    созданные шаблоны и генерирует Swift код, позволяя использовать техники метапрограммирования для экономии времени и минимизации потенциальных ошибок
  21. Метапрограммирование — код который пишет код. 34 Изменение программы самой

    программой во время выполнения (Obj-c runtime) На практике это обычно автоматическая кодогенерация боилплейт кода
  22. why stop there 39 AutoCases.stencil AutoEquatable.stencil AutoHashable.stencil AutoLenses.stencil AutoInterface.stencil AutoPropertiesProtocol.stencil

    AutoMockable.stencil Decorator.swifttemplate LinuxMain.stencil Diffable.stencil TypeErase.stencil AutoModelGeneratable.stencil
  23. Итоги 40 Уменьшайте количество бойлейплейт кода в ваших проектах Пишите

    код один раз и тестируйте один раз Ограничивайте риск человеческой ошибки Меньшие затраты на поддержку кода
  24. Resources Ole Begemann: How to Read the Swift Standard Library

    Source Fatal Error podcast: Episode 11. Codegen Sourcery Docs: Link 41 Introduction to Generamba
  25. 42

  26. the Generate Your Boilerplate tool Это препроцессор написанный командой Swift

    для внутреннего использования на swift проекте. 43
  27. 44 Основные причины использования gyb командой swift: • для имплементации

    почти идентичного кода для разных типов (пример: Int, UInt, Int16, Int32, Int64…) • Временное решение для нереализованных фич языка (пример: Protocol conformance)