Когда я начал разрабатывать на IOS, мне всегда было интересно узнать о передовых методах, используемых гигантскими компаниями. Как выглядит их структура проекта? Какую архитектуру они используют? Какие библиотеки сторонних разработчиков наиболее популярны? Это моё желание опираться на опыт других людей и не тратить время на проблемы, которые уже решены.

Прошло 4 года. Я работал со многими клиентами и имел много умных людей в своей команде, чтобы обсуждать эти методы. Поэтому в этой статье я хочу рассказать о не очень очевидных практиках, которые я использую сейчас для IOS разработки.

Вы также можете их использовать, критиковать или улучшать.

Давайте начнем. 🚀

1 - Избегать чрезмерного использования ссылочных типов

Вы должны использовать только ссылочные типы для живых объектов. Что я подразумеваю под «живыми»? Давайте рассмотрим ниже пример:

struct Car {
  let model: String
}
class CarManager {
  private(set) var cars: [Car]
  func fetchCars()
  func registerCar(_ car: Car)
}

Это мертвый код, который предоставляет данные и имеет просто значение. Он ничем не управляет. Так что это не должно жить и, поэтому, его не надо определять как ссылочный тип.

С другой стороны, CarManager должен быть активным объектом. Поскольку это объект, который запускает сетевой запрос, ждёт для ответа и сохраняет все загруженные машины. Вы не можете выполнить какое-либо асинхронное действие для типа значения, потому что, опять же, он мертв. Сам объект CarManager, который живёт в сфере действия, забирает автомобили с сервера и регистрирует новые автомобили, как настоящий менеджер в реальной компании.

Эта тема заслуживает собственного блога, поэтому я не буду углубляться. Но я рекомендую посмотреть эту ссылку автора Andy Matuschak или презентацию на WWDC, чтобы понять, почему это так важно для создания приложений, защищенных от вирусов.

2 - Никогда не использовать не явно развернутые свойства

Вы не должны использовать не явно развернутые свойства по умолчанию. Вы даже можете забыть их в большинстве случаев. Но могут быть некоторые особые случаи, когда вам нужно это решение, чтобы порадовать компилятор. И важно понять логику, лежащую в основе этого.

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

Предположим, у нас есть nameLabel outlet.

class SomeView: UIView {
  @IBOutlet let nameLabel: UILabel
}

Если вы напишете это так, компилятор попросит вас определить инициатор и присвоить none-nil значение к nameLabel. Это совершенно нормально, потому что вы утверждаете, что SomeView всегда имеет nameLabel. Но вы не можете этого сделать, потому что привязка будет выполняться за кулисами в initWithCoder. Вы уверены, он не будет nil, так что нет необходимости делать проверку на nil. Но в тоже время, вы не можете (или не должны) его заполнять.

В этом случае вы определяете его как не явно развернутое свойство. Это похоже на подписание контракта с компилятором:

Вы: «Это никогда не будет nil, так что перестать предупреждать меня об этом».
Компилятор: «ОК».

class SomeView: UIView {
  @IBOutlet var nameLabel: UILabel!
}

Популярный вопрос:
Должен ли я использовать не явно развертывание при извлечении ячейки из table view?

Не очень популярный ответ:
Нет. Или по крайне мере выводить сообщение об ошибке:

guard let cell = tableView.dequeueCell(...) else {
  fatalError("Cannot dequeue cell with identifier \(cellID)")
}

Swift Syntax Cheat Codes

3 - Избегайте чрезмерного использования AppDelegate

AppDelegate — это не место для хранения в PersistentStoreCoordinator ваших глобальных объектов, вспомогательных функций и т.д. Это как любой класс, который реализует протокол. Забудьте об этом.

Я понимаю, что у вас есть важные дела в applicationDidFinishLaunching, но слишком легко выйти из-под контроля, когда проект растет. Всегда старайтесь создавать отдельные классы (и файлы) для управления различными видами обязанностей.

👎 Не стоит делать так:

