По специфике работы мне приходится вести несколько (5-10) проектов одновременно, и часто возвращается к кодированию проекта после значительной паузы, месяц и более. Такая особенность требует организации кода, которая позволит очень быстро вспомнить архитектуру проекта.
Так же для экономии времени и ресурсов я использую общие классы для MacOS и iOS версий моего приложения.
Для тех, кому интересны особенности моего опыта — прошу под кат.
Во многих проектах, которые я изучал в процессе работы, я видел чрезмерные усложнения в организации классов. Излишнее дробление файлов вызывает путаницу в понимании глобальной структуры кода. Увлечение делением классов усложняет понимание функционала отдельных частей кода.
В итоге я остановился на таких принципах организации проекта:
1. Я отказался от подклассов в проекте, кроме необходимых — UIViewController, UITableViewCell
2. Все взаимодействие с внешним сервером вынесено в отдельный класс (способы его организации я расскажу во второй части, этот класс у меня мультиплатформенный)
3. Все подклассы UIViewControllers названы правильными именами (MyViewConfroller1 — неправильное имя, MessagesViewController связанный с кнопкой Messages — правильное имя)
4. Все подклассы TableViewCells находятся рядом с ViewControllers и названы правильно (MyTableViewCell — неправильно, MessagesTableViewCell — правильно)
5. В ресурсах я создаю папки со всеми картинками и размещаю эти папки рядом с файлом класса (папка MessagesImages).
Такая организация помогает быстро добраться до нужного файла и в итоге кода из левой части Xcode.
Объединение в один проект кода для MacOS и IOS дает существенный выигрыш в скорости разработки/ доработки. Изменение (исправление) в общих классах применяется сразу для обеих систем. Есть возможность использовать одни и те же картинки для обеих проектов. Однако такая организация имеет свои особенности:
1. При создании таргета сразу предусмотрите AppDelegate prefix. Идеально — IOSAppDelegate MacOSAppDelegate
2. Xcode имеет баг с отображением кода в разделенных панелях, поэтому нежелательно называть одинаковыми именами подклассы UIViewController и NSViewController в обеих проектах. MessagesIOSViewController и MessagesMacOSViewController соответственно — хорошие названия.
Общие классы выносятся в топ проекта в отдельную директорию с хорошим названием ServerIteractionClasses например.
Так как все взаимодействие с сервером подразумевает часто приличное ожидание, то любое взаимодействие с сервером из view controllers происходит следующим образом:
1. Создание экземпляра класса
ClientController *clientController = [[ClientController alloc] init];
clientController.sender = self;
где sender это
@property (strong) id sender;
Старт необходимой функции
[clientController startPasswordRecoveryForEmail:email];
где
-(void) updateUIwithMessage:(NSString *)message andProgressPercent:(NSNumber *)percent;
{
if (self.sender && [self.sender respondsToSelector:@selector(updateUIWithData:)]) {
[sender performSelector:@selector(updateUIWithData:) withObject:[NSArray arrayWithObjects:message,percent,nil]];
}
}
-(void) startPasswordRecoveryForEmail:(NSString *)email;
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^(void) {
//// ваша функция
[self updateUIwithMessage:[NSString stringWithFormat:@"startPasswordRecoveryForEmail:%@",statusValue] andProgressPercent:nil];
});
}
Далее все изменения в интерфейсе идет только калбэками в соотвествующем view controller (не забудем перейти в main thread для изменений интерфейса):
#pragma mark - ClientControllerDelegate
-(void)updateUIWithData:(NSArray *)data;
{
//NSLog(@"ClientControllerDelegate:>>>>>>>%@",data);
NSString *message = [data objectAtIndex:0];
NSNumber *progressNumber = nil;
if (data.count == 2) progressNumber = [data objectAtIndex:1];
if ([message isEqualToString:@"didSendBodyData"] || [message isEqualToString:@"didSendBodyData"] || [message isEqualToString:@"didReceiveData"]) {
dispatch_async(dispatch_get_main_queue(), ^(void) {
self.progressDownloading.progress = progressNumber.floatValue;
self.progressDownloading.hidden = NO;
});
} else {
dispatch_async(dispatch_get_main_queue(), ^(void) {
self.progressDownloading.hidden = YES;
});
}
BOOL isDidFailWithError = ([message rangeOfString:@"didFailWithError"].location != NSNotFound);
if (isDidFailWithError) {
NSArray *allBlocks = [message componentsSeparatedByString:@":"];
NSString *errorMessageReceived = @"";
if (allBlocks.count > 1) errorMessageReceived = [allBlocks objectAtIndex:1];
dispatch_async(dispatch_get_main_queue(), ^(void) {
[self showErrorMessage:errorMessageReceived];
});
}
Методика позволяет передавать и данные в callback, разделенные двоеточием.
Такой вариант а позволяет очень быстро переносить на macos с iOS необходимый код ( не забываем называть элементы интерфейса одинаково в iOS и macos версиях).
Если понравится статья, продолжу про организацию северной части.
Автор: oleksiivinogradov