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

Набить морду павлину
Яйца раскрасить — не картошки пожарить.
Для каждой палитры есть подходящий материал, а когда речь идет о продукте, который потом еще и съедается — не хочется делать это как попало.

Можно!
Все, что вам понадобится — это iPhone, хотя на первых порах можно обойтись и без него. Главное — припасите яиц.
Нарисовав первые прототипы интерфейса мы прикинули, сколько у нас будет разметок, орнаментов, как они будут выглядеть и, наконец, отправились готовить контент для приложения. Спустя несколько дней у нас получилось немного-немало 350 разных орнаментов для 5 цветов и 5 разметок.
“Мотор, камера… Экшн!”
Тут же были нарисованы первые прототипы, на которых были распределены основные экраны приложения, доступный функционал и основная механика расраски. Естественно, все тестировалось на яйцах.

Казалось, что при такой простой задаче должно найтись множество способов оформить идею в интерфейсе, однако, оставалось противоречивое мнение по разным мелочам, вроде “нужно ли переключаться на следующий этап редактирования при выборе какой-то зоны, или нет?” и так далее

Каждое яйцо было заботливо отрендерено вручную — на это ушло немало времени.
“Почему бы не рендерить в самом приложении по модели?” — спросите вы. Отвечаем: каждый орнамент натягивался на яйцо по-своему — такую работу нельзя доверить роботам.
Подводные яйца
Первую версию мы сделали на Кокосе, и основные сложности были только со скрещиванием объектов сцены с наследниками UIView, но и это можно решить, разбив объекты по разным сценам.

Задача, которая появилась на горизонте: слишком много картинок, нужно уложиться в 50 мегабайт (на дворе был 2013, в iOS 7 лимит увеличился вдвое), иначе приложение не получится скачать через сотовую сеть. Пожали все в джипеги, для каждой зоны нарисовали маску, по которой фильтровали слой с каждой зоной. Уложились!
В синий — прищемите дверью
Проверяйте дважды все, прежде чем отправляете приложение с In-App Purchase внутри. Особенно — не забудьте кнопку восстановления для non-consumable покупок. Мы получили свой заслуженный reject и отправились на доработку к следующему году.
Огромного внимания заслуживает тестирование In-App Purchase. Тестирование при помощи тестовых iTunes аккаунтов очень своеобразное — лучше один раз попробовать. И да, если вы публикуете новую версию, и определенные In-App’ы доступны только в ней — не забудьте, что это нужно указать перед отправкой приложения на review.
Те же яйца — вид сбоку
Быстро перевели проект на SpriteKit — логика осталась той же, но стало значительно проще работать с внутренней структурой объектов для отрисовки, для этого даже не пришлось лезть CoreGraphics:
SKSpriteNode *patternNode = [SKSpriteNode spriteNodeWithImageNamed:spriteImageName];
SKSpriteNode *maskNode = [SKSpriteNode spriteNodeWithImageNamed:maskImageName];
SKCropNode *zoneNode = [SKCropNode node];
[zoneNodeaddChild:patternNode];
[zoneNode setMaskNode:maskNode];
Зачем изобретать велосипед? Вернулись к стандартной навигации UINavigationController, сейчас кастомизируй — не хочу.

Симулировать поведение UIScrollView для наследника SKNode можно положив его в один контейнер с UIScrollView, а сам контейнер положить поверх SKView который отрисовывает SKScene, и реализовать для такого контейнера протокол UIScrollViewDelegate и далее изменять положение SKNode внутри контейнера в зависимости от параметра contentOffset:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
// See also: http://stackoverflow.com/a/19197965/429253
_selectorNode.position = CGPointMake(- scrollView.contentOffset.x + 160.0f,
_selectorNode.position.y);
}
Анатомия яйца
В качестве счетчика был подключен Google Analytics, это потребовало минимального количества времени на интеграцию, и предоставило кучу рычагов прямо из коробки. Экраны, события, модель поведения пользователей — все добавляется в пару строчек.
Отслеживание событий:
[[[GAI sharedInstance] defaultTracker] send:
[[GAIDictionaryBuilder createEventWithCategory:@"Share"
action:@"Facebook"
label:nil
value:nil] build]];
Отслеживание экранов:
id tracker = [[GAI sharedInstance] defaultTracker];
[tracker set:kGAIScreenName value:@"Store: Super Eggs"];
[tracker send:[[GAIDictionaryBuilder createAppView] build]];
Особо доставляет раздел Real-Time:

И да, не забудьте отрубить статистику для симулятора и тестовых запусков — зачем в статистике мусор?
#if TARGET_IPHONE_SIMULATOR || DEBUG
[[GAI sharedInstance] setOptOut:YES];
#endif
Сетевые яйца