let persistentStoreCoordinator: NSPersistentStoreCoordinator
func rgb(r: CGFloat, g: CGFloat, b: CGFloat) -> UIColor { ... }
func appDidFinishLaunching... {
  Firebase.setup("3KDSF-234JDF-234D")
  Firebase.logLevel = .verbose
  AnotherSDK.start()
  AnotherSDK.enableSomething()
  AnotherSDK.disableSomething()
  AnotherSDK.anotherConfiguration()
  persistentStoreCoordinator = ...
  return true
}

Swift Syntax Cheat Codes

Разработчик в AppDelegate.swift

👍 Лучше сделать так:

func appDidFinishLaunching... {
  DependencyManager.configure()
  CoreDataStack.setup()
  return true
}

4 - Избегайте чрезмерного использования параметров по умолчанию

Вы можете установить значение по умолчанию для параметров в функции. Это очень удобно, потому что иначе вы, в конечном итоге, создадите разные версии той же функции, что и ниже, просто для добавления синтаксиса.

func print(_ string: String, options: String?) { ... }
func print(_ string: String) {
  print(string, options: nil)
}

С параметрами по умолчанию это становится:

func print(_ string: String, options: String? = nil) { ... }

Swift Syntax Cheat Codes

Легко правда? Очень просто установить цвет по умолчанию для вашего пользовательского интерфейса, чтобы предоставить параметры по умолчанию для вашей функции или назначить тайм-аут по умолчанию для вашего сетевого компонента. Но, вы должны быть осторожны, когда дело доходит до зависимостей.

Давайте рассмотрим следующий пример:

class TicketsViewModel {
  let service: TicketService
  let database: TicketDatabase
  init(service: TicketService,
       database: TicketDatabase) { ... }
}

Использование цели в приложении:

let model = TicketsViewModel(
  service: LiveTicketService()
  database: LiveTicketDatabase()
)

Использование цели в тесте:

let model = TicketsViewModel(
  service: MockTicketService()
  database: MockTicketDatabase()
)

Несколько причин, по которым у вас есть протоколы для сервиса (TicketService) и базы данных (TicketDatabase), — это абстрагирование от любых конкретных типов. Это позволяет вам внедрять любую реализацию, которая вам нравится в TicketsViewModel. Поэтому, если вы введете LiveTicketService в качестве параметра по умолчанию в TicketsViewModel, это фактически сделает TicketsViewModel зависимым от LiveTicketService, который является конкретным типом. Это противоречит тому, что мы пытаемся достичь в первую очередь, верно?

Еще не уверены?

Изображение, в котором указаны цели приложений и тестов. TicketsViewModel обычно добавляется к обеим целям. Затем вы добавили бы реализацию LiveTicketService в целевое приложение, а реализацию MockTicketService — в тестовую. Если вы создадите зависимость между и MockTicketService и LiveTicketService, ваш тестовый объект не будет компилироваться, потому что он не должен знать о LiveTicketService!

5 - Использование переменных параметов

Потому что это круто, супер легко реализовать и мощно.

func sum(_ numbers: Int...) -> Int {
  return numbers.reduce(0, +)
}
sum(1, 2)       // Returns 3
sum(1, 2, 3)    // Returns 6
sum(1, 2, 3, 4) // Returns 10

6 - Использование нужных типов

Swift Syntax Cheat Codes

Swift поддерживает внутренние типы, поэтому вы можете(должны) вставлять типы везде, где это имеет смысл.

👎 Не стоит делать так:

enum PhotoCollectionViewCellStyle {
  case default
  case photoOnly
  case photoAndDescription
}

Вы никогда не будете использовать это перечисление вне PhotoCollectionViewCellStyle, поэтому нет смысла помещать его в глобальную область видимости.

👍 Лучше сделать так:

class PhotoCollectionViewCell {
  enum Style {
    case default
    case photoOnly
    case photoAndDescription
  }
  let style: Style = .default
  // Implementation...
}

Это имеет больше смысла, потому что Style является частью PhotoCollectionViewCell и на 23 символа короче, чем PhotoCollectionViewCellStyle.

