From cc3b1777569104d3f89b144064730a4c3c77375e Mon Sep 17 00:00:00 2001 From: Peter Nash <480796+pablonosh@users.noreply.github.com> Date: Tue, 6 Jun 2023 20:24:57 +0100 Subject: [PATCH 1/2] after tidy up --- .../contents.xcworkspacedata | 4 - .../AccentColor.colorset/Contents.json | 4 + FitCount/ContentView.swift | 22 +- FitCount/FitCountApp.swift | 4 +- FitCount/History/HistoryView.swift | 2 +- .../Preview Assets.xcassets/Contents.json | 6 - FitCount/Workout/BoundingBoxView.swift | 42 -- FitCount/Workout/ExerciseDetailsView.swift | 6 +- FitCount/Workout/InstructionsView.swift | 6 - FitCount/Workout/QuickPoseBasicView.swift | 358 +++++++++--------- FitCount/Workout/VolumeChangeView.swift | 61 ++- FitCount/Workout/Workout.swift | 14 - FitCount/Workout/WorkoutResultsView.swift | 9 +- FitCountTests/FitCountTests.swift | 36 -- FitCountUITests/FitCountUITests.swift | 41 -- .../FitCountUITestsLaunchTests.swift | 32 -- .../project.pbxproj | 272 +------------ .../xcshareddata/swiftpm/Package.resolved | 2 +- .../xcschemes/FitCounter.xcscheme | 5 +- 19 files changed, 244 insertions(+), 682 deletions(-) delete mode 100644 FitCount.xcodeproj/project.xcworkspace/contents.xcworkspacedata delete mode 100644 FitCount/Preview Content/Preview Assets.xcassets/Contents.json delete mode 100644 FitCount/Workout/BoundingBoxView.swift delete mode 100644 FitCount/Workout/Workout.swift delete mode 100644 FitCountTests/FitCountTests.swift delete mode 100644 FitCountUITests/FitCountUITests.swift delete mode 100644 FitCountUITests/FitCountUITestsLaunchTests.swift diff --git a/FitCount.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/FitCount.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 94b2795..0000000 --- a/FitCount.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,4 +0,0 @@ - - - diff --git a/FitCount/Assets.xcassets/AccentColor.colorset/Contents.json b/FitCount/Assets.xcassets/AccentColor.colorset/Contents.json index eb87897..4bf40ac 100644 --- a/FitCount/Assets.xcassets/AccentColor.colorset/Contents.json +++ b/FitCount/Assets.xcassets/AccentColor.colorset/Contents.json @@ -1,6 +1,10 @@ { "colors" : [ { + "color" : { + "platform" : "universal", + "reference" : "systemIndigoColor" + }, "idiom" : "universal" } ], diff --git a/FitCount/ContentView.swift b/FitCount/ContentView.swift index 755778a..451f5d6 100644 --- a/FitCount/ContentView.swift +++ b/FitCount/ContentView.swift @@ -12,15 +12,13 @@ class SessionConfig: ObservableObject { @Published var nReps : Int = 1 @Published var nMinutes : Int = 0 @Published var nSeconds : Int = 1 - @Published var useReps: Bool = true @Published var exercise: Exercise = exercises[0] // use first exercise by default but change in the ExerciseDetailsView } struct ContentView: View { @StateObject var viewModel = ViewModel() - @StateObject var sessionConfig: SessionConfig = SessionConfig() - + @StateObject var sessionConfig = SessionConfig() var body: some View { NavigationStack(path: $viewModel.path) { @@ -34,9 +32,11 @@ struct ContentView: View { .font(.headline) } .padding() - .cornerRadius(8) // Add corner radius for a rounded look + .cornerRadius(8) }.navigationDestination(for: Exercise.self) { exercise in - ExerciseDetailsView(exercise: exercise).environmentObject(viewModel).environmentObject(sessionConfig) + ExerciseDetailsView(exercise: exercise) + .environmentObject(viewModel) + .environmentObject(sessionConfig) } } .background(.white) @@ -60,13 +60,9 @@ struct ContentView: View { class ViewModel: ObservableObject { - @Published var path: NavigationPath = NavigationPath() -} - -struct ContentView_Previews: PreviewProvider { - static var previews: some View { - ContentView() + @Published var path = NavigationPath() + + func popToRoot(){ + path.removeLast(path.count) // pop to root } } - - diff --git a/FitCount/FitCountApp.swift b/FitCount/FitCountApp.swift index a6df980..4abf301 100644 --- a/FitCount/FitCountApp.swift +++ b/FitCount/FitCountApp.swift @@ -19,9 +19,9 @@ struct Exercise: Identifiable, Hashable { let exercises = [ Exercise( - name: "Biceps Curls", + name: "Bicep Curls", details: "Lift weights in both hands by bending your elbow and lifting them towards your shoulder.", - features: [.fitness(.bicepsCurls), .overlay(.upperBody)] + features: [.fitness(.bicepCurls), .overlay(.upperBody)] ), Exercise( name: "Squats", diff --git a/FitCount/History/HistoryView.swift b/FitCount/History/HistoryView.swift index b3ad65b..a1455ae 100644 --- a/FitCount/History/HistoryView.swift +++ b/FitCount/History/HistoryView.swift @@ -25,7 +25,7 @@ struct HistoryView: View { Text(sessionData.exercise) .font(.title) .fontWeight(.bold) - .foregroundColor(.indigo) + .foregroundColor(Color("AccentColor")) Text(sessionData.date.formatted( .dateTime diff --git a/FitCount/Preview Content/Preview Assets.xcassets/Contents.json b/FitCount/Preview Content/Preview Assets.xcassets/Contents.json deleted file mode 100644 index 73c0059..0000000 --- a/FitCount/Preview Content/Preview Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/FitCount/Workout/BoundingBoxView.swift b/FitCount/Workout/BoundingBoxView.swift deleted file mode 100644 index ba7531f..0000000 --- a/FitCount/Workout/BoundingBoxView.swift +++ /dev/null @@ -1,42 +0,0 @@ -// -// BoundingBoxView.swift -// FitCount -// -// Created by QuickPose.ai on 25.05.2023. -// - -import SwiftUI - -struct BoundingBoxView: View { - var isInBBox: Bool - var indicatorWidth: Double - - init(isInBBox:Bool, indicatorWidth: Double) { - self.isInBBox = isInBBox - self.indicatorWidth = indicatorWidth - } - - var body: some View { - GeometryReader { geometry in - VStack { - RoundedRectangle(cornerRadius: 15) - .stroke(isInBBox ? Color.green : Color.red, lineWidth: 5) - .frame(width: geometry.size.width * 0.6, height: geometry.size.height * 0.8) - .position(x: geometry.size.width / 2, y: geometry.size.height / 2) - - } - }.overlay( - GeometryReader { geometry in - VStack { - if (indicatorWidth > 0) { - RoundedRectangle(cornerRadius: 15) - .fill(.green.opacity(0.5)) - .frame(width: geometry.size.width * 0.6 * indicatorWidth, height: geometry.size.height * 0.8) - .position(x: geometry.size.width / 2, y: geometry.size.height / 2) - } - } - } - ) - - } -} diff --git a/FitCount/Workout/ExerciseDetailsView.swift b/FitCount/Workout/ExerciseDetailsView.swift index 78ab09c..892593d 100644 --- a/FitCount/Workout/ExerciseDetailsView.swift +++ b/FitCount/Workout/ExerciseDetailsView.swift @@ -8,7 +8,6 @@ import SwiftUI import PagerTabStripView - struct TitleNavBarItem: View { let title: String @@ -36,7 +35,6 @@ struct ExerciseDetailsView: View { .font(.body) .padding() - Spacer() PagerTabStripView( @@ -117,8 +115,8 @@ struct ExerciseDetailsView: View { Text("Start workout") .foregroundColor(.white) .padding() - .background(.indigo) // Set background color to the main color - .cornerRadius(8) // Add corner radius for a rounded look + .background(Color("AccentColor")) + .cornerRadius(8) } .navigationDestination(for: String.self) { _ in diff --git a/FitCount/Workout/InstructionsView.swift b/FitCount/Workout/InstructionsView.swift index 3a6fa75..9462a17 100644 --- a/FitCount/Workout/InstructionsView.swift +++ b/FitCount/Workout/InstructionsView.swift @@ -31,9 +31,3 @@ struct InstructionsView: View { } } - -struct InstructionsView_Previews: PreviewProvider { - static var previews: some View { - InstructionsView() - } -} diff --git a/FitCount/Workout/QuickPoseBasicView.swift b/FitCount/Workout/QuickPoseBasicView.swift index 0b1889b..5c5034a 100644 --- a/FitCount/Workout/QuickPoseBasicView.swift +++ b/FitCount/Workout/QuickPoseBasicView.swift @@ -8,21 +8,34 @@ import SwiftUI import QuickPoseCore import QuickPoseSwiftUI +import AVFoundation -struct VoiceCommands { - public static let standInsideBBox = "Stand so that your whole body is inside the bounding box" +struct SessionData: Equatable { + let count: Int + let seconds: Int } -class SessionData: ObservableObject { - @Published var count = 0 - @Published var seconds = 0 -} - -enum WorkoutState { - case volume +enum ViewState: Equatable { + case startVolume case instructions - case bbox - case exercise + case introBoundingBox + case boundingBox(enterTime: Date) + case introExercise(Exercise) + case exercise(SessionData, enterTime: Date) + case results(SessionData) + + var speechPrompt: String? { + switch self { + case .introBoundingBox: + return "Stand so that your whole body is inside the bounding box" + case .introExercise(let exercise): + return "Now let's start the \(exercise.name) exercise" + case .exercise(let results, _): + return "\(results.count)" + default: + return nil + } + } } struct QuickPoseBasicView: View { @@ -31,48 +44,25 @@ struct QuickPoseBasicView: View { @EnvironmentObject var sessionConfig: SessionConfig @State private var overlayImage: UIImage? - @State private var feedbackText: String? = nil - @State var counter = QuickPoseThresholdCounter() - @State var measure: Double = 0 - @State var count: Int = 0 - @State var seconds: Int = 0 - let exerciseTimer = TimerManager() - - @State var isInBBox = false - @State var state = WorkoutState.volume - - @State private var indicatorWidth: CGFloat = 0.0 - - @StateObject var sessionData = SessionData() - - @State var countScale = 1.0 - - @State private var isActive: Bool = false + @State private var counter = QuickPoseThresholdCounter() + @State private var state: ViewState = .startVolume - let bboxTimer = TimerManager() - - func goToResults() { - DispatchQueue.main.async { - sessionData.seconds = Int(exerciseTimer.getTotalSeconds()) - sessionData.count = Int(counter.getCount()) - - isActive = true // Set the state variable to trigger the navigation - } + @State private var countScale = 1.0 + @State private var boundingBoxMaskWidth = 0.0 + + func canMoveFromBoundingBox(landmarks: QuickPose.Landmarks) -> Bool { + + let xsInBox = landmarks.poseLandmarks.allSatisfy { 0.5 - (0.6/2) < $0[0] && $0[0] < 0.5 + (0.6/2) } + let ysInBox = landmarks.poseLandmarks.allSatisfy { 0.5 - (0.8/2) < $0[1] && $0[1] < 0.5 + (0.8/2) } + + return xsInBox && ysInBox } var body: some View { - VStack{ - NavigationLink(value: "Workout results") { - EmptyView() - }.navigationDestination(isPresented: $isActive) { - WorkoutResultsView() - .environmentObject(sessionData) - .environmentObject(viewModel) - } - - GeometryReader { geometry in + GeometryReader { geometry in + VStack { ZStack(alignment: .top) { QuickPoseCameraView(useFrontCamera: true, delegate: quickPose) QuickPoseOverlayView(overlayImage: $overlayImage) @@ -80,68 +70,97 @@ struct QuickPoseBasicView: View { .frame(width: geometry.safeAreaInsets.leading + geometry.size.width + geometry.safeAreaInsets.trailing) .edgesIgnoringSafeArea(.all) .overlay() { - if (state == WorkoutState.volume) { - VolumeChangeView().overlay(alignment: .bottom) { - Button (action: { - state = WorkoutState.instructions - - }) { - Text("Continue").foregroundColor(.white) - .padding() - .background(.indigo) // Set background color to the main color - .cornerRadius(8) // Add corner radius for a rounded look + switch state { + case .startVolume: + VolumeChangeView() + .overlay(alignment: .bottom) { + Button (action: { + state = .instructions + }) { + Text("Continue").foregroundColor(.white) + .padding() + .background(Color("AccentColor")) + .cornerRadius(8) + } } - } - } - - if (state == WorkoutState.instructions) { - InstructionsView().overlay(alignment: .bottom) { - Button (action: { - state = WorkoutState.bbox - Text2Speech(text: VoiceCommands.standInsideBBox).say() - }) { - Text("Start Workout").foregroundColor(.white) - .padding() - .background(.indigo) // Set background color to the main color - .cornerRadius(8) // Add corner radius for a rounded look + case .instructions: + InstructionsView() + .overlay(alignment: .bottom) { + Button (action: { + state = .introBoundingBox + }) { + Text("Start Workout").foregroundColor(.white) + .padding() + .background(Color("AccentColor")) + .cornerRadius(8) } + } + case .introBoundingBox: + ZStack { + RoundedRectangle(cornerRadius: 15) + .stroke(.red, lineWidth: 5) } - } - - if (state == WorkoutState.bbox) { - BoundingBoxView(isInBBox: isInBBox, indicatorWidth: indicatorWidth) + .frame(width: geometry.size.width * 0.6, height: geometry.size.height * 0.8) + .padding(.horizontal, (geometry.size.width * 1 - 0.6)/2) + + case .boundingBox: + ZStack { + RoundedRectangle(cornerRadius: 15) + .stroke(.green, lineWidth: 5) + + RoundedRectangle(cornerRadius: 15) + .fill(.green.opacity(0.5)) + .mask(alignment: .leading) { + Rectangle() + .frame(width: geometry.size.width * 0.6 * boundingBoxMaskWidth) + } + } + .frame(width: geometry.size.width * 0.6, height: geometry.size.height * 0.8) + .padding(.horizontal, (geometry.size.width * 1 - 0.6)/2) + + case .results(let results): + WorkoutResultsView(sessionData: results) + .environmentObject(viewModel) + + default: + EmptyView() } } + .overlay(alignment: .topTrailing) { Button(action: { - goToResults() + if case .results = state { + viewModel.popToRoot() + } else { + state = .results(SessionData(count: counter.state.count, seconds: 0)) + } }) { Image(systemName: "xmark.circle.fill") .font(.system(size: 44)) - .foregroundColor(.indigo) + .foregroundColor(Color("AccentColor")) } .padding() } - + .overlay(alignment: .bottom) { - if (state == WorkoutState.exercise) { + if case .exercise(let results, let enterTime) = state { HStack { - Text(String(count) + (sessionConfig.useReps ? " \\ " + String(sessionConfig.nReps) : "") + " reps") + Text(String(results.count) + (sessionConfig.useReps ? " \\ " + String(sessionConfig.nReps) : "") + " reps") .font(.system(size: 30, weight: .semibold)) .padding(16) .scaleEffect(countScale) - - Text(String(seconds) + (!sessionConfig.useReps ? " \\ " + String(sessionConfig.nSeconds + sessionConfig.nMinutes * 60) : "") + " sec") + + Text(String(format: "%.0f",-enterTime.timeIntervalSinceNow) + (!sessionConfig.useReps ? " \\ " + String(sessionConfig.nSeconds + sessionConfig.nMinutes * 60) : "") + " sec") .font(.system(size: 30, weight: .semibold)) .padding(16) } .frame(maxWidth: .infinity) .foregroundColor(.white) - .background(.indigo) + .background(Color("AccentColor")) } } .overlay(alignment: .center) { - if (state == WorkoutState.exercise) { + if case .exercise = state { if let feedbackText = feedbackText { Text(feedbackText) .font(.system(size: 26, weight: .semibold)).foregroundColor(.white) @@ -149,108 +168,105 @@ struct QuickPoseBasicView: View { } } } - .onAppear { - quickPose.start(features: sessionConfig.exercise.features, onFrame: { status, image, features, feedback, landmarks in - - - - let width = geometry.size.width * 0.6 - let height = geometry.size.height * 0.8 - let x0 = geometry.size.width / 2 - let y0 = geometry.size.height / 2 - - // all xs in [x0 - (width/2), x0 + (width/2)] - // all ys in [y0 - (height/2), y0 + (height/2)] - - let scaleToView = CGAffineTransform(scaleX: geometry.size.width, y:geometry.size.height) - - if (state == WorkoutState.bbox && landmarks != nil) { - let scaledLandmarks = landmarks!.poseLandmarks.map { - let point = CGPoint(x: $0[0], y: $0[1]).applying(scaleToView) - return [point.x, point.y, $0[2]] - } - - let xsInBox = scaledLandmarks.allSatisfy { x0 - (width/2) < $0[0] && $0[0] < x0 + (width/2) } - let ysInBox = scaledLandmarks.allSatisfy { y0 - (height/2) < $0[1] && $0[1] < y0 + (height/2) } - - isInBBox = xsInBox && ysInBox - } - - if (!isInBBox && bboxTimer.isRunning()) { - bboxTimer.pause() - bboxTimer.reset() - indicatorWidth = 0 - } - - if (isInBBox && !bboxTimer.isRunning()) { - bboxTimer.start() - } - - if (bboxTimer.isRunning()) { - indicatorWidth = bboxTimer.getTotalSeconds() / 2 - } - - if (bboxTimer.isRunning() && bboxTimer.getTotalSeconds() > 2) { - bboxTimer.pause() - bboxTimer.reset() - - state = WorkoutState.exercise - } - - if (state == WorkoutState.exercise && !exerciseTimer.isRunning()) { - Text2Speech(text: "Now let's start the \(sessionConfig.exercise.name) exercise").say() - exerciseTimer.start() - } - - if (state == WorkoutState.exercise) { - seconds = Int(exerciseTimer.getTotalSeconds()) - - if let feedback = feedback[.fitness(.bicepsCurls)] { - feedbackText = feedback.displayString - } else { - feedbackText = nil - } - - if case .fitness = sessionConfig.exercise.features.first, let result = features[sessionConfig.exercise.features.first!]{ - counter.count(probability: result.value) - if (counter.getCount() > count) { - Text2Speech(text: String(counter.getCount())).say() - withAnimation(.easeInOut(duration: 0.1)) { - countScale = 2.0 + + .onChange(of: state) { _ in + + if let speechPrompt = state.speechPrompt { + let utterance = AVSpeechUtterance(string: speechPrompt) + AVSpeechSynthesizer().speak(utterance) + } + + if state == .introBoundingBox { + quickPose.start(features: sessionConfig.exercise.features, onFrame: { status, image, features, feedback, landmarks in + overlayImage = image + if case .success(_,_) = status { + + switch state { + case .introBoundingBox: + + if let landmarks = landmarks, canMoveFromBoundingBox(landmarks: landmarks) { + + state = .boundingBox(enterTime: Date()) + boundingBoxMaskWidth = 0 + withAnimation(.easeInOut(duration: 2)) { + boundingBoxMaskWidth = 1.0 + } } + case .boundingBox(let enterDate): + if let landmarks = landmarks, canMoveFromBoundingBox(landmarks: landmarks) { + if -enterDate.timeIntervalSinceNow > 2 { + state = .introExercise(sessionConfig.exercise) + } + } else { + state = .introBoundingBox + } + + case .introExercise(_): DispatchQueue.main.asyncAfter(deadline: .now()+0.5) { - withAnimation(.easeInOut(duration: 0.2)) { - countScale = 1.0 + state = .exercise(SessionData(count: 0, seconds: 0), enterTime: Date()) + } + case .exercise(_, let enterDate): + let secondsElapsed = Int(-enterDate.timeIntervalSinceNow) + + if let feedback = feedback[.fitness(.bicepCurls)] { + feedbackText = feedback.displayString + } else { + feedbackText = nil + + if case .fitness = sessionConfig.exercise.features.first, let result = features[sessionConfig.exercise.features.first!] { + _ = counter.count(result.value) { newState in + if !newState.isEntered { + DispatchQueue.main.asyncAfter(deadline: .now()+0.1) { + withAnimation(.easeInOut(duration: 0.1)) { + countScale = 2.0 + } + DispatchQueue.main.asyncAfter(deadline: .now()+0.4) { + withAnimation(.easeInOut(duration: 0.2)) { + countScale = 1.0 + } + } + } + } + } } } + + let newResults = SessionData(count: counter.state.count, seconds: secondsElapsed) + state = .exercise(newResults, enterTime: enterDate) // refresh view for every updated second + var hasFinished = false + if sessionConfig.useReps { + hasFinished = counter.state.count >= sessionConfig.nReps + } else { + hasFinished = secondsElapsed >= sessionConfig.nSeconds + sessionConfig.nMinutes * 60 + } + + if hasFinished { + state = .results(newResults) + } + default: + break } - count = counter.getCount() - - if (sessionConfig.useReps && count >= sessionConfig.nReps || !sessionConfig.useReps && Int(exerciseTimer.getTotalSeconds()) >= (sessionConfig.nSeconds + sessionConfig.nMinutes * 60)) { - goToResults() - } - measure = result.value + } else { + state = .introBoundingBox } - } - if case .success(_,_) = status { - overlayImage = image - } else { - overlayImage = nil - } - }) - }.onAppear() { + }) + } + + if case .results(let result) = state { + let sessionDataDump = SessionDataModel(exercise: sessionConfig.exercise.name, count: result.count, seconds: result.seconds, date: Date()) + appendToJson(sessionData: sessionDataDump) + } + } + .onAppear() { UIApplication.shared.isIdleTimerDisabled = true } .onDisappear { - let sessionDataDump = SessionDataModel(exercise: sessionConfig.exercise.name, count: Int(counter.getCount()), seconds: Int(exerciseTimer.getTotalSeconds()), date: Date()) - appendToJson(sessionData: sessionDataDump) - quickPose.stop() UIApplication.shared.isIdleTimerDisabled = false } } - } .navigationBarBackButtonHidden(true) .toolbar(.hidden, for: .tabBar) + } } } diff --git a/FitCount/Workout/VolumeChangeView.swift b/FitCount/Workout/VolumeChangeView.swift index ba37f20..ca6dc9e 100644 --- a/FitCount/Workout/VolumeChangeView.swift +++ b/FitCount/Workout/VolumeChangeView.swift @@ -9,33 +9,6 @@ import SwiftUI import MediaPlayer import AVFoundation - -func setSystemVolume(_ volume: Float) { - let volumeView = MPVolumeView() - let slider = volumeView.subviews.first(where: { $0 is UISlider }) as? UISlider - - DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.01) { - slider?.setValue(volume, animated: false) - } -} - - - -func getCurrentVolume() -> Float { - let audioSession = AVAudioSession.sharedInstance() - - do { - try audioSession.setActive(true) - - let volume = audioSession.outputVolume - return volume - } catch { - print("Failed to get current volume: \(error)") - return 0.0 - } -} - - struct VolumeChangeView: View { @State private var soundLevel: Float = getCurrentVolume() @@ -55,26 +28,46 @@ struct VolumeChangeView: View { HStack { Image(systemName: "speaker.wave.1") .font(.system(size: 30)) - .foregroundColor(.indigo) + .foregroundColor(Color("AccentColor")) Slider(value: $soundLevel, in: 0...1, step: 0.01, onEditingChanged: { data in - setSystemVolume(self.soundLevel) + VolumeChangeView.setSystemVolume(self.soundLevel) }) .tint(soundLevel < 0.5 ? .red : .green) Image(systemName: "speaker.wave.3") .font(.system(size: 30)) - .foregroundColor(.indigo) + .foregroundColor(Color("AccentColor")) } }.frame(width: 300) ) } } -} + + static func setSystemVolume(_ volume: Float) { + let volumeView = MPVolumeView() + let slider = volumeView.subviews.first(where: { $0 is UISlider }) as? UISlider + + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.01) { + slider?.setValue(volume, animated: false) + } + } -struct VolumeChangeView_Previews: PreviewProvider { - static var previews: some View { - VolumeChangeView() + static func getCurrentVolume() -> Float { + let audioSession = AVAudioSession.sharedInstance() + + do { + try audioSession.setActive(true) + + let volume = audioSession.outputVolume + return volume + } catch { + print("Failed to get current volume: \(error)") + return 0.0 + } } } + + + diff --git a/FitCount/Workout/Workout.swift b/FitCount/Workout/Workout.swift deleted file mode 100644 index 52d83e8..0000000 --- a/FitCount/Workout/Workout.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// Workout.swift -// FitCount -// -// Created by QuickPose.ai on 25.05.2023. -// - -import Foundation - - -//class Workout { -// var date: Date -// var -//} diff --git a/FitCount/Workout/WorkoutResultsView.swift b/FitCount/Workout/WorkoutResultsView.swift index c3cfb66..f6e1f2d 100644 --- a/FitCount/Workout/WorkoutResultsView.swift +++ b/FitCount/Workout/WorkoutResultsView.swift @@ -8,13 +8,14 @@ import SwiftUI struct WorkoutResultsView: View { - @EnvironmentObject var sessionData: SessionData + let sessionData: SessionData + @EnvironmentObject var viewModel: ViewModel var body: some View { NavigationView{ VStack(spacing: 20) { - Text("Your workour results") + Text("Your workout results") .font(.largeTitle) .padding(.top, 50) @@ -28,14 +29,14 @@ struct WorkoutResultsView: View { .padding(.bottom, 40) Button(action: { - viewModel.path.removeLast(viewModel.path.count) + viewModel.popToRoot() }) { Text("Finish Workout") .foregroundColor(.white) .font(.title2) .padding() .frame(maxWidth: .infinity) - .background(Color.indigo) + .background(Color("AccentColor")) .cornerRadius(8) } .padding() diff --git a/FitCountTests/FitCountTests.swift b/FitCountTests/FitCountTests.swift deleted file mode 100644 index 0eb3c07..0000000 --- a/FitCountTests/FitCountTests.swift +++ /dev/null @@ -1,36 +0,0 @@ -// -// FitCountTests.swift -// FitCountTests -// -// Created by Денис Волхонский on 22.05.2023. -// - -import XCTest -@testable import FitCount - -final class FitCountTests: XCTestCase { - - override func setUpWithError() throws { - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - - func testExample() throws { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. - // Any test you write for XCTest can be annotated as throws and async. - // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error. - // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards. - } - - func testPerformanceExample() throws { - // This is an example of a performance test case. - self.measure { - // Put the code you want to measure the time of here. - } - } - -} diff --git a/FitCountUITests/FitCountUITests.swift b/FitCountUITests/FitCountUITests.swift deleted file mode 100644 index 4a2a4fa..0000000 --- a/FitCountUITests/FitCountUITests.swift +++ /dev/null @@ -1,41 +0,0 @@ -// -// FitCountUITests.swift -// FitCountUITests -// -// Created by Денис Волхонский on 22.05.2023. -// - -import XCTest - -final class FitCountUITests: XCTestCase { - - override func setUpWithError() throws { - // Put setup code here. This method is called before the invocation of each test method in the class. - - // In UI tests it is usually best to stop immediately when a failure occurs. - continueAfterFailure = false - - // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. - } - - override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - - func testExample() throws { - // UI tests must launch the application that they test. - let app = XCUIApplication() - app.launch() - - // Use XCTAssert and related functions to verify your tests produce the correct results. - } - - func testLaunchPerformance() throws { - if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) { - // This measures how long it takes to launch your application. - measure(metrics: [XCTApplicationLaunchMetric()]) { - XCUIApplication().launch() - } - } - } -} diff --git a/FitCountUITests/FitCountUITestsLaunchTests.swift b/FitCountUITests/FitCountUITestsLaunchTests.swift deleted file mode 100644 index aa12e80..0000000 --- a/FitCountUITests/FitCountUITestsLaunchTests.swift +++ /dev/null @@ -1,32 +0,0 @@ -// -// FitCountUITestsLaunchTests.swift -// FitCountUITests -// -// Created by Денис Волхонский on 22.05.2023. -// - -import XCTest - -final class FitCountUITestsLaunchTests: XCTestCase { - - override class var runsForEachTargetApplicationUIConfiguration: Bool { - true - } - - override func setUpWithError() throws { - continueAfterFailure = false - } - - func testLaunch() throws { - let app = XCUIApplication() - app.launch() - - // Insert steps here to perform after app launch but before taking a screenshot, - // such as logging into a test account or navigating somewhere in the app - - let attachment = XCTAttachment(screenshot: app.screenshot()) - attachment.name = "Launch Screen" - attachment.lifetime = .keepAlways - add(attachment) - } -} diff --git a/FitCounter by QuickPose.ai.xcodeproj/project.pbxproj b/FitCounter by QuickPose.ai.xcodeproj/project.pbxproj index abbdd6e..9555aee 100644 --- a/FitCounter by QuickPose.ai.xcodeproj/project.pbxproj +++ b/FitCounter by QuickPose.ai.xcodeproj/project.pbxproj @@ -8,9 +8,7 @@ /* Begin PBXBuildFile section */ 920A3EEC2A1F6E0E00EC6FC9 /* Timer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 920A3EEB2A1F6E0E00EC6FC9 /* Timer.swift */; }; - 920A3EEF2A1F76F800EC6FC9 /* BoundingBoxView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 920A3EEE2A1F76F800EC6FC9 /* BoundingBoxView.swift */; }; 920A3EF12A1F7C2300EC6FC9 /* WorkoutResultsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 920A3EF02A1F7C2300EC6FC9 /* WorkoutResultsView.swift */; }; - 920A3EF32A1F801A00EC6FC9 /* Workout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 920A3EF22A1F801A00EC6FC9 /* Workout.swift */; }; 920A3EF62A20B14100EC6FC9 /* PagerTabStripView in Frameworks */ = {isa = PBXBuildFile; productRef = 920A3EF52A20B14100EC6FC9 /* PagerTabStripView */; }; 9215905F2A2A10BF001254BC /* InstructionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9215905E2A2A10BF001254BC /* InstructionsView.swift */; }; 924D6E222A264B3600227183 /* JsonWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 924D6E212A264B3600227183 /* JsonWriter.swift */; }; @@ -25,54 +23,24 @@ 92CACFD12A1B7DD100DA2B40 /* FitCountApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92CACFD02A1B7DD100DA2B40 /* FitCountApp.swift */; }; 92CACFD32A1B7DD100DA2B40 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92CACFD22A1B7DD100DA2B40 /* ContentView.swift */; }; 92CACFD52A1B7DD100DA2B40 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 92CACFD42A1B7DD100DA2B40 /* Assets.xcassets */; }; - 92CACFD82A1B7DD100DA2B40 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 92CACFD72A1B7DD100DA2B40 /* Preview Assets.xcassets */; }; - 92CACFE22A1B7DD200DA2B40 /* FitCountTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92CACFE12A1B7DD200DA2B40 /* FitCountTests.swift */; }; - 92CACFEC2A1B7DD200DA2B40 /* FitCountUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92CACFEB2A1B7DD200DA2B40 /* FitCountUITests.swift */; }; - 92CACFEE2A1B7DD200DA2B40 /* FitCountUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92CACFED2A1B7DD200DA2B40 /* FitCountUITestsLaunchTests.swift */; }; 92CACFFB2A1B8F9D00DA2B40 /* ExerciseDetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92CACFFA2A1B8F9D00DA2B40 /* ExerciseDetailsView.swift */; }; 92CACFFD2A1B99A500DA2B40 /* WorkoutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92CACFFC2A1B99A500DA2B40 /* WorkoutView.swift */; }; 92F2D1FB2A1D0C8400EC1B81 /* Text2Speech.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92F2D1FA2A1D0C8400EC1B81 /* Text2Speech.swift */; }; /* End PBXBuildFile section */ -/* Begin PBXContainerItemProxy section */ - 92CACFDE2A1B7DD200DA2B40 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 92CACFC52A1B7DD000DA2B40 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 92CACFCC2A1B7DD100DA2B40; - remoteInfo = FitCount; - }; - 92CACFE82A1B7DD200DA2B40 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 92CACFC52A1B7DD000DA2B40 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 92CACFCC2A1B7DD100DA2B40; - remoteInfo = FitCount; - }; -/* End PBXContainerItemProxy section */ - /* Begin PBXFileReference section */ 920A3EEB2A1F6E0E00EC6FC9 /* Timer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Timer.swift; sourceTree = ""; }; - 920A3EEE2A1F76F800EC6FC9 /* BoundingBoxView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoundingBoxView.swift; sourceTree = ""; }; 920A3EF02A1F7C2300EC6FC9 /* WorkoutResultsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkoutResultsView.swift; sourceTree = ""; }; - 920A3EF22A1F801A00EC6FC9 /* Workout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Workout.swift; sourceTree = ""; }; 9215905E2A2A10BF001254BC /* InstructionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstructionsView.swift; sourceTree = ""; }; 924D6E212A264B3600227183 /* JsonWriter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JsonWriter.swift; sourceTree = ""; }; 924D6E242A289ED700227183 /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = ""; }; 924D6E272A29F90400227183 /* VolumeChangeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VolumeChangeView.swift; sourceTree = ""; }; 927261A32A24EF0B00C3B390 /* HistoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryView.swift; sourceTree = ""; }; 92C702F82A1BC705002ECC0B /* QuickPoseBasicView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickPoseBasicView.swift; sourceTree = ""; }; - 92C703032A1D049E002ECC0B /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 92CACFCD2A1B7DD100DA2B40 /* FitCounter.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FitCounter.app; sourceTree = BUILT_PRODUCTS_DIR; }; 92CACFD02A1B7DD100DA2B40 /* FitCountApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FitCountApp.swift; sourceTree = ""; }; 92CACFD22A1B7DD100DA2B40 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 92CACFD42A1B7DD100DA2B40 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 92CACFD72A1B7DD100DA2B40 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; - 92CACFDD2A1B7DD200DA2B40 /* FitCountTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FitCountTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 92CACFE12A1B7DD200DA2B40 /* FitCountTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FitCountTests.swift; sourceTree = ""; }; - 92CACFE72A1B7DD200DA2B40 /* FitCountUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FitCountUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 92CACFEB2A1B7DD200DA2B40 /* FitCountUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FitCountUITests.swift; sourceTree = ""; }; - 92CACFED2A1B7DD200DA2B40 /* FitCountUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FitCountUITestsLaunchTests.swift; sourceTree = ""; }; 92CACFFA2A1B8F9D00DA2B40 /* ExerciseDetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExerciseDetailsView.swift; sourceTree = ""; }; 92CACFFC2A1B99A500DA2B40 /* WorkoutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkoutView.swift; sourceTree = ""; }; 92F2D1FA2A1D0C8400EC1B81 /* Text2Speech.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Text2Speech.swift; sourceTree = ""; }; @@ -91,20 +59,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 92CACFDA2A1B7DD200DA2B40 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 92CACFE42A1B7DD200DA2B40 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -114,9 +68,7 @@ 92CACFFA2A1B8F9D00DA2B40 /* ExerciseDetailsView.swift */, 92C702F82A1BC705002ECC0B /* QuickPoseBasicView.swift */, 92CACFFC2A1B99A500DA2B40 /* WorkoutView.swift */, - 920A3EEE2A1F76F800EC6FC9 /* BoundingBoxView.swift */, 920A3EF02A1F7C2300EC6FC9 /* WorkoutResultsView.swift */, - 920A3EF22A1F801A00EC6FC9 /* Workout.swift */, 924D6E272A29F90400227183 /* VolumeChangeView.swift */, 9215905E2A2A10BF001254BC /* InstructionsView.swift */, ); @@ -143,8 +95,6 @@ isa = PBXGroup; children = ( 92CACFCF2A1B7DD100DA2B40 /* FitCount */, - 92CACFE02A1B7DD200DA2B40 /* FitCountTests */, - 92CACFEA2A1B7DD200DA2B40 /* FitCountUITests */, 92CACFCE2A1B7DD100DA2B40 /* Products */, ); sourceTree = ""; @@ -153,8 +103,6 @@ isa = PBXGroup; children = ( 92CACFCD2A1B7DD100DA2B40 /* FitCounter.app */, - 92CACFDD2A1B7DD200DA2B40 /* FitCountTests.xctest */, - 92CACFE72A1B7DD200DA2B40 /* FitCountUITests.xctest */, ); name = Products; sourceTree = ""; @@ -162,9 +110,7 @@ 92CACFCF2A1B7DD100DA2B40 /* FitCount */ = { isa = PBXGroup; children = ( - 92C703032A1D049E002ECC0B /* Info.plist */, 92CACFD42A1B7DD100DA2B40 /* Assets.xcassets */, - 92CACFD62A1B7DD100DA2B40 /* Preview Content */, 924D6E262A289F0100227183 /* History */, 920A3EED2A1F76D400EC6FC9 /* Workout */, 924D6E232A289EB600227183 /* About */, @@ -177,31 +123,6 @@ path = FitCount; sourceTree = ""; }; - 92CACFD62A1B7DD100DA2B40 /* Preview Content */ = { - isa = PBXGroup; - children = ( - 92CACFD72A1B7DD100DA2B40 /* Preview Assets.xcassets */, - ); - path = "Preview Content"; - sourceTree = ""; - }; - 92CACFE02A1B7DD200DA2B40 /* FitCountTests */ = { - isa = PBXGroup; - children = ( - 92CACFE12A1B7DD200DA2B40 /* FitCountTests.swift */, - ); - path = FitCountTests; - sourceTree = ""; - }; - 92CACFEA2A1B7DD200DA2B40 /* FitCountUITests */ = { - isa = PBXGroup; - children = ( - 92CACFEB2A1B7DD200DA2B40 /* FitCountUITests.swift */, - 92CACFED2A1B7DD200DA2B40 /* FitCountUITestsLaunchTests.swift */, - ); - path = FitCountUITests; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -229,42 +150,6 @@ productReference = 92CACFCD2A1B7DD100DA2B40 /* FitCounter.app */; productType = "com.apple.product-type.application"; }; - 92CACFDC2A1B7DD200DA2B40 /* FitCountTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 92CACFF42A1B7DD200DA2B40 /* Build configuration list for PBXNativeTarget "FitCountTests" */; - buildPhases = ( - 92CACFD92A1B7DD200DA2B40 /* Sources */, - 92CACFDA2A1B7DD200DA2B40 /* Frameworks */, - 92CACFDB2A1B7DD200DA2B40 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 92CACFDF2A1B7DD200DA2B40 /* PBXTargetDependency */, - ); - name = FitCountTests; - productName = FitCountTests; - productReference = 92CACFDD2A1B7DD200DA2B40 /* FitCountTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - 92CACFE62A1B7DD200DA2B40 /* FitCountUITests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 92CACFF72A1B7DD200DA2B40 /* Build configuration list for PBXNativeTarget "FitCountUITests" */; - buildPhases = ( - 92CACFE32A1B7DD200DA2B40 /* Sources */, - 92CACFE42A1B7DD200DA2B40 /* Frameworks */, - 92CACFE52A1B7DD200DA2B40 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 92CACFE92A1B7DD200DA2B40 /* PBXTargetDependency */, - ); - name = FitCountUITests; - productName = FitCountUITests; - productReference = 92CACFE72A1B7DD200DA2B40 /* FitCountUITests.xctest */; - productType = "com.apple.product-type.bundle.ui-testing"; - }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -278,14 +163,6 @@ 92CACFCC2A1B7DD100DA2B40 = { CreatedOnToolsVersion = 14.3; }; - 92CACFDC2A1B7DD200DA2B40 = { - CreatedOnToolsVersion = 14.3; - TestTargetID = 92CACFCC2A1B7DD100DA2B40; - }; - 92CACFE62A1B7DD200DA2B40 = { - CreatedOnToolsVersion = 14.3; - TestTargetID = 92CACFCC2A1B7DD100DA2B40; - }; }; }; buildConfigurationList = 92CACFC82A1B7DD000DA2B40 /* Build configuration list for PBXProject "FitCounter by QuickPose.ai" */; @@ -306,8 +183,6 @@ projectRoot = ""; targets = ( 92CACFCC2A1B7DD100DA2B40 /* FitCounter */, - 92CACFDC2A1B7DD200DA2B40 /* FitCountTests */, - 92CACFE62A1B7DD200DA2B40 /* FitCountUITests */, ); }; /* End PBXProject section */ @@ -317,25 +192,10 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 92CACFD82A1B7DD100DA2B40 /* Preview Assets.xcassets in Resources */, 92CACFD52A1B7DD100DA2B40 /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; - 92CACFDB2A1B7DD200DA2B40 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 92CACFE52A1B7DD200DA2B40 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -346,8 +206,6 @@ 92CACFD32A1B7DD100DA2B40 /* ContentView.swift in Sources */, 9215905F2A2A10BF001254BC /* InstructionsView.swift in Sources */, 92CACFD12A1B7DD100DA2B40 /* FitCountApp.swift in Sources */, - 920A3EEF2A1F76F800EC6FC9 /* BoundingBoxView.swift in Sources */, - 920A3EF32A1F801A00EC6FC9 /* Workout.swift in Sources */, 924D6E282A29F90400227183 /* VolumeChangeView.swift in Sources */, 924D6E252A289ED700227183 /* AboutView.swift in Sources */, 920A3EF12A1F7C2300EC6FC9 /* WorkoutResultsView.swift in Sources */, @@ -361,38 +219,8 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 92CACFD92A1B7DD200DA2B40 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 92CACFE22A1B7DD200DA2B40 /* FitCountTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 92CACFE32A1B7DD200DA2B40 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 92CACFEE2A1B7DD200DA2B40 /* FitCountUITestsLaunchTests.swift in Sources */, - 92CACFEC2A1B7DD200DA2B40 /* FitCountUITests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXSourcesBuildPhase section */ -/* Begin PBXTargetDependency section */ - 92CACFDF2A1B7DD200DA2B40 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 92CACFCC2A1B7DD100DA2B40 /* FitCounter */; - targetProxy = 92CACFDE2A1B7DD200DA2B40 /* PBXContainerItemProxy */; - }; - 92CACFE92A1B7DD200DA2B40 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 92CACFCC2A1B7DD100DA2B40 /* FitCounter */; - targetProxy = 92CACFE82A1B7DD200DA2B40 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - /* Begin XCBuildConfiguration section */ 92CACFEF2A1B7DD200DA2B40 /* Debug */ = { isa = XCBuildConfiguration; @@ -515,7 +343,6 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_ASSET_PATHS = "\"FitCount/Preview Content\""; DEVELOPMENT_TEAM = 22W63VGZT5; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; @@ -526,7 +353,7 @@ INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; - IPHONEOS_DEPLOYMENT_TARGET = 16.3; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -550,7 +377,6 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_ASSET_PATHS = "\"FitCount/Preview Content\""; DEVELOPMENT_TEAM = 22W63VGZT5; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; @@ -561,7 +387,7 @@ INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; - IPHONEOS_DEPLOYMENT_TARGET = 16.3; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -578,82 +404,6 @@ }; name = Release; }; - 92CACFF52A1B7DD200DA2B40 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 22W63VGZT5; - GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 16.4; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = ai.quickpose.FitCountTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/FitCount.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/FitCount"; - }; - name = Debug; - }; - 92CACFF62A1B7DD200DA2B40 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 22W63VGZT5; - GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 16.4; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = ai.quickpose.FitCountTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/FitCount.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/FitCount"; - }; - name = Release; - }; - 92CACFF82A1B7DD200DA2B40 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 22W63VGZT5; - GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = ai.quickpose.FitCountUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_TARGET_NAME = FitCount; - }; - name = Debug; - }; - 92CACFF92A1B7DD200DA2B40 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 22W63VGZT5; - GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = ai.quickpose.FitCountUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_TARGET_NAME = FitCount; - }; - name = Release; - }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -675,24 +425,6 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 92CACFF42A1B7DD200DA2B40 /* Build configuration list for PBXNativeTarget "FitCountTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 92CACFF52A1B7DD200DA2B40 /* Debug */, - 92CACFF62A1B7DD200DA2B40 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 92CACFF72A1B7DD200DA2B40 /* Build configuration list for PBXNativeTarget "FitCountUITests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 92CACFF82A1B7DD200DA2B40 /* Debug */, - 92CACFF92A1B7DD200DA2B40 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ diff --git a/FitCounter by QuickPose.ai.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/FitCounter by QuickPose.ai.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 8cebcaa..705be83 100644 --- a/FitCounter by QuickPose.ai.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/FitCounter by QuickPose.ai.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -15,7 +15,7 @@ "location" : "https://github.com/quickpose/quickpose-ios-sdk.git", "state" : { "branch" : "main", - "revision" : "3997bc1225a29e5a6a8c4c62ebd0b35bf85a81ef" + "revision" : "8fb20061d4787bda8250b2e88b0d70f94795efc2" } } ], diff --git a/FitCounter by QuickPose.ai.xcodeproj/xcshareddata/xcschemes/FitCounter.xcscheme b/FitCounter by QuickPose.ai.xcodeproj/xcshareddata/xcschemes/FitCounter.xcscheme index e8f13b0..b4670b6 100644 --- a/FitCounter by QuickPose.ai.xcodeproj/xcshareddata/xcschemes/FitCounter.xcscheme +++ b/FitCounter by QuickPose.ai.xcodeproj/xcshareddata/xcschemes/FitCounter.xcscheme @@ -1,7 +1,7 @@ + version = "1.8"> @@ -28,6 +28,8 @@ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES" shouldAutocreateTestPlan = "YES"> + + From a75c17df3acebbcc128182b0f7650b0f0e69046e Mon Sep 17 00:00:00 2001 From: Peter Nash <480796+pablonosh@users.noreply.github.com> Date: Tue, 13 Jun 2023 11:19:56 +0100 Subject: [PATCH 2/2] using inside bounding box --- FitCount/Workout/QuickPoseBasicView.swift | 172 +++++++++--------- .../project.pbxproj | 63 ++++--- .../xcshareddata/swiftpm/Package.resolved | 9 - 3 files changed, 124 insertions(+), 120 deletions(-) diff --git a/FitCount/Workout/QuickPoseBasicView.swift b/FitCount/Workout/QuickPoseBasicView.swift index 5c5034a..f156ef7 100644 --- a/FitCount/Workout/QuickPoseBasicView.swift +++ b/FitCount/Workout/QuickPoseBasicView.swift @@ -19,7 +19,7 @@ enum ViewState: Equatable { case startVolume case instructions case introBoundingBox - case boundingBox(enterTime: Date) + case boundingBox case introExercise(Exercise) case exercise(SessionData, enterTime: Date) case results(SessionData) @@ -36,6 +36,15 @@ enum ViewState: Equatable { return nil } } + + var features: [QuickPose.Feature]? { + switch self { + case .introBoundingBox, .boundingBox: + return [.inside(edgeInsets: QuickPose.RelativeCameraEdgeInsets(top: 0.1, left: 0.2, bottom: 0.01, right: 0.2))] + default: + return nil + } + } } struct QuickPoseBasicView: View { @@ -47,18 +56,12 @@ struct QuickPoseBasicView: View { @State private var feedbackText: String? = nil @State private var counter = QuickPoseThresholdCounter() + @State private var unchanged = QuickPoseDoubleUnchangedDetector(similarDuration: 2, leniency: 0) @State private var state: ViewState = .startVolume + @State private var boundingBoxVisibility = 1.0 @State private var countScale = 1.0 @State private var boundingBoxMaskWidth = 0.0 - - func canMoveFromBoundingBox(landmarks: QuickPose.Landmarks) -> Bool { - - let xsInBox = landmarks.poseLandmarks.allSatisfy { 0.5 - (0.6/2) < $0[0] && $0[0] < 0.5 + (0.6/2) } - let ysInBox = landmarks.poseLandmarks.allSatisfy { 0.5 - (0.8/2) < $0[1] && $0[1] < 0.5 + (0.8/2) } - - return xsInBox && ysInBox - } var body: some View { GeometryReader { geometry in @@ -102,12 +105,12 @@ struct QuickPoseBasicView: View { } .frame(width: geometry.size.width * 0.6, height: geometry.size.height * 0.8) .padding(.horizontal, (geometry.size.width * 1 - 0.6)/2) - + case .boundingBox: ZStack { RoundedRectangle(cornerRadius: 15) - .stroke(.green, lineWidth: 5) - + .stroke(boundingBoxVisibility == 1 ? .green : .red, lineWidth: 5) + RoundedRectangle(cornerRadius: 15) .fill(.green.opacity(0.5)) .mask(alignment: .leading) { @@ -117,7 +120,7 @@ struct QuickPoseBasicView: View { } .frame(width: geometry.size.width * 0.6, height: geometry.size.height * 0.8) .padding(.horizontal, (geometry.size.width * 1 - 0.6)/2) - + case .results(let results): WorkoutResultsView(sessionData: results) .environmentObject(viewModel) @@ -176,89 +179,94 @@ struct QuickPoseBasicView: View { AVSpeechSynthesizer().speak(utterance) } - if state == .introBoundingBox { - quickPose.start(features: sessionConfig.exercise.features, onFrame: { status, image, features, feedback, landmarks in - overlayImage = image - if case .success(_,_) = status { - - switch state { - case .introBoundingBox: - - if let landmarks = landmarks, canMoveFromBoundingBox(landmarks: landmarks) { + if case .results(let result) = state { + let sessionDataDump = SessionDataModel(exercise: sessionConfig.exercise.name, count: result.count, seconds: result.seconds, date: Date()) + appendToJson(sessionData: sessionDataDump) + } + + quickPose.update(features: state.features ?? sessionConfig.exercise.features) + } + .onAppear() { + UIApplication.shared.isIdleTimerDisabled = true + quickPose.start(features: state.features ?? sessionConfig.exercise.features, onFrame: { status, image, features, feedback, landmarks in + overlayImage = image + if case .success(_,_) = status { - state = .boundingBox(enterTime: Date()) - boundingBoxMaskWidth = 0 - withAnimation(.easeInOut(duration: 2)) { - boundingBoxMaskWidth = 1.0 - } - } - case .boundingBox(let enterDate): - if let landmarks = landmarks, canMoveFromBoundingBox(landmarks: landmarks) { - if -enterDate.timeIntervalSinceNow > 2 { - state = .introExercise(sessionConfig.exercise) + switch state { + case .introBoundingBox: + + if let result = features.first?.value, result.value == 1.0 { + unchanged.reset() + state = .boundingBox + } + case .boundingBox: + if let dictionaryEntry = features.first { + let result = dictionaryEntry.value + boundingBoxVisibility = result.value + unchanged.count(result: result.value) { + if result.value == 1 { // been in for 2 seconds + boundingBoxMaskWidth = 0 + withAnimation(.easeInOut(duration: 1)) { + boundingBoxMaskWidth = 1.0 + } + DispatchQueue.main.asyncAfter(deadline: .now()+1) { + state = .introExercise(sessionConfig.exercise) + } + } else { + state = .introBoundingBox } - } else { - state = .introBoundingBox } + } - case .introExercise(_): - DispatchQueue.main.asyncAfter(deadline: .now()+0.5) { - state = .exercise(SessionData(count: 0, seconds: 0), enterTime: Date()) - } - case .exercise(_, let enterDate): - let secondsElapsed = Int(-enterDate.timeIntervalSinceNow) - - if let feedback = feedback[.fitness(.bicepCurls)] { - feedbackText = feedback.displayString - } else { - feedbackText = nil - - if case .fitness = sessionConfig.exercise.features.first, let result = features[sessionConfig.exercise.features.first!] { - _ = counter.count(result.value) { newState in - if !newState.isEntered { - DispatchQueue.main.asyncAfter(deadline: .now()+0.1) { - withAnimation(.easeInOut(duration: 0.1)) { - countScale = 2.0 - } - DispatchQueue.main.asyncAfter(deadline: .now()+0.4) { - withAnimation(.easeInOut(duration: 0.2)) { - countScale = 1.0 - } + case .introExercise(_): + DispatchQueue.main.asyncAfter(deadline: .now()+0.5) { + state = .exercise(SessionData(count: 0, seconds: 0), enterTime: Date()) + } + case .exercise(_, let enterDate): + let secondsElapsed = Int(-enterDate.timeIntervalSinceNow) + + if let feedback = feedback[.fitness(.bicepCurls)] { + feedbackText = feedback.displayString + } else { + feedbackText = nil + + if case .fitness = sessionConfig.exercise.features.first, let result = features[sessionConfig.exercise.features.first!] { + _ = counter.count(result.value) { newState in + if !newState.isEntered { + DispatchQueue.main.asyncAfter(deadline: .now()+0.1) { + withAnimation(.easeInOut(duration: 0.1)) { + countScale = 2.0 + } + DispatchQueue.main.asyncAfter(deadline: .now()+0.4) { + withAnimation(.easeInOut(duration: 0.2)) { + countScale = 1.0 } } } } } } + } - let newResults = SessionData(count: counter.state.count, seconds: secondsElapsed) - state = .exercise(newResults, enterTime: enterDate) // refresh view for every updated second - var hasFinished = false - if sessionConfig.useReps { - hasFinished = counter.state.count >= sessionConfig.nReps - } else { - hasFinished = secondsElapsed >= sessionConfig.nSeconds + sessionConfig.nMinutes * 60 - } + let newResults = SessionData(count: counter.state.count, seconds: secondsElapsed) + state = .exercise(newResults, enterTime: enterDate) // refresh view for every updated second + var hasFinished = false + if sessionConfig.useReps { + hasFinished = counter.state.count >= sessionConfig.nReps + } else { + hasFinished = secondsElapsed >= sessionConfig.nSeconds + sessionConfig.nMinutes * 60 + } - if hasFinished { - state = .results(newResults) - } - default: - break + if hasFinished { + state = .results(newResults) } - } else { - state = .introBoundingBox + default: + break } - }) - } - - if case .results(let result) = state { - let sessionDataDump = SessionDataModel(exercise: sessionConfig.exercise.name, count: result.count, seconds: result.seconds, date: Date()) - appendToJson(sessionData: sessionDataDump) - } - } - .onAppear() { - UIApplication.shared.isIdleTimerDisabled = true + } else if state != .startVolume && state != .instructions{ + state = .introBoundingBox + } + }) } .onDisappear { quickPose.stop() diff --git a/FitCounter by QuickPose.ai.xcodeproj/project.pbxproj b/FitCounter by QuickPose.ai.xcodeproj/project.pbxproj index 9555aee..7cfe2cc 100644 --- a/FitCounter by QuickPose.ai.xcodeproj/project.pbxproj +++ b/FitCounter by QuickPose.ai.xcodeproj/project.pbxproj @@ -16,16 +16,16 @@ 924D6E282A29F90400227183 /* VolumeChangeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 924D6E272A29F90400227183 /* VolumeChangeView.swift */; }; 927261A42A24EF0B00C3B390 /* HistoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 927261A32A24EF0B00C3B390 /* HistoryView.swift */; }; 92C702F92A1BC705002ECC0B /* QuickPoseBasicView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92C702F82A1BC705002ECC0B /* QuickPoseBasicView.swift */; }; - 92C702FC2A1BC74A002ECC0B /* QuickPoseCamera in Frameworks */ = {isa = PBXBuildFile; productRef = 92C702FB2A1BC74A002ECC0B /* QuickPoseCamera */; }; - 92C702FE2A1BC74A002ECC0B /* QuickPoseCore in Frameworks */ = {isa = PBXBuildFile; productRef = 92C702FD2A1BC74A002ECC0B /* QuickPoseCore */; }; - 92C703002A1BC74A002ECC0B /* QuickPoseMP in Frameworks */ = {isa = PBXBuildFile; productRef = 92C702FF2A1BC74A002ECC0B /* QuickPoseMP */; }; - 92C703022A1BC74A002ECC0B /* QuickPoseSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 92C703012A1BC74A002ECC0B /* QuickPoseSwiftUI */; }; 92CACFD12A1B7DD100DA2B40 /* FitCountApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92CACFD02A1B7DD100DA2B40 /* FitCountApp.swift */; }; 92CACFD32A1B7DD100DA2B40 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92CACFD22A1B7DD100DA2B40 /* ContentView.swift */; }; 92CACFD52A1B7DD100DA2B40 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 92CACFD42A1B7DD100DA2B40 /* Assets.xcassets */; }; 92CACFFB2A1B8F9D00DA2B40 /* ExerciseDetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92CACFFA2A1B8F9D00DA2B40 /* ExerciseDetailsView.swift */; }; 92CACFFD2A1B99A500DA2B40 /* WorkoutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92CACFFC2A1B99A500DA2B40 /* WorkoutView.swift */; }; 92F2D1FB2A1D0C8400EC1B81 /* Text2Speech.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92F2D1FA2A1D0C8400EC1B81 /* Text2Speech.swift */; }; + D06C7CD82A373D18001B4508 /* QuickPoseCamera in Frameworks */ = {isa = PBXBuildFile; productRef = D06C7CD72A373D18001B4508 /* QuickPoseCamera */; }; + D06C7CDA2A373D18001B4508 /* QuickPoseCore in Frameworks */ = {isa = PBXBuildFile; productRef = D06C7CD92A373D18001B4508 /* QuickPoseCore */; }; + D06C7CDC2A373D18001B4508 /* QuickPoseMP in Frameworks */ = {isa = PBXBuildFile; productRef = D06C7CDB2A373D18001B4508 /* QuickPoseMP */; }; + D06C7CDE2A373D18001B4508 /* QuickPoseSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = D06C7CDD2A373D18001B4508 /* QuickPoseSwiftUI */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -44,6 +44,7 @@ 92CACFFA2A1B8F9D00DA2B40 /* ExerciseDetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExerciseDetailsView.swift; sourceTree = ""; }; 92CACFFC2A1B99A500DA2B40 /* WorkoutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkoutView.swift; sourceTree = ""; }; 92F2D1FA2A1D0C8400EC1B81 /* Text2Speech.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Text2Speech.swift; sourceTree = ""; }; + D06C7CD52A373CFA001B4508 /* quickpose-ios-sdk */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = "quickpose-ios-sdk"; path = "../../quickpose-ios-sdk-private/quickpose-ios-sdk"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -51,11 +52,11 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 92C702FE2A1BC74A002ECC0B /* QuickPoseCore in Frameworks */, - 92C703002A1BC74A002ECC0B /* QuickPoseMP in Frameworks */, - 92C702FC2A1BC74A002ECC0B /* QuickPoseCamera in Frameworks */, + D06C7CDE2A373D18001B4508 /* QuickPoseSwiftUI in Frameworks */, + D06C7CDC2A373D18001B4508 /* QuickPoseMP in Frameworks */, + D06C7CD82A373D18001B4508 /* QuickPoseCamera in Frameworks */, 920A3EF62A20B14100EC6FC9 /* PagerTabStripView in Frameworks */, - 92C703022A1BC74A002ECC0B /* QuickPoseSwiftUI in Frameworks */, + D06C7CDA2A373D18001B4508 /* QuickPoseCore in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -94,8 +95,10 @@ 92CACFC42A1B7DD000DA2B40 = { isa = PBXGroup; children = ( + D06C7CD42A373CFA001B4508 /* Packages */, 92CACFCF2A1B7DD100DA2B40 /* FitCount */, 92CACFCE2A1B7DD100DA2B40 /* Products */, + D06C7CD62A373D18001B4508 /* Frameworks */, ); sourceTree = ""; }; @@ -123,6 +126,21 @@ path = FitCount; sourceTree = ""; }; + D06C7CD42A373CFA001B4508 /* Packages */ = { + isa = PBXGroup; + children = ( + D06C7CD52A373CFA001B4508 /* quickpose-ios-sdk */, + ); + name = Packages; + sourceTree = ""; + }; + D06C7CD62A373D18001B4508 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -140,11 +158,11 @@ ); name = FitCounter; packageProductDependencies = ( - 92C702FB2A1BC74A002ECC0B /* QuickPoseCamera */, - 92C702FD2A1BC74A002ECC0B /* QuickPoseCore */, - 92C702FF2A1BC74A002ECC0B /* QuickPoseMP */, - 92C703012A1BC74A002ECC0B /* QuickPoseSwiftUI */, 920A3EF52A20B14100EC6FC9 /* PagerTabStripView */, + D06C7CD72A373D18001B4508 /* QuickPoseCamera */, + D06C7CD92A373D18001B4508 /* QuickPoseCore */, + D06C7CDB2A373D18001B4508 /* QuickPoseMP */, + D06C7CDD2A373D18001B4508 /* QuickPoseSwiftUI */, ); productName = FitCount; productReference = 92CACFCD2A1B7DD100DA2B40 /* FitCounter.app */; @@ -175,7 +193,6 @@ ); mainGroup = 92CACFC42A1B7DD000DA2B40; packageReferences = ( - 92C702FA2A1BC74A002ECC0B /* XCRemoteSwiftPackageReference "quickpose-ios-sdk" */, 920A3EF42A20B14000EC6FC9 /* XCRemoteSwiftPackageReference "PagerTabStripView" */, ); productRefGroup = 92CACFCE2A1B7DD100DA2B40 /* Products */; @@ -436,14 +453,6 @@ minimumVersion = 4.0.0; }; }; - 92C702FA2A1BC74A002ECC0B /* XCRemoteSwiftPackageReference "quickpose-ios-sdk" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/quickpose/quickpose-ios-sdk.git"; - requirement = { - branch = main; - kind = branch; - }; - }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -452,24 +461,20 @@ package = 920A3EF42A20B14000EC6FC9 /* XCRemoteSwiftPackageReference "PagerTabStripView" */; productName = PagerTabStripView; }; - 92C702FB2A1BC74A002ECC0B /* QuickPoseCamera */ = { + D06C7CD72A373D18001B4508 /* QuickPoseCamera */ = { isa = XCSwiftPackageProductDependency; - package = 92C702FA2A1BC74A002ECC0B /* XCRemoteSwiftPackageReference "quickpose-ios-sdk" */; productName = QuickPoseCamera; }; - 92C702FD2A1BC74A002ECC0B /* QuickPoseCore */ = { + D06C7CD92A373D18001B4508 /* QuickPoseCore */ = { isa = XCSwiftPackageProductDependency; - package = 92C702FA2A1BC74A002ECC0B /* XCRemoteSwiftPackageReference "quickpose-ios-sdk" */; productName = QuickPoseCore; }; - 92C702FF2A1BC74A002ECC0B /* QuickPoseMP */ = { + D06C7CDB2A373D18001B4508 /* QuickPoseMP */ = { isa = XCSwiftPackageProductDependency; - package = 92C702FA2A1BC74A002ECC0B /* XCRemoteSwiftPackageReference "quickpose-ios-sdk" */; productName = QuickPoseMP; }; - 92C703012A1BC74A002ECC0B /* QuickPoseSwiftUI */ = { + D06C7CDD2A373D18001B4508 /* QuickPoseSwiftUI */ = { isa = XCSwiftPackageProductDependency; - package = 92C702FA2A1BC74A002ECC0B /* XCRemoteSwiftPackageReference "quickpose-ios-sdk" */; productName = QuickPoseSwiftUI; }; /* End XCSwiftPackageProductDependency section */ diff --git a/FitCounter by QuickPose.ai.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/FitCounter by QuickPose.ai.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 705be83..3f53c04 100644 --- a/FitCounter by QuickPose.ai.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/FitCounter by QuickPose.ai.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -8,15 +8,6 @@ "revision" : "39ec00d2577adf75d82d1c7023c56950b96d2298", "version" : "4.0.0" } - }, - { - "identity" : "quickpose-ios-sdk", - "kind" : "remoteSourceControl", - "location" : "https://github.com/quickpose/quickpose-ios-sdk.git", - "state" : { - "branch" : "main", - "revision" : "8fb20061d4787bda8250b2e88b0d70f94795efc2" - } } ], "version" : 2