Моделирование симулятора iOS CoreMotion

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

Редактировать: я знаю, что CoreMotion еще не поддерживается в симуляторе. Но надеялся, что кто-нибудь подскажет, как это подделать.

ОБНОВЛЕНИЕ: чего я достиг до сих пор, используя метод swizzling. Для этого я переключился обратно на target-c, так как считаю, что лучше сначала попытаться разобраться на этом языке, а затем попытаться перевести его на Swift.

Я настроил класс метода swizzle так

+ (void)swizzleClass:(Class)class method:(NSString*)methodName {

  SEL originalSelector = NSSelectorFromString(methodName);
  SEL newSelector = NSSelectorFromString([NSString stringWithFormat:@"%@%@", @"override_", methodName]);

  Method originalMethod = class_getInstanceMethod(class, originalSelector);
  Method newMethod = class_getInstanceMethod(class, newSelector);

  method_exchangeImplementations(originalMethod, newMethod);
}

Я создал категорию для CMMotion и CMAccelerometerData.

- (void) simx_setAcceleration:(CMAcceleration)acceleration {
  NSValue *value = [NSValue value:&acceleration withObjCType:@encode(CMAcceleration)];
  objc_setAssociatedObject(self, ACCELERATION_IDENTIFIER, value, OBJC_ASSOCIATION_RETAIN);
}

- (CMAcceleration)override_acceleration {
  NSValue *value = objc_getAssociatedObject(self, ACCELERATION_IDENTIFIER);
  CMAcceleration acc;
  [value getValue:&acc];
  return acc;
}

Затем категория для класса CMMotionManager.

- (void) simx_setAccelerationData:(CMAcceleration )accelerationData
{
    NSValue *value = [NSValue value:&accelerationData withObjCType:@encode(CMAcceleration)];
    objc_setAssociatedObject(self, HANDLER_IDENTIFIER, value, OBJC_ASSOCIATION_RETAIN);
}

- (CMAccelerometerData *)override_accelerometerData
{
    NSValue *value = objc_getAssociatedObject(self, HANDLER_IDENTIFIER);
    CMAcceleration acc;
    [value getValue:&acc];

    CMAccelerometerData *data = [[CMAccelerometerData alloc] init];

    //Add
    [data simx_setAcceleration:acc];

    return data;
}

Swizzling методов делается следующим образом

 [CESwizzleUtils swizzleClass:[CMMotionManager class]
                              method:@"INSERT METHOD NAME TO BE SWIZZLED HERE"];

Это позволяет мне передавать мои собственные данные

 //Add
    CMAccelerometerData *data = [[CMAccelerometerData alloc] init];
    [data simx_setAcceleration:acc];
    [motionManager simx_setAccelerationData:acc];

Итак, я могу получить данные, подобные этому

motionManager.accelerometerData.acceleration.x;

Я также использовал метод класса DeviceMotion. Вот краткий пример приложения, которое у меня есть, которое извлекает данные из акселерометра и гироскопа, используя методы метода swizzle.

введите здесь описание изображения

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

Код выглядит так

-(IBAction)testing:(id)sender
{
  //random double just generates a random double between 0 and 1
    CMAcceleration acc;
    acc.x = [self randomDouble:0 :1];
    acc.y = [self randomDouble:0 :1];
    acc.z = [self randomDouble:0 :1];

    //Add
    CMAccelerometerData *data = [[CMAccelerometerData alloc] init];
    [data simx_setAcceleration:acc];
    [motionManager simx_setAccelerationData:acc];

    //Sim gravity and userAcel
    CMAcceleration grav;
    grav.x = [self randomDouble:0 :1];
    grav.y = [self randomDouble:0 :1];
    grav.z = [self randomDouble:0 :1];

    CMAcceleration userAcel;
    userAcel.x = [self randomDouble:0 :1];
    userAcel.y = [self randomDouble:0 :1];
    userAcel.z = [self randomDouble:0 :1];

    CMDeviceMotion *deviceMotion = [[CMDeviceMotion alloc] init];
    [deviceMotion simx_setUserAcceleration:userAcel];
    [deviceMotion simx_setGravity:grav];
    [motionManager simx_setDeviceMotion:deviceMotion];

    accelLabael.text = [NSString stringWithFormat:@"Accelerometer: %.02f %.02f %.02f", motionManager.accelerometerData.acceleration.x,motionManager.accelerometerData.acceleration.y,motionManager.accelerometerData.acceleration.z];

    gravityLabel.text = [NSString stringWithFormat:@"Gravity: %.02f %.02f %.02f", motionManager.deviceMotion.gravity.x,motionManager.deviceMotion.gravity.y,motionManager.deviceMotion.gravity.z];


    accelSpeedLabel.text = [NSString stringWithFormat:@"Accel: %.02f %.02f %.02f", motionManager.deviceMotion.userAcceleration.x,motionManager.deviceMotion.userAcceleration.y,motionManager.deviceMotion.userAcceleration.z];
}

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

 [activityManager startActivityUpdatesToQueue:[[NSOperationQueue alloc] init] withHandler:   ^(CMMotionActivity *activity){

}];


 [motionManager startDeviceMotionUpdatesToQueue:[[NSOperationQueue alloc] init]
                                           withHandler:^ (CMDeviceMotion *motionData, NSError *error) {

}];

я тоже пробовала эти способы

- (BOOL)override_isAccelerometerActive;
- (BOOL)override_isDeviceMotionActive;
-(BOOL)override_isGyroAvailable;

Таким образом, они всегда возвращают true, но все еще не могут запустить эти блоки. Мне нужна помощь, чтобы понять, как правильно использовать эти методы, чтобы я мог начать отправлять и получать фиктивные данные в симулятор.