Swift Syntax Cheat Codes

7 - По умолчанию идет финальная версия 🏁

По умолчанию классы должны быть окончательными, поскольку обычно они не предназначены для расширения.

8 - Больше имен в ваших константах

Знаете ли вы, что можете правильно называть свои глобальные константы вместо использования уродливых префиксов, таких как PFX или k?

👎 Не стоит делать так:

static let kAnimationDuration: TimeInterval = 0.3
static let kLowAlpha = 0.2
static let kAPIKey = "13511-5234-5234-59234"

👍 Лучше сделать так:

enum Constant {
  enum UI {
    static let animationDuration: TimeInterval = 0.3
    static let lowAlpha: CGFloat = 0.2
  }
  enum Analytics {
    static let apiKey = "13511-5234-5234-59234"
  }
}

Мое личное предпочтение — использовать только С вместо Constant, потому что это достаточно очевидно. Вы можете выбрать, как пожелаете.

9 - Избегайте не правильного использования

_ является переменной заполнителем, которая содержит не используемые значения. Это способ сообщить компилятору «я не забочусь о значении», чтобы он не стал жаловаться.

👎 Не стоит делать так:

if let _ = name {
  print("Name is not nil.")
}

Не обязательный элемент, вы можете проверить, пуст ли он. Вам не нужно вынимать все, если вам ничего не нужно.

👍 Лучше сделать так:

Nil-контроль:

if name != nil {
  print("Name is not nil.")
}

Неиспользуемый возврат:

_ = manager.removeCar(car) // Returns true if successful.

Блоки завершения:

service.fetchItems { data, error, _ in
  // Hey, I don't care about the 3rd parameter to this block.
}

10 - Избегайте двусмысленных имен методов

Это действительно относится к любому языку программирования, который должен понимать человек. Люди не должны прилагать дополнительные усилия для понимания того, что вы имеете в виду, уже трудно понять язык программирования!

Например, проверьте этот вызов метода:

driver.driving()

Что это на самом деле? Мои догадки были бы:

  1. Он обозначает водителя как движущую силу.
  2. Он проверяет, водит ли водитель, и возвращает true, если это так.

Swift Syntax Cheat Codes

Если кому-то нужно увидеть реализацию, чтобы понять, что делает метод. Особенно, если вы работаете в команде, передавая старые проекты, вы будете читать больше, чем пишете код. Так что будьте предельно ясны, когда называете вещи, чтобы люди не страдали, понимая ваш код.

11 - Избегайте большого вывода информации в журнале

Прекратите печатать каждую маленькую ошибку или ответ, который вы получите. Шутки в сторону. Это эквивалентно тому, что не печатается вообще. Потому что в какой-то момент вы увидите, что ваше окно журнала скроллится от ненужной информации.

Swift Syntax Cheat Codes

👍 Лучше сделать так:

  1. Используйте уровень журнала ошибок в используемых вами фреймворках.
  2. Используйте фреймворки ведения журнала (или реализуйте его самостоятельно), которые позволяют вам устанавливать уровни журналов. Некоторые популярные фреймворки: XCGLogger, SwiftyBeaver
  3. Прекратите использовать журнал в качестве основного источника для отладки. Xcode предоставляет мощные инструменты для этого. Проверьте этот пост в блоге, чтобы узнать больше.

12 - Избегайте отключения неиспользуемого кода

Прекратите комментировать фрагменты кода. Если вам это не нужно, просто удалите его! Так просто. Я никогда не решал проблему путем включения устаревшего кода. Поэтому очищайте свой беспорядок и делайте свой код читаемым.

Что, если я скажу тебе ...

Swift Syntax Cheat Codes

... что вы можете достичь большинства из них с помощью автоматизации? См. Статью Candost на «Использование SwiftLint и опасность для стремительных лучших практик».

Спасибо за прокрутку полностью!

Автор оригинала: Göksel Köksal
Ссылка на оригинальную статью