Принципы организации проекта в iOS для быстрого понимания кода после паузы в разработке и использование совместных классов в iOS и MacOS

в 13:34, , рубрики: ios development, Песочница, разработка под iOS, разработка приложений, метки: , ,

По специфике работы мне приходится вести несколько (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

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js