2016-06-15 8 views
1

Кто-нибудь еще заметил, что проблема с исправлениями не исправляется должным образом. Я только что добавил некоторую начальную поддержку для своего приложения, но заметил, что они не отображали то, что я ожидал от них. Ради примера, и для удобства тестирования этого вопроса быстро я создать графикApple Clock ClockKit Осложнения не обновляют свои записи в хронологическом порядке, если циферблат не скрыт во время выполнения.

A -> B -> C 
0s 10s 20s 

Но все, что я хотел видеть, был осложнение записи в пребывании около мимо времени, которые должны отображаться B и C.

Мое обычное приложение не настроено на создание регулярных интервалов, подобных этому, у него есть много аспектов таймеров, которые он предоставляет, которые могут быть установлены пользователем, но один из таких аспектов позволяет пользователю запускать несколько таймеров при один раз, который будет завершен после того, как пользователь определит длительность, которую они выберут. В отличие от таймера приложения часов iOS, вы можете указать длительность таймера в секундах, и поэтому вполне возможно, что 2 таймера будут заканчиваться в течение нескольких секунд друг от друга, в целом, хотя более вероятно, что они будут на несколько минут друг от друга. Также не должно быть добавлено слишком много записей осложнений, хотя другие более сложные аспекты моего приложения могут легко добавить 10 или даже 100 записей сложности в зависимости от того, насколько сложна задача, которую пользователь установил в ней. Пока же этот простой пример легче обсуждать и тестировать.

Я обновил Xcode до последней версии (7.3.2) без каких-либо улучшений и отправил сборку на мой реальный телефон и посмотрел и снова не улучшился. До тех пор он действительно работал. При дальнейшей отладке я обнаружил, что могу сделать шкалу времени самой, просто опустив часы (чтобы отключить экран), а затем снова разбудить ее, пока она была в середине, выполняя мою временную шкалу. Сделав это, временная шкала затем будет работать должным образом с тех пор.

Я создал тестовое приложение, чтобы продемонстрировать проблему, которая полностью воспроизводит проблему, поэтому я отправлю ее на яблоко в отчете об ошибке. Просто подумал, что посмотрю, заметил ли кто-нибудь еще этот вопрос.

Кроме того, когда мой тест приложение выполняет я получаю следующий вывод протоколирования с ошибкой, что не имеет смысл

-[ExtensionDelegate session:didReceiveUserInfo:]:67 - complication.family=1 in activeComplications - calling reloadTimelineForComplication 
-[ComplicationController getTimelineStartDateForComplication:withHandler:]:43 - calling handler for startDate=2016-06-15 22:08:26 +0000 
-[ComplicationController getTimelineEndDateForComplication:withHandler:]:73 - calling handler for endDate=2016-06-15 22:08:46 +0000 
-[ComplicationController getCurrentTimelineEntryForComplication:withHandler:]:148 - calling handler for entry at date=2016-06-15 22:08:26 +0000 
-[ComplicationController getTimelineEntriesForComplication:afterDate:limit:withHandler:]:202 - adding entry at date=2016-06-15 22:08:36 +0000; with timerEndDate=2016-06-15 22:08:46 +0000 i=1 
getTimelineEntriesForComplication:afterDate:limit:withHandler: -- invalid entries returned. (1 entries before start date 2016-06-15 22:08:46 +0000). Excess entries will be discarded. 

Соответствующую информацию из этого журнала следующего

getTimelineStartDateForComplication - calling handler for startDate=22:08:26 
getTimelineEndDateForComplication - calling handler for endDate=22:08:46 
getCurrentTimelineEntryForComplication - calling handler for entry at date=22:08:26 
getTimelineEntriesForComplication:afterDate - adding entry at date=22:08:36 
getTimelineEntriesForComplication:afterDate:limit:withHandler: -- invalid entries returned. (1 entries before start date 22:08:46). Excess entries will be discarded. 

