3
\$\begingroup\$

I have 2 asynchronous return values from 2 different classes, one from HealthKit, the other from MotionManager. I combine the outcome of these classes through a combinedViewModel. The code works, but I want to know if it is properly formatted and if all functions, ... are used in the right place. With other words, can I build an official app with this code or do I need to structure it differently.

MotionManager.swift

struct MotionValues { var rotationX: Double = 0.0 var rotationY: Double = 0.0 var rotationZ: Double = 0.0 var pitch: Double = 0.0 var roll: Double = 0.0 var yaw: Double = 0.0 } class MotionManager { @Published var motionValues = MotionValues() private let manager = CMMotionManager() func startMotionUpdates() { manager.deviceMotionUpdateInterval = 1.0 manager.startDeviceMotionUpdates(to: .main) { (data, error) in guard let data = data, error == nil else { print(error!) return } self.motionValues.rotationX = data.rotationRate.x self.motionValues.rotationY = data.rotationRate.y self.motionValues.rotationZ = data.rotationRate.z self.motionValues.pitch = data.attitude.pitch self.motionValues.roll = data.attitude.roll self.motionValues.yaw = data.attitude.yaw } } func stopMotionUpdates() { manager.stopDeviceMotionUpdates() resetAllMotionData() } func resetAllMotionData() { self.motionValues.rotationX = 0.0 self.motionValues.rotationY = 0.0 self.motionValues.rotationZ = 0.0 self.motionValues.pitch = 0.0 self.motionValues.roll = 0.0 self.motionValues.yaw = 0.0 } } 

HealthKitManager.swift

class HealthKitManager { private var healthStore = HKHealthStore() private var heartRateQuantity = HKUnit(from: "count/min") private var activeQueries = [HKQuery]() @Published var heartRateValue: Double = 0.0 func autorizeHealthKit() { let heartRate = HKObjectType.quantityType(forIdentifier: .heartRate)! let heartRateVariability = HKObjectType.quantityType(forIdentifier: .heartRateVariabilitySDNN)! let HKreadTypes: Set = [heartRate, heartRateVariability] healthStore.requestAuthorization(toShare: nil, read: HKreadTypes) { (success, error) in if let error = error { print("Error requesting health kit authorization: \(error)") } } } func fetchHeartRateData(quantityTypeIdentifier: HKQuantityTypeIdentifier ) { let devicePredicate = HKQuery.predicateForObjects(from: [HKDevice.local()]) let updateHandler: (HKAnchoredObjectQuery, [HKSample]?, [HKDeletedObject]?, HKQueryAnchor?, Error?) -> Void = { query, samples, deletedObjects, queryAnchor, error in guard let samples = samples as? [HKQuantitySample] else { return } self.process(samples, type: quantityTypeIdentifier) } let query = HKAnchoredObjectQuery(type: HKObjectType.quantityType(forIdentifier: quantityTypeIdentifier)!, predicate: devicePredicate, anchor: nil, limit: HKObjectQueryNoLimit, resultsHandler: updateHandler) query.updateHandler = updateHandler healthStore.execute(query) activeQueries.append(query) } private func process(_ samples: [HKQuantitySample], type: HKQuantityTypeIdentifier) { for sample in samples { if type == .heartRate { DispatchQueue.main.async { self.heartRateValue = sample.quantity.doubleValue(for: self.heartRateQuantity) } } } } func stopFetchingHeartRateData() { activeQueries.forEach { healthStore.stop($0) } activeQueries.removeAll() DispatchQueue.main.async { self.heartRateValue = 0.0 } } } 

CombinedViewModel.swift

class CombinedViewModel: ObservableObject { @Published var motionValues: MotionValues = MotionValues() @Published var heartRateValue: Double = 0.0 var motion = MotionManager() var health = HealthKitManager() var cancellables = Set<AnyCancellable>() init() { motion.$motionValues .combineLatest(health.$heartRateValue) .sink(receiveValue: { [weak self] combined in self?.motionValues = combined.0 self?.heartRateValue = combined.1 }) .store(in: &cancellables) } } 

ContentView.swift

struct ContentView: View { @State var isActive: Bool = false @ObservedObject var combined = CombinedViewModel() var body: some View { ScrollView { VStack(alignment: .leading) { Indicator(title: "X:", value: combined.motionValues.rotationX) Indicator(title: "Y:", value: combined.motionValues.rotationY) Indicator(title: "Z:", value: combined.motionValues.rotationZ) Divider() Indicator(title: "Pitch:", value: combined.motionValues.pitch) Indicator(title: "Roll:", value: combined.motionValues.roll) Indicator(title: "Yaw:", value: combined.motionValues.yaw) Divider() Indicator(title: "HR:", value: combined.heartRateValue) } .padding(.horizontal, 10) Button(action: { self.isActive.toggle() self.isActive ? self.start() : self.stop() }) { Text(isActive ? "Stop" : "Start") } .background(isActive ? Color.green : Color.blue) .cornerRadius(10) .padding(.horizontal, 5) }.onAppear { self.combined.health.autorizeHealthKit() } } private func start() { self.combined.motion.startMotionUpdates() self.combined.health.fetchHeartRateData(quantityTypeIdentifier: .heartRate) } private func stop() { self.combined.motion.stopMotionUpdates() self.combined.health.stopFetchingHeartRateData() } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } } struct Indicator: View { var title: String var value: Double var body: some View { HStack { Text(title) .font(.footnote) .foregroundColor(.blue) Text("\(value)") .font(.footnote) } } } 
\$\endgroup\$
0

    0

    Start asking to get answers

    Find the answer to your question by asking.

    Ask question

    Explore related questions

    See similar questions with these tags.