Разработка iOS приложения - процесс достаточно долгий и скрупулезный, и часто бывают ситуации, особенно в больших проектах, когда код у разработчика на устройстве и у конечного пользователя должен выдавать разные результаты. Это может показаться немного абсурдным, но я сейчас все объясню.

Используется:

  • Swift 5

  • Xcode 13.4.1

Вступление

Предположим, мы работаем с определенной фичей, которую мы хотим включить только на сборке Debug, или inner, но не хотим, чтоб эта функциональность попала в AppStore при следующей публикации, комментировать код новой фичи перед публикацией в AppStore - идея так себе, потому что можно случайно что-то сломать, или банально забыть это сделать, и согласитесь, было бы удобно иметь где нибудь настройку, в которой мы сможем указать, в какой сборке включить данную фичу, а в какой выключить, и спокойно писать код, не переживая, что ненужный код попадет туда, куда не следует

Или вот, ситуация, которую вы встретите в 99 процентах случаев: В настоящих проектах очень часто бывает, что для разработки и для релиза используются разные адреса сервера, с которого получаем данные, и, я считаю, было бы очень удобно, написать примерно так:

func fetchData(completion: @escaping ((Response) -> Void)) {
    let serverURL = ApplicationConfig.serverURL
    
    // some code...
}

Данный код при работе, будет опираться на debug сервер, и при публикации, будет смотреть уже на версию release. Если нам нужно вдруг изменить адрес, мы можем сделать это в одном месте, и все сразу будет работать как положено.

В обоих примерах мы можем использовать User-Defined

Что такое User-Defined?

User-Defined - это определяемый разработчиком пресет настроек для разных конфигураций проекта, они же schemes, если говорить простым языком, это настройки, которые мы задаем для каждой из наших сборок, если все еще сложно, не переживайте, дальше Вы все поймете.

Как добавить настройку?

Для того, чтобы наглядно показать работу с User-Defined, давайте использовать один из наших примеров выше, возьмем случай с serverURL, потому что он более распространенный.

Представим, что у нас есть 2 сервера, откуда мы получаем данные

  • Сервер для разработки - https://fitnessPro-develop/api/

  • Сервер для релиза - https://fitnessPro/api/

Мы добавим новую настройку с адресом сервера, а для разных схем укажем, какую использовать, переходим в настройки проекта, и в верхнем меню выбираем

Editor -> Add Build Setting -> Add User-Defined Setting

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

Принято указывать поля через uppercased snake case, поэтому напишем так:

SERVER_URL - имя нашей новой настройки, и для схемы debug установим адрес дебаг сервера, и для релиза - релиз сервер соответственно

Но, как полагается, есть один нюанс. Мы не можем напрямую из кода получить доступ к этим настройкам, и есть несколько способов, как в итоге их получить. Самым популярным является добавление новых полей в наш info.plist файл, мы поступим именно так.

Открываем наш info.plist файл, нажимаем знак "+" для добавления нового значения, в ключ пишем название настройки, но уже в нашем, родном, camel case, Вы можете указать тут любое название, но я рекомендую указывать такое же, как в настройках User-Defined, чтобы не путаться по проекту, особенно актуально, если работают много разработчиков.

В значение мы пишем название нашей новой настройки так, как мы записали её изначально в User-Defined, после того, как написали, оборачиваем значение в скобки, и ставим в начало знак доллара $

Должно получиться вот так:

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

Как получить данные из User-Defined

После того, как мы добавили нашу настройку в info.plist, мы можем получить ее следующим образом

let serverURL = Bundle.main.object(forInfoDictionaryKey: "serverURL")

Но такой способ не безопасен, мы можем ошибиться, поэтому немного напишем кода, и сделаем процесс быстрым, удобным и безопасным

Создадим новый класс, назовем его, например, ApplicationConfig, и добавим в него статическую вычисляемую переменную serverURL, в которой будем получать и возвращать данные из нашего Bundle.

class ApplicationConfig {
    static var serverURL: URL? {
        let urlString = Bundle.main.object(forInfoDictionaryKey: "serverURL") as? String ?? ""
        return URL(string: urlString)
    }
}

теперь мы можем получать адрес сервера вот таким образом

let serverURL = ApplicationConfig.serverURL
Альтернативное использование

В качестве альтернативы можно использовать расширение для Bundle, в котором мы создаем статическую переменную с нашим значением

extension Bundle {
    var serverUrl: URL? {
        let urlString object(forInfoDictionaryKey: "serverUrl") as? String?? ""
        return URL(string: urlString ?? "")
    }
}

Способов можно придумать много, и еще их можно комбинировать, если Ваша архитектура того требует

Заключение

Отлично, мы познакомились с User-Defined и научились правильно и красиво получать наши настройке в коде. Данная функциональность имеет очень много применений, и помогает делать код чище и красивее.

Комментарии (0)