Что вас может видеть в ошибке из системы в конце, что она использует дату начала 22:08:46, что было на самом деле тем, что я сказал Clockkit, был моей конечной датой окончания, NOT the startDate. Я не уверен, связано ли это с поведением, которое я вижу, когда вижу ту же ошибку, когда она работает после того, как я скрываю/показываю экран.

Я разместил видео этого поведения в своем тестовом приложении онлайн here. Подробности этого тестового приложения следующие:

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

В моем удлинительном делегате, я получаю USERINFO из приложения IOS и график перезагрузки моих осложнения сроков

ExtensionDelegate.м

- (void)session:(WCSession *)session didReceiveUserInfo:(NSDictionary<NSString *, id> *)userInfo 
{ 
    DbgLog(@""); 

    WKExtension *extension = [WKExtension sharedExtension]; 
    DbgLog(@"self=%p; wkExtension=%p; userInfo=%@", self, extension, userInfo); 

    self.lastReceivedUserInfo = userInfo; 

    CLKComplicationServer *complicationServer = [CLKComplicationServer sharedInstance]; 
    for (CLKComplication *complication in complicationServer.activeComplications) 
    { 
    DbgLog(@"complication.family=%d in activeComplications - calling reloadTimelineForComplication", complication.family); 
    [complicationServer reloadTimelineForComplication:complication]; 
    } 
} 

Тогда в моем ComplicationController следующие методы для обработки Усложнение сторону вещей

ComplicationController.m

#define DbgLog(fmt, ...) NSLog((@"%s:%d - " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__) 

@interface ComplicationController() 

@end 

@implementation ComplicationController 

#pragma mark - Timeline Configuration 

- (void)getSupportedTimeTravelDirectionsForComplication:(CLKComplication *)complication withHandler:(void(^)(CLKComplicationTimeTravelDirections directions))handler 
{ 
    handler(CLKComplicationTimeTravelDirectionForward|CLKComplicationTimeTravelDirectionBackward); 
} 

- (void)getTimelineStartDateForComplication:(CLKComplication *)complication withHandler:(void(^)(NSDate * __nullable date))handler 
{ 
    NSDate *startDate; 

    WKExtension *extension = [WKExtension sharedExtension]; 
    assert(extension.delegate); 
    assert([extension.delegate isKindOfClass:[ExtensionDelegate class]]); 
    ExtensionDelegate *extensionDelegate = (ExtensionDelegate *)extension.delegate; 
    if (extensionDelegate.lastReceivedUserInfo) 
    { 
    NSDictionary *userInfo = extensionDelegate.lastReceivedUserInfo; 
    startDate = [userInfo objectForKey:@"date"]; 
    } 

    DbgLog(@"calling handler for startDate=%@", startDate); 
    handler(startDate); 
} 

- (NSDate*)getTimelineEndDate 
{ 
    NSDate *endDate; 

    WKExtension *extension = [WKExtension sharedExtension]; 
    assert(extension.delegate); 
    assert([extension.delegate isKindOfClass:[ExtensionDelegate class]]); 
    ExtensionDelegate *extensionDelegate = (ExtensionDelegate *)extension.delegate; 
    if (extensionDelegate.lastReceivedUserInfo) 
    { 
    NSDictionary *userInfo = extensionDelegate.lastReceivedUserInfo; 
    NSDate *startDate = [userInfo objectForKey:@"date"]; 
    NSNumber *duration = [userInfo objectForKey:@"duration"]; 
    NSNumber *count = [userInfo objectForKey:@"count"]; 

    NSTimeInterval totalDuration = duration.floatValue * count.floatValue; 
    endDate = [startDate dateByAddingTimeInterval:totalDuration]; 
    } 

    return endDate; 
} 

- (void)getTimelineEndDateForComplication:(CLKComplication *)complication withHandler:(void(^)(NSDate * __nullable date))handler 
{ 
    NSDate *endDate=[self getTimelineEndDate]; 

    DbgLog(@"calling handler for endDate=%@", endDate); 
    handler(endDate); 
} 

- (void)getPrivacyBehaviorForComplication:(CLKComplication *)complication withHandler:(void(^)(CLKComplicationPrivacyBehavior privacyBehavior))handler { 
    handler(CLKComplicationPrivacyBehaviorShowOnLockScreen); 
} 

