Swift/iOS — Как использовать значение из одной области/функции и передавать его в другую?

#ios #swift

Вопрос:

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

Вот код:

 var gyroX = Float()
        
motion.startGyroUpdates(to: .main) { (data, error) in
    if let myData = data {
        gyroX = Float(myData.rotationRate.x)
    }
}
 

Комментарии:

1. Закрытие обновления вызывается асинхронно при обновлении значения (что будет происходить очень часто). Вам было бы лучше вызвать функцию, которой требуется значение из замыкания, а не пытаться использовать значение вне замыкания

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

Ответ №1:

С бета-версией Xcode 13 и Swift 5.5

Это проблема, которую мы теперь можем решить с помощью продолжения Async/Await

Сначала мы бы создали функцию, которая преобразует обратный вызов в ожидаемый результат, например:

 func getXRotation(from motion: CMMotionManager) async throws -> Float {
    try await withCheckedThrowingContinuation { continuation in
        class GyroUpdateFailure: Error {} // make error to throw
        motion.startGyroUpdates(to: .main) { (data, error) in
            if let myData = data {
                continuation.resume(returning:  Float(myData.rotationRate.x))
            } else {
                throw GyroUpdateFailure()
            }
        }
    }
}
 

Затем мы можем назначить переменную и использовать ее следующим образом:

 let gyroX = try await getXRotation(from: motion)
callSomeOtherFunction(with: gyroX)
 

С помощью Xcode

В текущей версии Swift и Xcode мы можем использовать платформу Combine, чтобы немного упростить обработку обратного вызова. Сначала мы преобразуем закрытие из менеджера движения в «Будущее». Тогда мы сможем использовать это будущее в цепочке объединения.

 
func getXRotation(from motion: CMMotionManager) -> Future<CMGyroData, Error> {
    Future { promise in
        class GyroUpdateFailure: Error {} // make error to throw
        motion.startGyroUpdates(to: .main) { (data, error) in
            if let myData = data {
                promise(.success(myData))
            } else {
                promise(.failure(GyroUpdateFailure()))
            }
        }
    }
}

// This is the other function you want to call
func someOtherFunction(_ x: Float) {}

// Then we can use it like so
_ = getXRotation(from: motion)
    .eraseToAnyPublisher()
    .map { Float($0.rotationRate.x) }
    .map(someOtherFunction)
    .sink { completion in
        switch completion {
        case .failure(let error):
            print(error.localizedDescription)
        default: break
        }
    } receiveValue: {
        print($0)
    }


 

В потоке комбайна есть несколько важных частей. Это _ = один из них. Результатом «погружения» издателя является «отменяемый» объект. Если мы не сохраним это в локальной переменной, система сможет очистить задачу до ее выполнения. Так что вы наверняка захотите это сделать.

Я настоятельно рекомендую вам оформить заказ SwiftBySundell.com чтобы узнать больше о комбинировании или асинхронности/ожидании и RayWenderlich.com для мобильной разработки в целом.

Комментарии:

1. Ух ты! Большое вам спасибо! Я посмотрю, смогу ли я заставить его работать. Очень признателен!

2. С удовольствием! Дай мне знать, если повесишь трубку.

3. Кстати, с решением xcode 12 вам нужно будет добавить import Combine в начало вашего файла.

4. Хорошо, я вставил туда комбинацию / XCode 12 и заставил ее работать… как бы я использовал его вне этих функций? Например, я попробовал: «печать(getXRotation(от: движение))», и это дало мне «Объединить. Будущее Ошибка>» вывод.

5. Вы получаете ответ особого рода, который фактически не выполняется до sink тех пор, пока не будет вызван. Я бы сделал это в цепочке издателей, как будто .map { print($0); return $0 } в combine может быть .print() функция… Хотя ИДК.