Создание «приложение» для Facebook — это стотыщ баннеров разного размера. Вам все равно придется их сделать. Здесь тоже придется какое-то время подождать (говорят про три рабочих дня, обычно делают за день), прежде чем фейсбучное приложение появится в App Center.
Также, логин при помощи Facebook SDK очень чувствителен к разным условиям: у зависимости от того, установлено на телефоне приложение Facebook или нет, залогинены вы в социальных сетях в настройках iOS, или нет — результат будет разный. Для SSO авторизации не забудьте правильно прописать URL types в настройках приложения в XCode (заменив девятки на идентификатор вашего приложения в Facebook‘е):

Фотографию заливаем с помощью FBRequestConnection и указываем, кого мы на этой фотографии отмечаем:
FBRequestConnection *photoUploadConnection = [[FBRequestConnection alloc] init];
FBRequest *request = [FBRequest requestForUploadPhoto:_image];
[request.parameters setObject:[NSString stringWithFormat:@"[{\"tag_uid\":\"%@\"}]", friendId] forKey:@"tags"];
[request.parameters setObject:K_APP_PROMO_TEXT forKey:@"name"];
[photoUploadConnection addRequest:request
completionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
if (!error){
// handle success
}
} batchEntryName:@"photopost"];
[photoUploadConnection start];
Заливать картинки ВКонтакт немного сложнее, придется сначала запросить адрес сервера для загрузки картинки, и только после этого запостить ее другу на стену (в отличие от Facebook, где мы добавляем фотку в альбом и отмечаем на ней кого-то:
// 1. Get upload server
NSDictionary *result = [self sendVkRequestForMethod:@"photos.getWallUploadServer"
params:[NSDictionary dictionaryWithObjectsAndKeys:
friendId, @"uid",
K_ACCESS_TOKEN, @"access_token", nil]
error:&error];
// 2. Upload image
if (!error) {
result = [self sendPOSTRequest:[[result objectForKey:@"response"] objectForKey:@"upload_url"]
withImageData:UIImageJPEGRepresentation(_image, 75)];
}
// 3. Save image
if (!error) {
result = [self sendVkRequestForMethod:@"photos.saveWallPhoto"
params:[NSDictionary dictionaryWithObjectsAndKeys:
friendId, @"uid",
K_ACCESS_TOKEN, @"access_token",
[result objectForKey:@"hash"], @"hash",
[result objectForKey:@"photo"], @"photo",
[result objectForKey:@"server"], @"server", nil]
error:&error];
}
// 4. Post on wall
if (!error) {
result = [self sendVkRequestForMethod:@"wall.post"
params:[NSDictionary dictionaryWithObjectsAndKeys:
friendId, @"owner_id",
K_ACCESS_TOKEN, @"access_token",
K_APP_PROMO_TEXT, @"message",
[[[result objectForKey:@"response"] lastObject]objectForKey:@"id"], @"attachment",
nil]
error:&error];
}
C Instagram‘ом проще, сначала сохраняем картинку с расширением *.igo, потом открываем ссылку через UIDocumentInteractionController.
// 1. Save file with *.igo extension
NSString *path = [[self applicationDocumentsDirectory].path
stringByAppendingPathComponent:@"EggMaker.igo"];
NSError *error = nil;
[UIImagePNGRepresentation(postcardImage) writeToFile:path
options:0
// 2. Open with UIDocumentInteractionController
if (!error) {
NSURL *instagramURL = [NSURL URLWithString:@"instagram://app"];
if ([[UIApplication sharedApplication] canOpenURL:instagramURL]) {
self.documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:
[NSURL fileURLWithPath:path]];
self.documentInteractionController.UTI = @"com.instagram.exclusivegram";
self.documentInteractionController.annotation = [NSDictionary dictionaryWithObject:K_SHARE_TEXT
forKey:@"InstagramCaption"];
[self.documentInteractionController presentOpenInMenuFromRect:self.view.frame inView:self.view animated:YES];
}
}
Интерактивные яйца
Всегда не хватало возможности сделать интерактивную перезентацию своего приложения, особенно если приложение платное, а правилами App Store демо версии запрещены? С этой задачей справился App.io:

Добавить приложение можно зарегистрировав бесплатный аккаунт, бесплатно доступно множество настроек отображения и встраивания такого интерактивного демо через параметры ссылки. С недавнего времени такое демо своего приложения можно встраивать в виде полноэкранной интерактивной рекламы.
Скачай Яйцедел в App Store: http://appstore.com/Яйцедел (кто спрашивал про короткие ссылки?)
Спасибо что вы с нами, обязательно попробуйте Super Eggs — там есть с котиками и пингвинятами!
Наши страницы в Фейсбуке и ВКонтакте
И на будущее: берегите яйца!
Христос Воскресе!