#pragma mark - Timeline Population 

- (CLKComplicationTemplate *)getComplicationTemplateForComplication:(CLKComplication *)complication 
          forEndDate:(NSDate *)endDate 
          orBodyText:(NSString *)bodyText 
          withHeaderText:(NSString *)headerText 
{ 
    assert(complication.family == CLKComplicationFamilyModularLarge); 

    CLKComplicationTemplateModularLargeStandardBody *template = [[CLKComplicationTemplateModularLargeStandardBody alloc] init]; 

    template.headerTextProvider = [CLKSimpleTextProvider textProviderWithText:headerText]; 
    if (endDate) 
    { 
    template.body1TextProvider = [CLKRelativeDateTextProvider textProviderWithDate:endDate style:CLKRelativeDateStyleTimer units:NSCalendarUnitHour|NSCalendarUnitMinute|NSCalendarUnitSecond]; 
    } 
    else 
    { 
    assert(bodyText); 
    template.body1TextProvider = [CLKSimpleTextProvider textProviderWithText:bodyText]; 
    } 

    return template; 
} 

- (CLKComplicationTimelineEntry *)getComplicationTimelineEntryForComplication:(CLKComplication *)complication 
           forStartDate:(NSDate *)startDate 
             endDate:(NSDate *)endDate 
            orBodyText:(NSString *)bodyText 
            withHeaderText:(NSString *)headerText 
{ 
    CLKComplicationTimelineEntry *entry = [[CLKComplicationTimelineEntry alloc] init]; 
    entry.date = startDate; 
    entry.complicationTemplate = [self getComplicationTemplateForComplication:complication forEndDate:endDate orBodyText:bodyText withHeaderText:headerText]; 

    return entry; 
} 

- (void)getCurrentTimelineEntryForComplication:(CLKComplication *)complication withHandler:(void(^)(CLKComplicationTimelineEntry * __nullable))handler 
{ 
    // Call the handler with the current timeline entry 
    CLKComplicationTimelineEntry *entry; 
    assert(complication.family == CLKComplicationFamilyModularLarge); 

    WKExtension *extension = [WKExtension sharedExtension]; 
    assert(extension.delegate); 
    assert([extension.delegate isKindOfClass:[ExtensionDelegate class]]); 
    ExtensionDelegate *extensionDelegate = (ExtensionDelegate *)extension.delegate; 
    if (extensionDelegate.lastReceivedUserInfo) 
    { 
    NSDictionary *userInfo = extensionDelegate.lastReceivedUserInfo; 
    NSDate *startDate = [userInfo objectForKey:@"date"]; 
    NSNumber *duration = [userInfo objectForKey:@"duration"]; 
    //NSNumber *count = [userInfo objectForKey:@"count"]; 

    NSTimeInterval totalDuration = duration.floatValue; 
    NSDate *endDate = [startDate dateByAddingTimeInterval:totalDuration]; 

    entry = [self getComplicationTimelineEntryForComplication:complication forStartDate:startDate endDate:endDate orBodyText:nil withHeaderText:@"current"]; 
    } 

    if (!entry) 
    { 
    NSDate *currentDate = [NSDate date]; 
    entry = [self getComplicationTimelineEntryForComplication:complication forStartDate:currentDate endDate:nil orBodyText:@"no user info" withHeaderText:@"current"]; 
    } 

    DbgLog(@"calling handler for entry at date=%@", entry.date); 
    handler(entry); 
} 