Редактировать: я изменил метод обновления акселерометра, добавив следующую категорию. в класс CMMotionManager

-(void)override_startAccelerometerUpdatesToQueue:(NSOperationQueue *)queue
                                     withHandler:(CMAccelerometerHandler)handler{

    dispatch_async(dispatch_get_main_queue(), ^{

        NSLog(@"Test %.02f %.02f %.02f", self.accelerometerData.acceleration.x,self.accelerometerData.acceleration.y,self.accelerometerData.acceleration.z);


    });
}

Это работает достаточно хорошо, так как я просмотрел данные ускорения.

Затем я попробовал это с классом CMMotionActivityManager, добавив эту категорию

-(void)override_startActivityUpdatesToQueue:(NSOperationQueue *)queue
                                withHandler:(CMMotionActivity *)activity
{
    dispatch_async(dispatch_get_main_queue(), ^
                   {
                      BOOL walking = [activity walking];
                       NSLog(@"%i", walking);
                   });
}

Однако я получаю эту ошибку здесь

[NSGlobalBlock walk]: нераспознанный селектор отправлен в экземпляр 0x1086cf330.

Мы ценим любые предложения

ОБНОВЛЕНИЕ 2:

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

-(void)override_startActivityUpdatesToQueue:(NSOperationQueue *)queue
                                withHandler:(CMMotionActivityHandler )activity
{

}

Дело в том, что мне нужен доступ к CMMotionActivity, чтобы выяснить, ходят ли они, бегают ли они и т. д. Исходный код

 [activityManager startActivityUpdatesToQueue:[[NSOperationQueue alloc] init] withHandler:   ^(CMMotionActivity *activity){
        dispatch_async(dispatch_get_main_queue(), ^
        {
            [activity walking];
        });
    }];

позволяет получить доступ к этой переменной. Однако теперь, когда я это просмотрел, теперь он вызывает мое новое объявление внутри моего файла категории, который не содержит переменной CMMotionActivity. Любые идеи о том, как я могу получить к этому доступ. Это становится немного сложнее, но это последнее препятствие для меня, прежде чем я наконец смогу начать издеваться над данными CoreMotion. Я уже смоделировал данные гироскопа и компаса, и у меня есть данные из реальных путешествий, так что я могу передать их в симулятор. Но мне нужно, чтобы он сказал мне, идет ли пользователь, бежит, едет и т. д.

Какие-либо предложения?


person AdamM    schedule 11.11.2014    source источник


Ответы (1)


Ваша подпись метода для override_startActivityUpdatesToQueue:withHandler: неверна. Параметр handler должен быть блоком CMMotionActivityHandler, предоставляющим один аргумент CMMotionActivity:

typedef void (^CMMotionActivityHandler)(CMMotionActivity *activity)
person mattt    schedule 13.11.2014
comment
Кроме того, вы можете попробовать выполнить отправку на queue.underlyingQueue вместо dispatch_get_main_queue. - person mattt; 13.11.2014
comment
Я объявил метод в файле интерфейса, но правильно ли я реализую его в файле реализации? - person AdamM; 14.11.2014
comment
Смотрите мой обновленный ответ, чтобы увидеть, что я пробовал. Также мне нужно сделать что-то подобное с ActivityManager override_startActivityUpdatesToQueue, а не с моим текущим методом - person AdamM; 14.11.2014
comment
Нет, нет, нет... почему ты объявляешь CMMotionActivityHandler? Обработчик — это второй параметр. Вы только что заменили очередь операций, которая была единственно правильной частью этого. Просто скопируйте исходное объявление из заголовочного файла CoreMotion, добавьте к нему override_ и закройте его. - person mattt; 15.11.2014
comment
Я обновил свой ответ, глупая ошибка с моей стороны. Однако у меня есть последняя проблема, с которой я надеялся, что вы мне поможете, см. мой обновленный ответ. - person AdamM; 17.11.2014
comment
Извините, но каждое обновление вашего вопроса, похоже, указывает либо на фундаментальное непонимание основных принципов, либо на отсутствие ясности цели. На данный момент я чувствую, что единственный разумный ответ на этот вопрос — это отрицание его предпосылки: не тратьте свое время, пытаясь смоделировать условия движения. Используйте фиктивные объекты или внедрение зависимостей, чтобы вернуть YES к любым проверкам, которые вы хотите, но остановитесь на этом. - person mattt; 17.11.2014
comment
Я знаю, что то, о чем я прошу, сложно и запутанно, но без этого тестирование приложения станет кошмаром. Это означает, что мне нужно установить приложение на устройство, чтобы протестировать каждый сценарий, что доставляет много хлопот. Мне уже удалось внедрить свой собственный акселерометр, данные гироскопа и т. д. Я надеялся, что затем ActivityManager распознает эти данные, чтобы затем я мог определить, какое движение моделируется. Это не идеально, но это лучше, чем постоянно выходить на улицу с приложением и бегать, ходить, ездить на велосипеде, водить машину каждый раз, когда я хочу протестировать его. - person AdamM; 17.11.2014
comment
Я говорю, что это сложно, потому что вы делаете это так. Здесь нет причин, по которым вам нужно обманывать или подделывать какие-либо данные; просто напишите обработчик, который игнорирует реальную активность и использует то, что вы действительно хотите протестировать. - person mattt; 17.11.2014
comment
Я думаю, что вы, вероятно, правы, я слишком много думал об этом. Я просто сделаю, как вы предложили, и сделаю это намного проще, и напишу свой обработчик, чтобы я мог протестировать любую деятельность, которую захочу. Я отмечу ваш ответ как принятый. Спасибо за ваше время и помощь! - person AdamM; 17.11.2014