Начинающим разработчикам: маленькая хитрость для публикации в App Store

В комментариях к прошлой статье, посвященной мифам и реалиям работы с App Store, высказалось достаточно много читателей, приложения которых по тем или иным причинам отклонялись. Это навело меня на мысль рассказать об одном способе, который помогает обойти строгих проверяющих AppStore. Ниже я вкратце опишу суть этого «лайфхака» с пояснениями и примерами кода.

AppStore

Популярная причина отказа в публикации – использование непубличного API (пункт 2.5 Apple Review Guidelines). Проверить, использует ли ваше приложение приватные функции, можно, например, с помощью такой утилиты: https://www.chimpstudios.com/appscanner/

Проект, к сожалению, недавно перестал поддерживаться, но местами еще актуален. Провериться стоит всем, даже если вы добропорядочный разработчик и ни разу не слышали имени Erica Sadun. Проблема может быть в ваших самостоятельно реализованных методах: если название какого-либо из них совпадет с непубличным системным методом, приложение будет отклонено по тому же пункту 2.5 ARG.

На мой взгляд, можно выделить два больших класса задач, для решения которых используется непубличное API:

  1. добавление в iOS несуществующей функциональности (работа на прямую с файловой системой, захват экрана и т.д.)
  2. кастомизация стандартных элементов управления (Apple до iOS 5 предоставляла очень ограниченные возможности настройки стандартных элементов управления)

Приложениям из пункта 1 не видать публикации в App Store, даже если проверка на приватные функции будет пройдена, так как из описания приложения очевидно, что запрещенные технологии все же используются. Второй случай более интересен, ведь можно реализовать собственные элементы управления, похожие на системные, но «с блэкджеком и …», то есть кастомизируемые. Эта задача достаточно сложная и трудозатратная, особенно если элемент управления активно использует анимацию.

Предположим, что наша задача — написать приложение с поддержкой iOS 4 и выше, и реализовать в нем переключатель оранжевого цвета, как в iOS Settings – Airplane Mode Switch:

Настройки

Следующий код позволит сделать, что требуется:

(void)viewDidLoad
{
[super viewDidLoad];
[testSwitch setAlternateColors:YES];
}

Но setAlternateColors — непубличный метод, он не объявлен в официальном описании UISwitch. Использование этого метода обнаруживается обычным strings и влечет за собой отказ в публикации в App Store. Для маскировки использования непубличного метода можно воспользоваться возможностями Objective-C Runtime, то есть не посылать сообщение объекту, а вызвать соответствующую функцию, найденную по селектору. Теперь об этом подробнее:

  1. Объявляем имя сообщения setAlternateColors как составленное из частей слов:

    NSString* methodName = @””;
    for (NSString *s in [NSArray arrayWithObjects:@"set",
    @"Alternate",
    @"Colors:",
    nil])
    methodName = [methodName stringByAppendingString:s];
    const char *methodNameUTF8String = [methodName UTF8String];

  2. Перебираем методы класса UISwitch, пока не найдем указатель на метод, который по имени совпадает с setAlternateColors:

    SEL methodSelector = nil;
    Method *methodsList = class_copyMethodList([UISwitch class], &count);
    for (i = 0; i < count; i++) { SEL s = method_getName(methodsList[i]); if (0 == strcmp(sel_getName(s), methodNameUTF8String)) { methodSelector = s; break; } }

  3. Вызываем функцию у соответствующего объекта (в нашем случае это theSwitch), используя селектор из п.2 и параметры, которые нужно использовать. В нашем случае это YES, как и в самом первом примере кода:

    if (nil != methodSelector)
    {
    IMP m_imp = method_getImplementation(methodsList[i]);
    m_imp(theSwitch, methodSelector, YES);
    }
    free(methodsList);

Тем самым получаем, что приватный метод не встречается целым словом в ASCII строках бинарного файла (как раз то, что показывает strings) и не может быть обнаружен системами поиска непубличных API-вызовов.

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