- (void)getTimelineEntriesForComplication:(CLKComplication *)complication beforeDate:(NSDate *)date limit:(NSUInteger)limit withHandler:(void(^)(NSArray<CLKComplicationTimelineEntry *> * __nullable entries))handler 
{ 
    NSArray *retArray; 
    assert(complication.family == CLKComplicationFamilyModularLarge); 

    WKExtension *extension = [WKExtension sharedExtension]; 
    assert(extension.delegate); 
    assert([extension.delegate isKindOfClass:[ExtensionDelegate class]]); 
    ExtensionDelegate *extensionDelegate = (ExtensionDelegate *)extension.delegate; 
    if (extensionDelegate.lastReceivedUserInfo) 
    { 
    NSDictionary *userInfo = extensionDelegate.lastReceivedUserInfo; 
    NSDate *startDate = [userInfo objectForKey:@"date"]; 
    if ([startDate timeIntervalSinceDate:date] < 0.f) 
    { 
     assert(0); 
     // not expected to be asked about any date earlier than our startDate 
    } 
    } 

    // Call the handler with the timeline entries prior to the given date 
    handler(retArray); 
} 

- (void)getTimelineEntriesForComplication:(CLKComplication *)complication afterDate:(NSDate *)date limit:(NSUInteger)limit withHandler:(void(^)(NSArray<CLKComplicationTimelineEntry *> * __nullable entries))handler 
{ 
    NSMutableArray *timelineEntries = [[NSMutableArray alloc] init]; 
    assert(complication.family == CLKComplicationFamilyModularLarge); 

    WKExtension *extension = [WKExtension sharedExtension]; 
    assert(extension.delegate); 
    assert([extension.delegate isKindOfClass:[ExtensionDelegate class]]); 
    ExtensionDelegate *extensionDelegate = (ExtensionDelegate *)extension.delegate; 
    if (extensionDelegate.lastReceivedUserInfo) 
    { 
    NSDictionary *userInfo = extensionDelegate.lastReceivedUserInfo; 
    NSDate *startDate = [userInfo objectForKey:@"date"]; 
    NSNumber *duration = [userInfo objectForKey:@"duration"]; 
    NSNumber *count = [userInfo objectForKey:@"count"]; 

    NSInteger i; 
    for (i=0; i<count.integerValue && timelineEntries.count < limit; ++i) 
    { 
     NSTimeInterval entryDateOffset = duration.floatValue * i; 
     NSDate *entryDate = [startDate dateByAddingTimeInterval:entryDateOffset]; 

     if ([entryDate timeIntervalSinceDate:date] > 0) 
     { 
    NSDate *timerEndDate = [entryDate dateByAddingTimeInterval:duration.floatValue]; 

    DbgLog(@"adding entry at date=%@; with timerEndDate=%@ i=%d", entryDate, timerEndDate, i); 

    CLKComplicationTimelineEntry *entry = [self getComplicationTimelineEntryForComplication:complication forStartDate:entryDate endDate:timerEndDate orBodyText:nil withHeaderText:[NSString stringWithFormat:@"After %d", i]]; 
    [timelineEntries addObject:entry]; 
     } 
    } 

    if (i==count.integerValue && timelineEntries.count < limit) 
    { 
     NSDate *timelineEndDate = [self getTimelineEndDate]; 
     CLKComplicationTimelineEntry *entry = [self getComplicationTimelineEntryForComplication:complication forStartDate:timelineEndDate endDate:nil orBodyText:@"Finished" withHeaderText:@"Test"]; 
     [timelineEntries addObject:entry]; 
    } 
    } 

    NSArray *retArray; 

    if (timelineEntries.count > 0) 
    { 
    retArray = timelineEntries; 
    } 

    // Call the handler with the timeline entries after to the given date 
    handler(retArray); 
} 

#pragma mark Update Scheduling 

/* 
// don't want any updates other than the ones we request directly 
- (void)getNextRequestedUpdateDateWithHandler:(void(^)(NSDate * __nullable updateDate))handler 
{ 
    // Call the handler with the date when you would next like to be given the opportunity to update your complication content 
    handler(nil); 
} 
*/ 

- (void)requestedUpdateBudgetExhausted 
{ 
    DbgLog(@""); 

} 

#pragma mark - Placeholder Templates 

- (void)getPlaceholderTemplateForComplication:(CLKComplication *)complication withHandler:(void(^)(CLKComplicationTemplate * __nullable complicationTemplate))handler 
{ 
    CLKComplicationTemplate *template = [self getComplicationTemplateForComplication:complication forEndDate:nil orBodyText:@"doing nothing" withHeaderText:@"placeholder"]; 
    // This method will be called once per supported complication, and the results will be cached 
    handler(template); 
} 

