Предотвратить запуск закрытия до тех пор, пока не завершится другое

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

Происходит состояние гонки, когда функция getVariable запускается и возвращается до того, как функция callFunction реализовала изменение. В результате значение, отображаемое в getLightLabel.text, соответствует предыдущему условию, а не текущему условию.

Мой вопрос заключается в том, как переписать приведенный ниже код, чтобы myPhoton!.getVariable не выполнялся до тех пор, пока myPhoton!.callFunction не вернется (выполнит свою задачу).

Я попытался поместить getVariable внутри callFunction как до, так и после закрытия } if (error == nil), но результат был идентичен приведенному здесь коду.

@IBAction func lightOn(sender: AnyObject) {
    let funcArgs = [1]
    myPhoton!.callFunction("lightLed0", withArguments: funcArgs) { (resultCode : NSNumber!, error : NSError!) -> Void in
        if (error == nil) {
            self.lightStateLabel.text = "LED is on"
        }
    }
    myPhoton!.getVariable("Light", completion: { (result:AnyObject!, error:NSError!) -> Void in
        if let e = error {
            self.getLightLabel.text = "Failed reading light"
        }
        else {
            if let res = result as? Float {
                self.getLightLabel.text = "Light level is \(res) lumens"
            }
        }
    })



}


@IBAction func lightOff(sender: AnyObject) {
    let funcArgs = [0]
    myPhoton!.callFunction("lightLed0", withArguments: funcArgs) { (resultCode : NSNumber!, error : NSError!) -> Void in
        if (error == nil) {
            self.lightStateLabel.text = "LED is off"
        }
    }
    myPhoton!.getVariable("Light", completion: { (result:AnyObject!, error:NSError!) -> Void in
        if let e = error {
            self.getLightLabel.text = "Failed reading light"
        }
        else {
            if let res = result as? Float {
                self.getLightLabel.text = "Light level is \(res) lumens"
            }
        }
    })

}

Вот комментарии и код callFunction из файла .h. Этот SDK написан на Objective C. Я использую его в Swift с файлом заголовка моста.

/**
 *  Call a function on the device
 *
 *  @param functionName Function name
 *  @param args         Array of arguments to pass to the function on the device. Arguments will be converted to string maximum length 63 chars.
 *  @param completion   Completion block will be called when function was invoked on device. First argument of block is the integer return value of the function, second is NSError object in case of an error invoking the function
 */
-(void)callFunction:(NSString *)functionName withArguments:(NSArray *)args completion:(void (^)(NSNumber *, NSError *))completion;

/*
-(void)addEventHandler:(NSString *)eventName handler:(void(^)(void))handler;
-(void)removeEventHandler:(NSString *)eventName;
 */

Вот код файла .m

-(void)callFunction:(NSString *)functionName withArguments:(NSArray *)args completion:(void (^)(NSNumber *, NSError *))completion
{
    // TODO: check function name exists in list
    NSURL *url = [self.baseURL URLByAppendingPathComponent:[NSString stringWithFormat:@"v1/devices/%@/%@", self.id, functionName]];
    NSMutableDictionary *params = [NSMutableDictionary new]; //[self defaultParams];
    // TODO: check response of calling a non existant function

    if (args) {
        NSMutableArray *argsStr = [[NSMutableArray alloc] initWithCapacity:args.count];
        for (id arg in args)
        {
            [argsStr addObject:[arg description]];
        }
        NSString *argsValue = [argsStr componentsJoinedByString:@","];
        if (argsValue.length > MAX_SPARK_FUNCTION_ARG_LENGTH)
        {
            // TODO: arrange user error/codes in a list
            NSError *err = [self makeErrorWithDescription:[NSString stringWithFormat:@"Maximum argument length cannot exceed %d",MAX_SPARK_FUNCTION_ARG_LENGTH] code:1000];
            if (completion)
                completion(nil,err);
            return;
        }

        params[@"args"] = argsValue;
    }

    [self setAuthHeaderWithAccessToken];

    [self.manager POST:[url description] parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
        if (completion)
        {
            NSDictionary *responseDict = responseObject;
            if ([responseDict[@"connected"] boolValue]==NO)
            {
                NSError *err = [self makeErrorWithDescription:@"Device is not connected" code:1001];
                completion(nil,err);
            }
            else
            {
                // check
                NSNumber *result = responseDict[@"return_value"];
                completion(result,nil);
            }
        }
    } failure:^(AFHTTPRequestOperation *operation, NSError *error)
    {
        if (completion)
            completion(nil,error);
    }];

}

person Bendrix    schedule 18.06.2015    source источник
comment
Вы уверены, что ваша проблема не в самом коде? В обоих callFunctions вы, кажется, комбинируете код для определения функции и ее вызова - вы вызываете myPhoton!.callFunction(...) но у вас также есть код в скобках {...} после того, как вы прошли все ваши доводы.   -  person dunnmifflsys    schedule 18.06.2015
comment
@dunnmifflsys это то, что вы можете сделать с замыканием в качестве последнего параметра в вызове функции. foo(block: {замыкание}) можно записать как foo {замыкание}. Чего мне не хватает в исходном посте, так это определения callFunction и getVariable.   -  person Laurent    schedule 18.06.2015
comment
Можем ли мы увидеть ваше определение callFunction(_:withArguments:)?   -  person fqdn    schedule 18.06.2015
comment
См. соответствующие изменения. Я добавил код файла .h и .m из Objective C SDK. Я использую файл заголовка моста в Swift для доступа к функции. Если возможно ограничить решение Swift, это будет предпочтительнее.   -  person Bendrix    schedule 18.06.2015


Ответы (1)


Одно из решений состоит в том, чтобы поместить второе замыкание внутрь первого, где первое возвращает и предоставляет значение Error. Если ошибки нет, выполните второе закрытие. Это один из способов тесно связать два замыкания, не прибегая к семафорам или другим схемам обмена сообщениями.

В этом приложении проблема, с которой я столкнулся, не может быть решена на стороне стека IOS/Swift. Облачный API и встроенный uP не связаны тесно, поэтому облако возвращается к IOS с завершением до того, как на Particle uP будет запущен полный функциональный код.

Решение этой общей проблемы на самом деле заключается либо в изменении облачного API, либо в добавлении некоторого дополнительного кода к прошивке UP, чтобы тесно связать процесс с приложением IOS с дополнительной связью.

person Bendrix    schedule 13.07.2015