@end 

Может быть, вы можете увидеть, если у вас есть те же проблемы с вашими собственными осложнениями в ваших собственных приложениях.

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

Спасибо за ваше время,

Приветствия

ответ

0

Ошибки о записи до даты начала является точным, а запись правильно отбрасывала.

Причина в том, что getTimelineEntriesForComplication:afterDate: предназначен для возврата будущих записей после указанной даты. То, что вы сделали, это вернуть запись до указанной даты.

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

Без публикуемую кода, чтобы изучить, я думаю, что ваш beforeDate:/afterDate: условный код восстанавливается.

Другие вопросы, касающиеся нескольких записей временной шкалы в минуту:

Что касается десяти второго временного интервала, я могу указать на несколько вопросов для Вас, чтобы рассмотреть:

сервер
  1. Усложнение будет запросить ограниченное количество записей.

    Предоставление ваших записей всегда будет на десять секунд друг от друга и что сервер запросил 100 записей (в прошлом или в будущем), что составляет 1000 секунд (менее 17 минут). Это представляет две проблемы в целом:

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

    • С такой короткой продолжительностью путешествие во времени фактически бесполезно, так как вращение цифровой короны может быстро перемещаться более чем на 16 минут в прошлое или будущее, где не было (изначально) каких-либо дополнительных записей для отображения ,

  2. Сервер осложнения кэширует ограниченного количества записей.

    Даже если вы продляете временную шкалу, сервер в конечном итоге отбросит (отменит) записи с расширенной временной шкалы. Опять же, предполагая, что 10-секундный интервал, сервер усложнения сможет хранить в кэше всего около 2 часов. Кроме того, у вас нет контроля над тем, какие записи будут отброшены.

  3. С точки зрения временного путешествия, 5 из каждых 6 записей временной шкалы никогда не будут отображаться во время путешествий во времени (поскольку переход во времени изменяется на минуты).

    Это, безусловно, представляет некоторую путаницу для пользователя, поскольку они не смогут просматривать каждую запись.

Примечание об ограничении обновления:

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

Что касается энергоэффективности, вы также должны учитывать жесткие ограничения, которые Apple налагает на обновление осложнений. Даже в watchOS 3 (где вам рекомендуется регулярно обновлять снимок усложнения и док-станции в фоновом режиме), вы столкнетесь с ограничениями на 4 обновления фона в час и 50 обновлений обновлений в день. См. watchOS - Show realtime departure data on complication.

+0

Спасибо за такой подробный ответ. Я отредактирую свое сообщение, чтобы показать код, а затем снова ответьте на ваши вопросы. Ура! – jimbobuk

+0

Опять же, большое спасибо за написание такого подробного ответа. Осложнения, похоже, не имеют большого освещения здесь из моих собственных поисков. Теперь я обновил исходное сообщение с большим количеством обсуждений на фоне моего обычного приложения вместе с полными фрагментами кода и ссылкой на полный проект, который должен работать на симуляторе. Надеюсь, теперь вы можете видеть, что мой метод afterDate осложнен. Вы можете видеть из журналов вывода, что даты, которые я передаю, верны. Вы также можете увидеть, что startDate системные ошибки - это на самом деле мой endDate !! – jimbobuk

+0

Важнейшим образом, усложнение может вести себя правильно, если пользователь выполняет технику блокировки/отображения часов, подразумевая ошибку в ОС, поскольку нет новой информации о осложнениях, отправленной в систему после начальной загрузки информации. Я также добавил небольшую заметку в начале оригинального сообщения, в котором обсуждалось основное приложение. Он НЕ создает эти 10-секундные записи усложнения интервала, я только что создал тестовое приложение, которое делает это так быстро и просто, чтобы воспроизвести проблему и показать, что она работает и не работает, как показано на моем видео, в зависимости от действия пользователя. – jimbobuk