├── WorkoutApp
├── SupportingFiles
│ ├── Assets.xcassets
│ │ ├── Contents.json
│ │ ├── common
│ │ │ ├── Contents.json
│ │ │ ├── add_button.imageset
│ │ │ │ ├── add_button.pdf
│ │ │ │ └── Contents.json
│ │ │ └── down_arrow.imageset
│ │ │ │ ├── down_arrow.pdf
│ │ │ │ └── Contents.json
│ │ ├── overview
│ │ │ ├── Contents.json
│ │ │ ├── checkmark_done.imageset
│ │ │ │ ├── Checked.pdf
│ │ │ │ └── Contents.json
│ │ │ ├── checkmark_not_done.imageset
│ │ │ │ ├── Oval.pdf
│ │ │ │ └── Contents.json
│ │ │ └── right_arrow.imageset
│ │ │ │ ├── Right Arrow.pdf
│ │ │ │ └── Contents.json
│ │ ├── stats
│ │ │ ├── Contents.json
│ │ │ ├── stats_averagePace.imageset
│ │ │ │ ├── Speed Icon.pdf
│ │ │ │ └── Contents.json
│ │ │ ├── stats_totalSteps.imageset
│ │ │ │ ├── Steps Icon.pdf
│ │ │ │ └── Contents.json
│ │ │ ├── stats_heartRate.imageset
│ │ │ │ ├── Heart Beat Icon.pdf
│ │ │ │ └── Contents.json
│ │ │ └── stats_totalDistance.imageset
│ │ │ │ ├── Maps Icon.pdf
│ │ │ │ └── Contents.json
│ │ ├── tabBar
│ │ │ ├── Contents.json
│ │ │ ├── overview_tab.imageset
│ │ │ │ ├── overview_tab.pdf
│ │ │ │ └── Contents.json
│ │ │ ├── progress_tab.imageset
│ │ │ │ ├── progress_tab.pdf
│ │ │ │ └── Contents.json
│ │ │ ├── session_tab.imageset
│ │ │ │ ├── session_tab.pdf
│ │ │ │ └── Contents.json
│ │ │ └── settings_tab.imageset
│ │ │ │ ├── settings_tab.pdf
│ │ │ │ └── Contents.json
│ │ ├── AccentColor.colorset
│ │ │ └── Contents.json
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── Info.plist
│ └── Base.lproj
│ │ └── LaunchScreen.storyboard
├── Application
│ ├── AppDelegate.swift
│ └── SceneDelegate.swift
├── Controllers
│ ├── Settings
│ │ └── SettingsController.swift
│ ├── Base
│ │ ├── Views
│ │ │ ├── WABaseView.swift
│ │ │ ├── WABarsView
│ │ │ │ ├── WABarsView.swift
│ │ │ │ └── Views
│ │ │ │ │ └── WABarView.swift
│ │ │ ├── WAChartsView
│ │ │ │ ├── Views
│ │ │ │ │ ├── XAxisView.swift
│ │ │ │ │ ├── YAxisView.swift
│ │ │ │ │ └── ChartView.swift
│ │ │ │ └── WAChartsView.swift
│ │ │ ├── WAButton.swift
│ │ │ └── WABaseInfoView.swift
│ │ └── WABaseController.swift
│ ├── Navigation
│ │ ├── NavBarController.swift
│ │ └── TabBarController.swift
│ ├── Session
│ │ ├── Views
│ │ │ ├── Steps
│ │ │ │ └── StepsView.swift
│ │ │ ├── Stats
│ │ │ │ ├── StatsView.swift
│ │ │ │ └── Views
│ │ │ │ │ └── StatsItemView.swift
│ │ │ └── Timer
│ │ │ │ ├── PercentView.swift
│ │ │ │ ├── ProgressView.swift
│ │ │ │ └── TimerView.swift
│ │ └── SessionController.swift
│ ├── Progress
│ │ ├── Views
│ │ │ ├── DailyPerformance
│ │ │ │ └── DailyPerformanceView.swift
│ │ │ └── MonthlyPerformnaceView
│ │ │ │ └── MonthlyPerformanceView.swift
│ │ └── ProgressController.swift
│ └── Overview
│ │ ├── Views
│ │ ├── NavBar
│ │ │ ├── WeekView
│ │ │ │ ├── WeekView.swift
│ │ │ │ └── WeekdayView.swift
│ │ │ └── OverviewNavBar.swift
│ │ └── CollectionView
│ │ │ └── Views
│ │ │ ├── SectionHeaderView.swift
│ │ │ └── TrainingCellView.swift
│ │ └── OverviewController.swift
└── Helpers
│ ├── Extensions
│ ├── Date + ext.swift
│ ├── UIColor + ext.swift
│ └── UIView + ext.swift
│ └── Resouces.swift
└── WorkoutApp.xcodeproj
├── project.xcworkspace
├── contents.xcworkspacedata
└── xcshareddata
│ └── IDEWorkspaceChecks.plist
├── xcuserdata
└── viktor.xcuserdatad
│ ├── xcdebugger
│ └── Breakpoints_v2.xcbkptlist
│ └── xcschemes
│ └── xcschememanagement.plist
└── project.pbxproj
/WorkoutApp/SupportingFiles/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/WorkoutApp/SupportingFiles/Assets.xcassets/common/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/WorkoutApp/SupportingFiles/Assets.xcassets/overview/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/WorkoutApp/SupportingFiles/Assets.xcassets/stats/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/WorkoutApp/SupportingFiles/Assets.xcassets/tabBar/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/WorkoutApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/WorkoutApp/SupportingFiles/Assets.xcassets/common/add_button.imageset/add_button.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VAmpersand/WorkoutApp/HEAD/WorkoutApp/SupportingFiles/Assets.xcassets/common/add_button.imageset/add_button.pdf
--------------------------------------------------------------------------------
/WorkoutApp/SupportingFiles/Assets.xcassets/common/down_arrow.imageset/down_arrow.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VAmpersand/WorkoutApp/HEAD/WorkoutApp/SupportingFiles/Assets.xcassets/common/down_arrow.imageset/down_arrow.pdf
--------------------------------------------------------------------------------
/WorkoutApp/SupportingFiles/Assets.xcassets/overview/checkmark_done.imageset/Checked.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VAmpersand/WorkoutApp/HEAD/WorkoutApp/SupportingFiles/Assets.xcassets/overview/checkmark_done.imageset/Checked.pdf
--------------------------------------------------------------------------------
/WorkoutApp/SupportingFiles/Assets.xcassets/overview/checkmark_not_done.imageset/Oval.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VAmpersand/WorkoutApp/HEAD/WorkoutApp/SupportingFiles/Assets.xcassets/overview/checkmark_not_done.imageset/Oval.pdf
--------------------------------------------------------------------------------
/WorkoutApp/SupportingFiles/Assets.xcassets/overview/right_arrow.imageset/Right Arrow.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VAmpersand/WorkoutApp/HEAD/WorkoutApp/SupportingFiles/Assets.xcassets/overview/right_arrow.imageset/Right Arrow.pdf
--------------------------------------------------------------------------------
/WorkoutApp/SupportingFiles/Assets.xcassets/tabBar/overview_tab.imageset/overview_tab.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VAmpersand/WorkoutApp/HEAD/WorkoutApp/SupportingFiles/Assets.xcassets/tabBar/overview_tab.imageset/overview_tab.pdf
--------------------------------------------------------------------------------
/WorkoutApp/SupportingFiles/Assets.xcassets/tabBar/progress_tab.imageset/progress_tab.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VAmpersand/WorkoutApp/HEAD/WorkoutApp/SupportingFiles/Assets.xcassets/tabBar/progress_tab.imageset/progress_tab.pdf
--------------------------------------------------------------------------------
/WorkoutApp/SupportingFiles/Assets.xcassets/tabBar/session_tab.imageset/session_tab.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VAmpersand/WorkoutApp/HEAD/WorkoutApp/SupportingFiles/Assets.xcassets/tabBar/session_tab.imageset/session_tab.pdf
--------------------------------------------------------------------------------
/WorkoutApp/SupportingFiles/Assets.xcassets/tabBar/settings_tab.imageset/settings_tab.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VAmpersand/WorkoutApp/HEAD/WorkoutApp/SupportingFiles/Assets.xcassets/tabBar/settings_tab.imageset/settings_tab.pdf
--------------------------------------------------------------------------------
/WorkoutApp/SupportingFiles/Assets.xcassets/stats/stats_averagePace.imageset/Speed Icon.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VAmpersand/WorkoutApp/HEAD/WorkoutApp/SupportingFiles/Assets.xcassets/stats/stats_averagePace.imageset/Speed Icon.pdf
--------------------------------------------------------------------------------
/WorkoutApp/SupportingFiles/Assets.xcassets/stats/stats_totalSteps.imageset/Steps Icon.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VAmpersand/WorkoutApp/HEAD/WorkoutApp/SupportingFiles/Assets.xcassets/stats/stats_totalSteps.imageset/Steps Icon.pdf
--------------------------------------------------------------------------------
/WorkoutApp/SupportingFiles/Assets.xcassets/stats/stats_heartRate.imageset/Heart Beat Icon.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VAmpersand/WorkoutApp/HEAD/WorkoutApp/SupportingFiles/Assets.xcassets/stats/stats_heartRate.imageset/Heart Beat Icon.pdf
--------------------------------------------------------------------------------
/WorkoutApp/SupportingFiles/Assets.xcassets/stats/stats_totalDistance.imageset/Maps Icon.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VAmpersand/WorkoutApp/HEAD/WorkoutApp/SupportingFiles/Assets.xcassets/stats/stats_totalDistance.imageset/Maps Icon.pdf
--------------------------------------------------------------------------------
/WorkoutApp.xcodeproj/xcuserdata/viktor.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
--------------------------------------------------------------------------------
/WorkoutApp/SupportingFiles/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/WorkoutApp/SupportingFiles/Assets.xcassets/common/add_button.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "add_button.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/WorkoutApp/SupportingFiles/Assets.xcassets/common/down_arrow.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "down_arrow.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/WorkoutApp/SupportingFiles/Assets.xcassets/overview/checkmark_done.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Checked.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/WorkoutApp/SupportingFiles/Assets.xcassets/overview/checkmark_not_done.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Oval.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/WorkoutApp/SupportingFiles/Assets.xcassets/overview/right_arrow.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Right Arrow.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/WorkoutApp/SupportingFiles/Assets.xcassets/tabBar/overview_tab.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "overview_tab.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/WorkoutApp/SupportingFiles/Assets.xcassets/tabBar/progress_tab.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "progress_tab.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/WorkoutApp/SupportingFiles/Assets.xcassets/tabBar/session_tab.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "session_tab.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/WorkoutApp/SupportingFiles/Assets.xcassets/tabBar/settings_tab.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "settings_tab.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/WorkoutApp/SupportingFiles/Assets.xcassets/stats/stats_averagePace.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Speed Icon.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/WorkoutApp/SupportingFiles/Assets.xcassets/stats/stats_heartRate.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Heart Beat Icon.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/WorkoutApp/SupportingFiles/Assets.xcassets/stats/stats_totalDistance.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Maps Icon.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/WorkoutApp/SupportingFiles/Assets.xcassets/stats/stats_totalSteps.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Steps Icon.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/WorkoutApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/WorkoutApp.xcodeproj/xcuserdata/viktor.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | WorkoutApp.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/WorkoutApp/Application/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // WorkoutApp
4 | //
5 | // Created by Viktor Prikolota on 23.05.2022.
6 | //
7 |
8 | import UIKit
9 |
10 | @main
11 | class AppDelegate: UIResponder, UIApplicationDelegate {
12 |
13 | func application(
14 | _ application: UIApplication,
15 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
16 | ) -> Bool {
17 | return true
18 | }
19 | }
20 |
21 |
--------------------------------------------------------------------------------
/WorkoutApp/Controllers/Settings/SettingsController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SettingsController.swift
3 | // WorkoutApp
4 | //
5 | // Created by Viktor Prikolota on 23.05.2022.
6 | //
7 |
8 | import UIKit
9 |
10 | final class SettingsController: WABaseController {
11 |
12 | override func viewDidLoad() {
13 | super.viewDidLoad()
14 |
15 | title = R.Strings.NavBar.settings
16 | }
17 | }
18 |
19 | extension SettingsController {
20 | override func setupViews() {
21 | super.setupViews()
22 |
23 | }
24 |
25 | override func constraintViews() {
26 | super.constraintViews()
27 |
28 | }
29 |
30 | override func configureAppearance() {
31 | super.configureAppearance()
32 |
33 | title = R.Strings.NavBar.settings
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/WorkoutApp/Controllers/Base/Views/WABaseView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WABaseView.swift
3 | // WorkoutApp
4 | //
5 | // Created by Viktor Prikolota on 26.05.2022.
6 | //
7 |
8 | import UIKit
9 |
10 | class WABaseView: UIView {
11 | override init(frame: CGRect) {
12 | super.init(frame: frame)
13 |
14 | setupViews()
15 | constaintViews()
16 | configureAppearance()
17 | }
18 |
19 | required init?(coder: NSCoder) {
20 | super.init(coder: coder)
21 |
22 | setupViews()
23 | constaintViews()
24 | configureAppearance()
25 | }
26 | }
27 |
28 | @objc extension WABaseView {
29 | func setupViews() {}
30 | func constaintViews() {}
31 |
32 | func configureAppearance() {
33 | backgroundColor = .white
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/WorkoutApp/Controllers/Navigation/NavBarController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NavBarController.swift
3 | // WorkoutApp
4 | //
5 | // Created by Viktor Prikolota on 23.05.2022.
6 | //
7 |
8 | import UIKit
9 |
10 | final class NavBarController: UINavigationController {
11 |
12 | override func viewDidLoad() {
13 | super.viewDidLoad()
14 |
15 | configureAppearance()
16 | }
17 |
18 | private func configureAppearance() {
19 | view.backgroundColor = .white
20 | navigationBar.isTranslucent = false
21 | navigationBar.standardAppearance.titleTextAttributes = [
22 | .foregroundColor: R.Colors.titleGray,
23 | .font: R.Fonts.helvelticaRegular(with: 17)
24 | ]
25 |
26 | navigationBar.addBottomBorder(with: R.Colors.separator, height: 1)
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/WorkoutApp/Application/SceneDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SceneDelegate.swift
3 | // WorkoutApp
4 | //
5 | // Created by Viktor Prikolota on 23.05.2022.
6 | //
7 |
8 | import UIKit
9 |
10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate {
11 |
12 | var window: UIWindow?
13 |
14 | func scene(_ scene: UIScene,
15 | willConnectTo session: UISceneSession,
16 | options connectionOptions: UIScene.ConnectionOptions) {
17 |
18 | let tabBarController = TabBarController()
19 |
20 | guard let windowScene = (scene as? UIWindowScene) else { return }
21 | window = UIWindow(frame: windowScene.coordinateSpace.bounds)
22 | window?.windowScene = windowScene
23 | window?.rootViewController = tabBarController
24 | window?.makeKeyAndVisible()
25 | }
26 | }
27 |
28 |
--------------------------------------------------------------------------------
/WorkoutApp/SupportingFiles/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | UIApplicationSceneManifest
6 |
7 | UIApplicationSupportsMultipleScenes
8 |
9 | UISceneConfigurations
10 |
11 | UIWindowSceneSessionRoleApplication
12 |
13 |
14 | UISceneConfigurationName
15 | Default Configuration
16 | UISceneDelegateClassName
17 | $(PRODUCT_MODULE_NAME).SceneDelegate
18 |
19 |
20 |
21 |
22 | UIStatusBarStyle
23 | UIStatusBarStyleDefault
24 |
25 |
26 |
--------------------------------------------------------------------------------
/WorkoutApp/Helpers/Extensions/Date + ext.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Date + ext.swift
3 | // WorkoutApp
4 | //
5 | // Created by Viktor Prikolota on 28.05.2022.
6 | //
7 |
8 | import Foundation
9 |
10 | extension Date {
11 | static var calendar: Calendar = {
12 | var calendar = Calendar(identifier: .gregorian)
13 | calendar.firstWeekday = 2
14 | return calendar
15 | }()
16 |
17 | var startOfWeek: Date {
18 | let components = Date.calendar.dateComponents([.yearForWeekOfYear, .weekOfYear], from: self)
19 | guard let firstDay = Date.calendar.date(from: components) else { return self }
20 | return Date.calendar.date(byAdding: .day, value: 0, to: firstDay) ?? self
21 | }
22 |
23 | func agoForward(to days: Int) -> Date {
24 | return Date.calendar.date(byAdding: .day, value: days, to: self) ?? self
25 | }
26 |
27 | func stripTime() -> Date {
28 | let components = Date.calendar.dateComponents([.year, .month, .day], from: self)
29 | return Date.calendar.date(from: components) ?? self
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/WorkoutApp/Helpers/Extensions/UIColor + ext.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIColor + ext.swift
3 | // WorkoutApp
4 | //
5 | // Created by Viktor Prikolota on 23.05.2022.
6 | //
7 |
8 | import UIKit
9 |
10 | extension UIColor {
11 | convenience init(hexString: String) {
12 | let hex = hexString.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
13 | var int = UInt64()
14 | Scanner(string: hex).scanHexInt64(&int)
15 | let a, r, g, b: UInt64
16 | switch hex.count {
17 | case 3: // RGB (12-bit)
18 | (a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17)
19 | case 6: // RGB (24-bit)
20 | (a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF)
21 | case 8: // ARGB (32-bit)
22 | (a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
23 | default:
24 | (a, r, g, b) = (255, 0, 0, 0)
25 | }
26 | self.init(red: CGFloat(r) / 255, green: CGFloat(g) / 255, blue: CGFloat(b) / 255, alpha: CGFloat(a) / 255)
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/WorkoutApp/Controllers/Session/Views/Steps/StepsView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StepsView.swift
3 | // WorkoutApp
4 | //
5 | // Created by Viktor Prikolota on 08.10.2022.
6 | //
7 |
8 | import UIKit
9 |
10 | final class StepsView: WABaseInfoView {
11 |
12 | private let barsView = WABarsView()
13 |
14 | func configure(with itmes: [WABarView.Data]) {
15 | barsView.configure(with: itmes)
16 | }
17 | }
18 |
19 | extension StepsView {
20 | override func setupViews() {
21 | super.setupViews()
22 |
23 | setupView(barsView)
24 | }
25 |
26 | override func constaintViews() {
27 | super.constaintViews()
28 |
29 | NSLayoutConstraint.activate([
30 | barsView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 15),
31 | barsView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 10),
32 | barsView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -10),
33 | barsView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -15),
34 | ])
35 | }
36 |
37 | override func configureAppearance() {
38 | super.configureAppearance()
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/WorkoutApp/Controllers/Progress/Views/DailyPerformance/DailyPerformanceView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DailyPerformanceView.swift
3 | // WorkoutApp
4 | //
5 | // Created by Viktor Prikolota on 07.12.2022.
6 | //
7 |
8 | import UIKit
9 |
10 | final class DailyPerformanceView: WABaseInfoView {
11 |
12 | private let barsView = WABarsView()
13 |
14 | func configure(with itmes: [WABarView.Data]) {
15 | barsView.configure(with: itmes)
16 | }
17 | }
18 |
19 | extension DailyPerformanceView {
20 | override func setupViews() {
21 | super.setupViews()
22 |
23 | setupView(barsView)
24 | }
25 |
26 | override func constaintViews() {
27 | super.constaintViews()
28 |
29 | NSLayoutConstraint.activate([
30 | barsView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 15),
31 | barsView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 10),
32 | barsView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -10),
33 | barsView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -15),
34 | ])
35 | }
36 |
37 | override func configureAppearance() {
38 | super.configureAppearance()
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/WorkoutApp/Controllers/Progress/Views/MonthlyPerformnaceView/MonthlyPerformanceView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MonthlyPerformanceView.swift
3 | // WorkoutApp
4 | //
5 | // Created by Viktor Prikolota on 07.12.2022.
6 | //
7 |
8 | import UIKit
9 |
10 | final class MonthlyPerformanceView: WABaseInfoView {
11 |
12 | private let chartsView = WAChartsView()
13 |
14 | func configure(with itmes: [WAChartsView.Data], topChartOffset: Int) {
15 | chartsView.configure(with: itmes, topChartOffset: topChartOffset)
16 | }
17 | }
18 |
19 | extension MonthlyPerformanceView {
20 | override func setupViews() {
21 | super.setupViews()
22 |
23 | setupView(chartsView)
24 | }
25 |
26 | override func constaintViews() {
27 | super.constaintViews()
28 |
29 | NSLayoutConstraint.activate([
30 | chartsView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 15),
31 | chartsView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 15),
32 | chartsView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -15),
33 | chartsView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -15),
34 | ])
35 | }
36 |
37 | override func configureAppearance() {
38 | super.configureAppearance()
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/WorkoutApp/Controllers/Base/Views/WABarsView/WABarsView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WABarsView.swift
3 | // WorkoutApp
4 | //
5 | // Created by Viktor Prikolota on 08.10.2022.
6 | //
7 |
8 | import UIKit
9 |
10 | final class WABarsView: WABaseView {
11 |
12 | private let stackView: UIStackView = {
13 | let view = UIStackView()
14 | view.distribution = .fillEqually
15 | return view
16 | }()
17 |
18 | func configure(with data: [WABarView.Data]) {
19 | data.forEach {
20 | let barView = WABarView(data: $0)
21 | stackView.addArrangedSubview(barView)
22 | }
23 | }
24 | }
25 |
26 | extension WABarsView {
27 | override func setupViews() {
28 | super.setupViews()
29 |
30 | setupView(stackView)
31 | }
32 |
33 | override func constaintViews() {
34 | super.constaintViews()
35 |
36 | NSLayoutConstraint.activate([
37 | stackView.leadingAnchor.constraint(equalTo: leadingAnchor),
38 | stackView.topAnchor.constraint(equalTo: topAnchor),
39 | stackView.trailingAnchor.constraint(equalTo: trailingAnchor),
40 | stackView.bottomAnchor.constraint(equalTo: bottomAnchor),
41 | ])
42 | }
43 |
44 | override func configureAppearance() {
45 | super.configureAppearance()
46 |
47 | backgroundColor = .clear
48 | }
49 | }
50 |
51 |
--------------------------------------------------------------------------------
/WorkoutApp/Controllers/Session/Views/Stats/StatsView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StatsView.swift
3 | // WorkoutApp
4 | //
5 | // Created by Viktor Prikolota on 08.10.2022.
6 | //
7 |
8 | import UIKit
9 |
10 | final class StatsView: WABaseInfoView {
11 |
12 | private let stackView: UIStackView = {
13 | let view = UIStackView()
14 | view.axis = .vertical
15 | view.spacing = 15
16 | return view
17 | }()
18 |
19 | func configure(with itmes: [StatsItem]) {
20 | itmes.forEach {
21 | let itmeView = StatsItemView()
22 | itmeView.configure(with: $0)
23 | stackView.addArrangedSubview(itmeView)
24 | }
25 | }
26 | }
27 |
28 | extension StatsView {
29 | override func setupViews() {
30 | super.setupViews()
31 |
32 | setupView(stackView)
33 | }
34 |
35 | override func constaintViews() {
36 | super.constaintViews()
37 |
38 | NSLayoutConstraint.activate([
39 | stackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 15),
40 | stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 15),
41 | stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -15),
42 | stackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -15),
43 | ])
44 | }
45 |
46 | override func configureAppearance() {
47 | super.configureAppearance()
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/WorkoutApp/Controllers/Overview/Views/NavBar/WeekView/WeekView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WeekView.swift
3 | // WorkoutApp
4 | //
5 | // Created by Viktor Prikolota on 28.05.2022.
6 | //
7 |
8 | import UIKit
9 |
10 | final class WeekView: WABaseView {
11 |
12 | private let stackView: UIStackView = {
13 | let stackView = UIStackView()
14 | stackView.spacing = 7
15 | stackView.distribution = .fillEqually
16 | return stackView
17 | }()
18 | }
19 |
20 | extension WeekView {
21 | override func setupViews() {
22 | super.setupViews()
23 |
24 | setupView(stackView)
25 | }
26 |
27 | override func constaintViews() {
28 | super.constaintViews()
29 |
30 | NSLayoutConstraint.activate([
31 | stackView.topAnchor.constraint(equalTo: topAnchor),
32 | stackView.leadingAnchor.constraint(equalTo: leadingAnchor),
33 | stackView.trailingAnchor.constraint(equalTo: trailingAnchor),
34 | stackView.bottomAnchor.constraint(equalTo: bottomAnchor),
35 | ])
36 | }
37 |
38 | override func configureAppearance() {
39 | super.configureAppearance()
40 |
41 | var weekdays = Date.calendar.shortStandaloneWeekdaySymbols
42 |
43 | if Date.calendar.firstWeekday == 2 {
44 | let sun = weekdays.remove(at: 0)
45 | weekdays.append(sun)
46 | }
47 |
48 | weekdays.enumerated().forEach { index, name in
49 | let view = WeekdayView()
50 | view.configure(with: index, and: name)
51 | stackView.addArrangedSubview(view)
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/WorkoutApp/Controllers/Overview/Views/CollectionView/Views/SectionHeaderView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SectionHeaderView.swift
3 | // WorkoutApp
4 | //
5 | // Created by Viktor Prikolota on 10.02.2023.
6 | //
7 |
8 | import UIKit
9 |
10 | final class SectionHeaderView: UICollectionReusableView {
11 | static let id = "SectionHeaderView"
12 |
13 | private let title: UILabel = {
14 | let lable = UILabel()
15 | lable.font = R.Fonts.helvelticaRegular(with: 13)
16 | lable.textColor = R.Colors.inactive
17 | lable.textAlignment = .center
18 | return lable
19 | }()
20 |
21 | override init(frame: CGRect) {
22 | super.init(frame: frame)
23 |
24 | setupViews()
25 | constaintViews()
26 | configureAppearance()
27 | }
28 |
29 | required init?(coder: NSCoder) {
30 | super.init(frame: .zero)
31 |
32 | setupViews()
33 | constaintViews()
34 | configureAppearance()
35 | }
36 |
37 | func configure(with date: Date) {
38 | let dateFormatter = DateFormatter()
39 | dateFormatter.dateFormat = "EEEE, MMMM dd"
40 |
41 | self.title.text = dateFormatter.string(from: date).uppercased()
42 | }
43 | }
44 |
45 | private extension SectionHeaderView {
46 | func setupViews() {
47 | setupView(title)
48 | }
49 |
50 | func constaintViews() {
51 | NSLayoutConstraint.activate([
52 | title.centerXAnchor.constraint(equalTo: centerXAnchor),
53 | title.centerYAnchor.constraint(equalTo: centerYAnchor)
54 | ])
55 | }
56 |
57 | func configureAppearance() {}
58 | }
59 |
60 |
--------------------------------------------------------------------------------
/WorkoutApp/Controllers/Base/Views/WAChartsView/Views/XAxisView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // YAxisView.swift
3 | // WorkoutApp
4 | //
5 | // Created by Viktor Prikolota on 07.12.2022.
6 | //
7 |
8 | import UIKit
9 |
10 | final class XAxisView: WABaseView {
11 |
12 | private let stackView: UIStackView = {
13 | let view = UIStackView()
14 | view.distribution = .equalSpacing
15 | return view
16 | }()
17 |
18 | func configure(with data: [WAChartsView.Data]) {
19 | stackView.arrangedSubviews.forEach {
20 | $0.removeFromSuperview()
21 | }
22 |
23 | data.forEach {
24 | let lable = UILabel()
25 | lable.font = R.Fonts.helvelticaRegular(with: 9)
26 | lable.textColor = R.Colors.inactive
27 | lable.textAlignment = .center
28 | lable.text = $0.title.uppercased()
29 |
30 | stackView.addArrangedSubview(lable)
31 | }
32 | }
33 | }
34 |
35 | extension XAxisView {
36 | override func setupViews() {
37 | super.setupViews()
38 |
39 | setupView(stackView)
40 | }
41 |
42 | override func constaintViews() {
43 | super.constaintViews()
44 |
45 | NSLayoutConstraint.activate([
46 | stackView.leadingAnchor.constraint(equalTo: leadingAnchor),
47 | stackView.topAnchor.constraint(equalTo: topAnchor),
48 | stackView.trailingAnchor.constraint(equalTo: trailingAnchor),
49 | stackView.bottomAnchor.constraint(equalTo: bottomAnchor)
50 | ])
51 | }
52 |
53 | override func configureAppearance() {
54 | super.configureAppearance()
55 |
56 | backgroundColor = .clear
57 | }
58 | }
59 |
60 |
61 |
--------------------------------------------------------------------------------
/WorkoutApp/Controllers/Base/Views/WAChartsView/Views/YAxisView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // XAxisView.swift
3 | // WorkoutApp
4 | //
5 | // Created by Viktor Prikolota on 07.12.2022.
6 | //
7 |
8 | import UIKit
9 |
10 | final class YAxisView: WABaseView {
11 |
12 | private let stackView: UIStackView = {
13 | let view = UIStackView()
14 | view.axis = .vertical
15 | view.distribution = .equalSpacing
16 | return view
17 | }()
18 |
19 | func configure(with data: [WAChartsView.Data]) {
20 | stackView.arrangedSubviews.forEach {
21 | $0.removeFromSuperview()
22 | }
23 |
24 | (0...9).reversed().forEach {
25 | let lable = UILabel()
26 | lable.font = R.Fonts.helvelticaRegular(with: 9)
27 | lable.textColor = R.Colors.inactive
28 | lable.textAlignment = .right
29 | lable.text = "\($0 * 10)" // TODO: - Remake to colculated interval
30 |
31 | stackView.addArrangedSubview(lable)
32 | }
33 | }
34 | }
35 |
36 | extension YAxisView {
37 | override func setupViews() {
38 | super.setupViews()
39 |
40 | setupView(stackView)
41 | }
42 |
43 | override func constaintViews() {
44 | super.constaintViews()
45 |
46 | NSLayoutConstraint.activate([
47 | stackView.leadingAnchor.constraint(equalTo: leadingAnchor),
48 | stackView.topAnchor.constraint(equalTo: topAnchor),
49 | stackView.trailingAnchor.constraint(equalTo: trailingAnchor),
50 | stackView.bottomAnchor.constraint(equalTo: bottomAnchor)
51 | ])
52 | }
53 |
54 | override func configureAppearance() {
55 | super.configureAppearance()
56 |
57 | backgroundColor = .clear
58 | }
59 | }
60 |
61 |
62 |
--------------------------------------------------------------------------------
/WorkoutApp/Controllers/Base/WABaseController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WABaseController.swift
3 | // WorkoutApp
4 | //
5 | // Created by Viktor Prikolota on 23.05.2022.
6 | //
7 |
8 | import UIKit
9 |
10 | enum NavBarPosition {
11 | case left
12 | case right
13 | }
14 |
15 | class WABaseController: UIViewController {
16 |
17 | override func viewDidLoad() {
18 | super.viewDidLoad()
19 |
20 | setupViews()
21 | constraintViews()
22 | configureAppearance()
23 | }
24 | }
25 |
26 | @objc extension WABaseController {
27 |
28 | func setupViews() {}
29 | func constraintViews() {}
30 |
31 | func configureAppearance() {
32 | view.backgroundColor = R.Colors.background
33 | }
34 |
35 | func navBarLeftButtonHandler() {
36 | print("NavBar left button tapped")
37 | }
38 |
39 | func navBarRightButtonHandler() {
40 | print("NavBar right button tapped")
41 | }
42 | }
43 |
44 | extension WABaseController {
45 | func addNavBarButton(at position: NavBarPosition, with title: String) {
46 | let button = UIButton(type: .system)
47 | button.setTitle(title, for: .normal)
48 | button.setTitleColor(R.Colors.active, for: .normal)
49 | button.setTitleColor(R.Colors.inactive, for: .disabled)
50 | button.titleLabel?.font = R.Fonts.helvelticaRegular(with: 17)
51 |
52 | switch position {
53 | case .left:
54 | button.addTarget(self, action: #selector(navBarLeftButtonHandler), for: .touchUpInside)
55 | navigationItem.leftBarButtonItem = UIBarButtonItem(customView: button)
56 | case .right:
57 | button.addTarget(self, action: #selector(navBarRightButtonHandler), for: .touchUpInside)
58 | navigationItem.rightBarButtonItem = UIBarButtonItem(customView: button)
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/WorkoutApp/Controllers/Session/Views/Timer/PercentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PercentView.swift
3 | // WorkoutApp
4 | //
5 | // Created by Viktor Prikolota on 05.09.2022.
6 | //
7 |
8 | import UIKit
9 |
10 | extension TimerView {
11 | final class PercentView: WABaseView {
12 |
13 | private let stackView: UIStackView = {
14 | let view = UIStackView()
15 | view.axis = .vertical
16 | view.distribution = .fillProportionally
17 | view.spacing = 5
18 | return view
19 | }()
20 |
21 | private let percentLabel: UILabel = {
22 | let label = UILabel()
23 | label.font = R.Fonts.helvelticaRegular(with: 23)
24 | label.textColor = R.Colors.titleGray
25 | label.textAlignment = .center
26 | return label
27 | }()
28 |
29 | private let subtitleLabel: UILabel = {
30 | let label = UILabel()
31 | label.font = R.Fonts.helvelticaRegular(with: 10)
32 | label.textColor = R.Colors.inactive
33 | label.textAlignment = .center
34 | return label
35 | }()
36 |
37 | override func setupViews() {
38 | super.setupViews()
39 |
40 | setupView(stackView)
41 | stackView.addArrangedSubview(percentLabel)
42 | stackView.addArrangedSubview(subtitleLabel)
43 | }
44 |
45 | override func constaintViews() {
46 | super.constaintViews()
47 |
48 | NSLayoutConstraint.activate([
49 | stackView.leadingAnchor.constraint(equalTo: leadingAnchor),
50 | stackView.topAnchor.constraint(equalTo: topAnchor),
51 | stackView.trailingAnchor.constraint(equalTo: trailingAnchor),
52 | stackView.bottomAnchor.constraint(equalTo: bottomAnchor)
53 | ])
54 | }
55 |
56 | func configure(with title: String, andValue value: Int) {
57 | subtitleLabel.text = title
58 | percentLabel.text = "\(value)%"
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/WorkoutApp/Controllers/Navigation/TabBarController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TabBarController.swift
3 | // WorkoutApp
4 | //
5 | // Created by Viktor Prikolota on 23.05.2022.
6 | //
7 |
8 | import UIKit
9 |
10 | enum Tabs: Int, CaseIterable {
11 | case overview
12 | case session
13 | case progress
14 | case settings
15 | }
16 |
17 | final class TabBarController: UITabBarController {
18 |
19 | override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
20 | super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
21 |
22 | configureAppearance()
23 | }
24 |
25 | required init?(coder: NSCoder) {
26 | super.init(coder: coder)
27 |
28 | configureAppearance()
29 | }
30 |
31 | func switchTo(tab: Tabs) {
32 | selectedIndex = tab.rawValue
33 | }
34 |
35 | private func configureAppearance() {
36 | tabBar.tintColor = R.Colors.active
37 | tabBar.barTintColor = R.Colors.inactive
38 | tabBar.backgroundColor = .white
39 | tabBar.layer.borderColor = R.Colors.separator.cgColor
40 | tabBar.layer.borderWidth = 1
41 | tabBar.layer.masksToBounds = true
42 |
43 | let controllers: [NavBarController] = Tabs.allCases.map { tab in
44 | let controller = NavBarController(rootViewController: getController(for: tab))
45 | controller.tabBarItem = UITabBarItem(title: R.Strings.TabBar.title(for: tab),
46 | image: R.Images.TabBar.icon(for: tab),
47 | tag: tab.rawValue)
48 | return controller
49 | }
50 |
51 | setViewControllers(controllers, animated: false)
52 | }
53 |
54 | private func getController(for tab: Tabs) -> WABaseController {
55 | switch tab {
56 | case .overview: return OverviewController()
57 | case .session: return SessionController()
58 | case .progress: return ProgressController()
59 | case .settings: return SettingsController()
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/WorkoutApp/SupportingFiles/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "20x20"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x",
11 | "size" : "20x20"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "scale" : "2x",
16 | "size" : "29x29"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "3x",
21 | "size" : "29x29"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "scale" : "2x",
26 | "size" : "40x40"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "scale" : "3x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "2x",
36 | "size" : "60x60"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "scale" : "3x",
41 | "size" : "60x60"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "scale" : "1x",
46 | "size" : "20x20"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "scale" : "2x",
51 | "size" : "20x20"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "scale" : "1x",
56 | "size" : "29x29"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "scale" : "2x",
61 | "size" : "29x29"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "scale" : "1x",
66 | "size" : "40x40"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "scale" : "2x",
71 | "size" : "40x40"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "scale" : "1x",
76 | "size" : "76x76"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "scale" : "2x",
81 | "size" : "76x76"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "scale" : "2x",
86 | "size" : "83.5x83.5"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "scale" : "1x",
91 | "size" : "1024x1024"
92 | }
93 | ],
94 | "info" : {
95 | "author" : "xcode",
96 | "version" : 1
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/WorkoutApp/Helpers/Extensions/UIView + ext.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIView + ext.swift
3 | // WorkoutApp
4 | //
5 | // Created by Viktor Prikolota on 23.05.2022.
6 | //
7 |
8 | import UIKit
9 |
10 | extension UIView {
11 | func addBottomBorder(with color: UIColor, height: CGFloat) {
12 | let separator = UIView()
13 | separator.backgroundColor = color
14 | separator.autoresizingMask = [.flexibleWidth, .flexibleHeight]
15 | separator.frame = CGRect(x: 0,
16 | y: frame.height - height,
17 | width: frame.width,
18 | height: height)
19 | addSubview(separator)
20 | }
21 |
22 | func makeSystem(_ button: UIButton) {
23 | button.addTarget(self, action: #selector(handleIn), for: [
24 | .touchDown,
25 | .touchDragInside
26 | ])
27 |
28 | button.addTarget(self, action: #selector(handleOut), for: [
29 | .touchDragOutside,
30 | .touchUpInside,
31 | .touchUpOutside,
32 | .touchDragExit,
33 | .touchCancel
34 | ])
35 | }
36 |
37 | @objc func handleIn() {
38 | UIView.animate(withDuration: 0.15) { self.alpha = 0.55 }
39 | }
40 |
41 | @objc func handleOut() {
42 | UIView.animate(withDuration: 0.15) { self.alpha = 1 }
43 | }
44 |
45 | @objc func setupView(_ view: UIView) {
46 | addSubview(view)
47 | view.translatesAutoresizingMaskIntoConstraints = false
48 | }
49 |
50 | func roundCorners(_ corners: UIRectCorner, radius: CGFloat) {
51 | let path = UIBezierPath(roundedRect: bounds,
52 | byRoundingCorners: corners,
53 | cornerRadii: CGSize(width: radius, height: radius))
54 |
55 | let borderLayer = CAShapeLayer()
56 | borderLayer.frame = bounds
57 | borderLayer.path = path.cgPath
58 | borderLayer.strokeColor = R.Colors.separator.cgColor
59 | borderLayer.fillColor = UIColor.clear.cgColor
60 | borderLayer.lineWidth = 1
61 | layer.addSublayer(borderLayer)
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/WorkoutApp/Controllers/Base/Views/WAChartsView/WAChartsView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WAChartsView.swift
3 | // WorkoutApp
4 | //
5 | // Created by Viktor Prikolota on 07.12.2022.
6 | //
7 |
8 | import UIKit
9 |
10 | extension WAChartsView {
11 | struct Data {
12 | let value: Int
13 | let title: String
14 | }
15 | }
16 |
17 | final class WAChartsView: WABaseView {
18 |
19 | private let yAxisView = YAxisView()
20 | private let xAxisView = XAxisView()
21 | private let chartView = ChartView()
22 |
23 | func configure(with data: [WAChartsView.Data], topChartOffset: Int = 10) {
24 | yAxisView.configure(with: data)
25 | xAxisView.configure(with: data)
26 | chartView.configure(with: data, topChartOffset: topChartOffset)
27 | }
28 | }
29 |
30 | extension WAChartsView {
31 | override func setupViews() {
32 | super.setupViews()
33 |
34 | setupView(yAxisView)
35 | setupView(xAxisView)
36 | setupView(chartView)
37 | }
38 |
39 | override func constaintViews() {
40 | super.constaintViews()
41 |
42 | NSLayoutConstraint.activate([
43 | yAxisView.leadingAnchor.constraint(equalTo: leadingAnchor),
44 | yAxisView.topAnchor.constraint(equalTo: topAnchor),
45 | yAxisView.bottomAnchor.constraint(equalTo: xAxisView.topAnchor, constant: -12),
46 |
47 | xAxisView.leadingAnchor.constraint(equalTo: yAxisView.trailingAnchor, constant: 8),
48 | xAxisView.bottomAnchor.constraint(equalTo: bottomAnchor),
49 | xAxisView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 4),
50 |
51 | chartView.leadingAnchor.constraint(equalTo: yAxisView.trailingAnchor, constant: 16),
52 | chartView.topAnchor.constraint(equalTo: topAnchor, constant: 4),
53 | chartView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -4),
54 | chartView.bottomAnchor.constraint(equalTo: xAxisView.topAnchor, constant: -16),
55 | ])
56 | }
57 |
58 | override func configureAppearance() {
59 | super.configureAppearance()
60 |
61 | backgroundColor = .clear
62 | }
63 | }
64 |
65 |
--------------------------------------------------------------------------------
/WorkoutApp/SupportingFiles/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/WorkoutApp/Controllers/Overview/Views/NavBar/WeekView/WeekdayView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WeekdayView.swift
3 | // WorkoutApp
4 | //
5 | // Created by Viktor Prikolota on 28.05.2022.
6 | //
7 |
8 | import UIKit
9 |
10 | extension WeekView {
11 | final class WeekdayView: WABaseView {
12 |
13 | private let nameLabel: UILabel = {
14 | let lable = UILabel()
15 | lable.font = R.Fonts.helvelticaRegular(with: 9)
16 | lable.textAlignment = .center
17 | return lable
18 | }()
19 |
20 | private let dateLabel: UILabel = {
21 | let lable = UILabel()
22 | lable.font = R.Fonts.helvelticaRegular(with: 15)
23 | lable.textAlignment = .center
24 | return lable
25 | }()
26 |
27 | private let stackView: UIStackView = {
28 | let view = UIStackView()
29 | view.spacing = 3
30 | view.axis = .vertical
31 | return view
32 | }()
33 |
34 | func configure(with index: Int, and name: String) {
35 | let startOfWeek = Date().startOfWeek
36 | let currenrDay = startOfWeek.agoForward(to: index)
37 | let day = Date.calendar.component(.day, from: currenrDay)
38 |
39 | let isToday = currenrDay.stripTime() == Date().stripTime()
40 |
41 | backgroundColor = isToday ? R.Colors.active : R.Colors.background
42 |
43 | nameLabel.text = name.uppercased()
44 | nameLabel.textColor = isToday ? .white : R.Colors.inactive
45 |
46 | dateLabel.text = "\(day)"
47 | dateLabel.textColor = isToday ? .white : R.Colors.inactive
48 | }
49 | }
50 | }
51 |
52 |
53 | extension WeekView.WeekdayView {
54 | override func setupViews() {
55 | super.setupViews()
56 |
57 | setupView(stackView)
58 |
59 | stackView.addArrangedSubview(nameLabel)
60 | stackView.addArrangedSubview(dateLabel)
61 | }
62 |
63 | override func constaintViews() {
64 | super.constaintViews()
65 |
66 | NSLayoutConstraint.activate([
67 | stackView.centerXAnchor.constraint(equalTo: centerXAnchor),
68 | stackView.centerYAnchor.constraint(equalTo: centerYAnchor)
69 | ])
70 | }
71 |
72 | override func configureAppearance() {
73 | super.configureAppearance()
74 |
75 | layer.cornerRadius = 5
76 | layer.masksToBounds = true
77 | }
78 | }
79 |
80 |
--------------------------------------------------------------------------------
/WorkoutApp/Controllers/Base/Views/WABarsView/Views/WABarView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WABarView.swift
3 | // WorkoutApp
4 | //
5 | // Created by Viktor Prikolota on 08.10.2022.
6 | //
7 |
8 | import UIKit
9 |
10 | extension WABarView {
11 | struct Data {
12 | let value: String
13 | let heightMultiplier: Double
14 | let title: String
15 | }
16 | }
17 |
18 | final class WABarView: WABaseView {
19 |
20 | private let heightMultiplier: Double
21 |
22 | private let valueLabel: UILabel = {
23 | let label = UILabel()
24 | label.font = R.Fonts.helvelticaRegular(with: 13)
25 | label.textColor = R.Colors.active
26 | return label
27 | }()
28 |
29 | private let barView: UIView = {
30 | let view = UIView()
31 | view.backgroundColor = R.Colors.active
32 | view.layer.cornerRadius = 2.5
33 | return view
34 | }()
35 |
36 | private let titleLabel: UILabel = {
37 | let label = UILabel()
38 | label.font = R.Fonts.helvelticaRegular(with: 9)
39 | label.textColor = R.Colors.inactive
40 | return label
41 | }()
42 |
43 | init(data: Data) {
44 | self.heightMultiplier = data.heightMultiplier
45 | super.init(frame: .zero)
46 |
47 | valueLabel.text = data.value
48 | titleLabel.text = data.title.uppercased()
49 | }
50 |
51 | required init?(coder: NSCoder) {
52 | self.heightMultiplier = 0
53 | super.init(frame: .zero)
54 | }
55 | }
56 |
57 | extension WABarView {
58 | override func setupViews() {
59 | super.setupViews()
60 |
61 | setupView(valueLabel)
62 | setupView(barView)
63 | setupView(titleLabel)
64 | }
65 |
66 | override func constaintViews() {
67 | super.constaintViews()
68 |
69 | NSLayoutConstraint.activate([
70 | valueLabel.centerXAnchor.constraint(equalTo: centerXAnchor),
71 | valueLabel.heightAnchor.constraint(equalToConstant: 10),
72 |
73 | barView.topAnchor.constraint(equalTo: valueLabel.bottomAnchor, constant: 7),
74 | barView.centerXAnchor.constraint(equalTo: centerXAnchor),
75 | barView.widthAnchor.constraint(equalToConstant: 17),
76 | barView.heightAnchor.constraint(equalTo: heightAnchor, multiplier: heightMultiplier * 0.8),
77 |
78 | titleLabel.topAnchor.constraint(equalTo: barView.bottomAnchor, constant: 10),
79 | titleLabel.centerXAnchor.constraint(equalTo: centerXAnchor),
80 | titleLabel.bottomAnchor.constraint(equalTo: bottomAnchor),
81 | titleLabel.heightAnchor.constraint(equalToConstant: 10)
82 | ])
83 | }
84 |
85 | override func configureAppearance() {
86 | super.configureAppearance()
87 |
88 | backgroundColor = .clear
89 | }
90 | }
91 |
92 |
--------------------------------------------------------------------------------
/WorkoutApp/Controllers/Base/Views/WAButton.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WAButton.swift
3 | // WorkoutApp
4 | //
5 | // Created by Viktor Prikolota on 24.05.2022.
6 | //
7 |
8 | import UIKit
9 |
10 | public enum WAButtonType {
11 | case primary
12 | case secondary
13 | }
14 |
15 | final class WAButton: UIButton {
16 |
17 | private var type: WAButtonType = .primary
18 |
19 | private let lable: UILabel = {
20 | let lable = UILabel()
21 | lable.textAlignment = .center
22 | return lable
23 | }()
24 |
25 | private let iconView: UIImageView = {
26 | let view = UIImageView()
27 | view.image = R.Images.Common.downArrow?.withRenderingMode(.alwaysTemplate)
28 | return view
29 | }()
30 |
31 | init(with type: WAButtonType) {
32 | super.init(frame: .zero)
33 | self.type = type
34 |
35 | setupViews()
36 | constaintViews()
37 | configureAppearance()
38 | }
39 |
40 | required init?(coder: NSCoder) {
41 | super.init(frame: .zero)
42 |
43 | setupViews()
44 | constaintViews()
45 | configureAppearance()
46 | }
47 |
48 | func setTitle(_ title: String?) {
49 | lable.text = title
50 | }
51 | }
52 |
53 | private extension WAButton {
54 |
55 | func setupViews() {
56 | setupView(lable)
57 | setupView(iconView)
58 | }
59 |
60 | func constaintViews() {
61 | var horisontalOffset: CGFloat {
62 | switch type {
63 | case .primary: return 0
64 | case .secondary: return 10
65 | }
66 | }
67 |
68 | NSLayoutConstraint.activate([
69 | iconView.centerYAnchor.constraint(equalTo: centerYAnchor),
70 | iconView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -horisontalOffset),
71 | iconView.heightAnchor.constraint(equalToConstant: 5),
72 | iconView.widthAnchor.constraint(equalToConstant: 10),
73 |
74 | lable.centerYAnchor.constraint(equalTo: centerYAnchor),
75 | lable.trailingAnchor.constraint(equalTo: iconView.leadingAnchor, constant: -10),
76 | lable.leadingAnchor.constraint(equalTo: leadingAnchor, constant: horisontalOffset * 2)
77 | ])
78 | }
79 |
80 | func configureAppearance() {
81 | switch type {
82 | case .primary:
83 | lable.textColor = R.Colors.inactive
84 | lable.font = R.Fonts.helvelticaRegular(with: 13)
85 | iconView.tintColor = R.Colors.inactive
86 |
87 | case .secondary:
88 | backgroundColor = R.Colors.secondary
89 | layer.cornerRadius = 14
90 | lable.textColor = R.Colors.active
91 | lable.font = R.Fonts.helvelticaRegular(with: 15)
92 | iconView.tintColor = R.Colors.active
93 | }
94 |
95 | makeSystem(self)
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/WorkoutApp/Controllers/Base/Views/WABaseInfoView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WABaseInfoView.swift
3 | // WorkoutApp
4 | //
5 | // Created by Viktor Prikolota on 04.06.2022.
6 | //
7 |
8 | import UIKit
9 |
10 | class WABaseInfoView: WABaseView {
11 |
12 | private let titleLabel: UILabel = {
13 | let label = UILabel()
14 | label.font = R.Fonts.helvelticaRegular(with: 13)
15 | label.textColor = R.Colors.inactive
16 | return label
17 | }()
18 |
19 | private let button = WAButton(with: .primary)
20 |
21 | let contentView: UIView = {
22 | let view = UIView()
23 | view.backgroundColor = .white
24 | view.layer.borderColor = R.Colors.separator.cgColor
25 | view.layer.borderWidth = 1
26 | view.layer.cornerRadius = 5
27 | return view
28 | }()
29 |
30 | init(with title: String? = nil, buttonTitle: String? = nil) {
31 | titleLabel.text = title?.uppercased()
32 | titleLabel.textAlignment = buttonTitle == nil ? .center : .left
33 |
34 | button.setTitle(buttonTitle?.uppercased())
35 | button.isHidden = buttonTitle == nil ? true : false
36 |
37 | super.init(frame: .zero)
38 | }
39 |
40 | required init?(coder: NSCoder) {
41 | super.init(frame: .zero)
42 | }
43 |
44 | func addButtonTarget(target: Any?, action: Selector) {
45 | button.addTarget(action, action: action, for: .touchUpInside)
46 | }
47 | }
48 |
49 | extension WABaseInfoView {
50 | override func setupViews() {
51 | super.setupViews()
52 |
53 | setupView(titleLabel)
54 | setupView(button)
55 | setupView(contentView)
56 | }
57 |
58 | override func constaintViews() {
59 | super.constaintViews()
60 |
61 | let contentTopAnchor: NSLayoutAnchor = titleLabel.text == nil ? topAnchor : titleLabel.bottomAnchor
62 | let contentTopOffset: CGFloat = titleLabel.text == nil ? 0 : 10
63 |
64 | NSLayoutConstraint.activate([
65 | titleLabel.leadingAnchor.constraint(equalTo: leadingAnchor),
66 | titleLabel.topAnchor.constraint(equalTo: topAnchor),
67 | titleLabel.trailingAnchor.constraint(equalTo: trailingAnchor),
68 |
69 | button.trailingAnchor.constraint(equalTo: trailingAnchor),
70 | button.centerYAnchor.constraint(equalTo: titleLabel.centerYAnchor),
71 | button.heightAnchor.constraint(equalToConstant: 28),
72 |
73 | contentView.topAnchor.constraint(equalTo: contentTopAnchor, constant: contentTopOffset),
74 | contentView.leadingAnchor.constraint(equalTo: leadingAnchor),
75 | contentView.trailingAnchor.constraint(equalTo: trailingAnchor),
76 | contentView.bottomAnchor.constraint(equalTo: bottomAnchor)
77 | ])
78 | }
79 |
80 | override func configureAppearance() {
81 | super.configureAppearance()
82 |
83 | backgroundColor = .clear
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/WorkoutApp/Controllers/Overview/Views/NavBar/OverviewNavBar.swift:
--------------------------------------------------------------------------------
1 | //
2 | // OverviewNavBar.swift
3 | // WorkoutApp
4 | //
5 | // Created by Viktor Prikolota on 26.05.2022.
6 | //
7 |
8 | import UIKit
9 |
10 | final class OverviewNavBar: WABaseView {
11 |
12 | private let titleLabel: UILabel = {
13 | let lable = UILabel()
14 | lable.text = R.Strings.NavBar.overview
15 | lable.textColor = R.Colors.titleGray
16 | lable.font = R.Fonts.helvelticaRegular(with: 22)
17 | return lable
18 | }()
19 |
20 | private let allWorkoutsButton: WAButton = {
21 | let button = WAButton(with: .secondary)
22 | button.setTitle(R.Strings.Overview.allWorcoutsButton)
23 | return button
24 | }()
25 |
26 | private let addButton: UIButton = {
27 | let button = UIButton()
28 | button.setImage(R.Images.Common.add, for: .normal)
29 | return button
30 | }()
31 |
32 | private let weekView = WeekView()
33 |
34 | override func layoutSubviews() {
35 | super.layoutSubviews()
36 |
37 | addBottomBorder(with: R.Colors.separator, height: 1)
38 | }
39 |
40 | func addAllWorkoutsAction(_ action: Selector, with target: Any?) {
41 | allWorkoutsButton.addTarget(target, action: action, for: .touchUpInside)
42 | }
43 |
44 | func addAdditingAction(_ action: Selector, with target: Any?) {
45 | addButton.addTarget(target, action: action, for: .touchUpInside)
46 | }
47 | }
48 |
49 | extension OverviewNavBar {
50 | override func setupViews() {
51 | super.setupViews()
52 |
53 | setupView(titleLabel)
54 | setupView(allWorkoutsButton)
55 | setupView(addButton)
56 | setupView(weekView)
57 | }
58 |
59 | override func constaintViews() {
60 | super.constaintViews()
61 |
62 | NSLayoutConstraint.activate([
63 | addButton.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor, constant: 8),
64 | addButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -15),
65 | addButton.heightAnchor.constraint(equalToConstant: 28),
66 | addButton.widthAnchor.constraint(equalToConstant: 28),
67 |
68 | allWorkoutsButton.topAnchor.constraint(equalTo: addButton.topAnchor),
69 | allWorkoutsButton.trailingAnchor.constraint(equalTo: addButton.leadingAnchor, constant: -15),
70 | allWorkoutsButton.heightAnchor.constraint(equalToConstant: 28),
71 |
72 | titleLabel.centerYAnchor.constraint(equalTo: allWorkoutsButton.centerYAnchor),
73 | titleLabel.trailingAnchor.constraint(equalTo: allWorkoutsButton.leadingAnchor),
74 | titleLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 15),
75 |
76 | weekView.topAnchor.constraint(equalTo: addButton.bottomAnchor, constant: 15),
77 | weekView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 15),
78 | weekView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -15),
79 | weekView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -15),
80 | weekView.heightAnchor.constraint(equalToConstant: 47)
81 | ])
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/WorkoutApp/Controllers/Session/Views/Stats/Views/StatsItemView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StatsItemView.swift
3 | // WorkoutApp
4 | //
5 | // Created by Viktor Prikolota on 08.10.2022.
6 | //
7 |
8 | import UIKit
9 |
10 | enum StatsItem {
11 | case averagePace(value: String)
12 | case heartRate(value: String)
13 | case totalDistance(value: String)
14 | case totalSteps(value: String)
15 |
16 | var data: StatsItemView.ItemData {
17 | switch self {
18 | case .averagePace(let value):
19 | return .init(image: R.Images.Session.Stats.averagePace,
20 | value: value + " / km",
21 | title: R.Strings.Session.averagePace)
22 | case .heartRate(let value):
23 | return .init(image: R.Images.Session.Stats.heartRate,
24 | value: value + " bpm",
25 | title: R.Strings.Session.heartRate)
26 | case .totalDistance(let value):
27 | return .init(image: R.Images.Session.Stats.totalDistance,
28 | value: value + " km",
29 | title: R.Strings.Session.totalDistance)
30 | case .totalSteps(let value):
31 | return .init(image: R.Images.Session.Stats.totalSteps,
32 | value: value,
33 | title: R.Strings.Session.totalSteps)
34 | }
35 | }
36 | }
37 |
38 | final class StatsItemView: WABaseView {
39 | struct ItemData {
40 | let image: UIImage?
41 | let value: String
42 | let title: String
43 | }
44 |
45 | private let imageView = UIImageView()
46 |
47 | private let valueLabel: UILabel = {
48 | let label = UILabel()
49 | label.font = R.Fonts.helvelticaRegular(with: 17)
50 | label.textColor = R.Colors.titleGray
51 | return label
52 | }()
53 |
54 | private let titleLabel: UILabel = {
55 | let label = UILabel()
56 | label.font = R.Fonts.helvelticaRegular(with: 10)
57 | label.textColor = R.Colors.inactive
58 | return label
59 | }()
60 |
61 | private let stackView: UIStackView = {
62 | let view = UIStackView()
63 | view.axis = .vertical
64 | return view
65 | }()
66 |
67 | func configure(with item: StatsItem) {
68 | imageView.image = item.data.image
69 | valueLabel.text = item.data.value
70 | titleLabel.text = item.data.title.uppercased()
71 | }
72 | }
73 |
74 | extension StatsItemView {
75 | override func setupViews() {
76 | super.setupViews()
77 |
78 | setupView(imageView)
79 | setupView(stackView)
80 | stackView.addArrangedSubview(valueLabel)
81 | stackView.addArrangedSubview(titleLabel)
82 | }
83 |
84 | override func constaintViews() {
85 | super.constaintViews()
86 |
87 | NSLayoutConstraint.activate([
88 | imageView.centerYAnchor.constraint(equalTo: centerYAnchor),
89 | imageView.leadingAnchor.constraint(equalTo: leadingAnchor),
90 | imageView.widthAnchor.constraint(equalToConstant: 23),
91 |
92 | stackView.leadingAnchor.constraint(equalTo: imageView.trailingAnchor, constant: 15),
93 | stackView.topAnchor.constraint(equalTo: topAnchor),
94 | stackView.bottomAnchor.constraint(equalTo: bottomAnchor),
95 | stackView.trailingAnchor.constraint(equalTo: trailingAnchor)
96 | ])
97 | }
98 |
99 | override func configureAppearance() {
100 | super.configureAppearance()
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/WorkoutApp/Controllers/Overview/Views/CollectionView/Views/TrainingCellView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TrainingCellView.swift
3 | // WorkoutApp
4 | //
5 | // Created by Viktor Prikolota on 10.02.2023.
6 | //
7 |
8 | import UIKit
9 |
10 | enum CellRoundedType {
11 | case top, bottom, all, notRounded
12 | }
13 |
14 | final class TrainingCellView: UICollectionViewCell {
15 | static let id = "TrainingCellView"
16 |
17 | private let checkmarkView = UIImageView(image: R.Images.Overview.checkmarkNotDone)
18 |
19 | private let stackView: UIStackView = {
20 | let view = UIStackView()
21 | view.axis = .vertical
22 | view.spacing = 3
23 | return view
24 | }()
25 |
26 | private let title: UILabel = {
27 | let lable = UILabel()
28 | lable.font = R.Fonts.helvelticaRegular(with: 17)
29 | lable.textColor = R.Colors.titleGray
30 | return lable
31 | }()
32 |
33 | private let subtitle: UILabel = {
34 | let lable = UILabel()
35 | lable.font = R.Fonts.helvelticaRegular(with: 13)
36 | lable.textColor = R.Colors.inactive
37 | return lable
38 | }()
39 |
40 | private let rightArrowView = UIImageView(image: R.Images.Overview.rightArrow)
41 |
42 | override init(frame: CGRect) {
43 | super.init(frame: frame)
44 |
45 | setupViews()
46 | constaintViews()
47 | configureAppearance()
48 | }
49 |
50 | required init?(coder: NSCoder) {
51 | super.init(frame: .zero)
52 |
53 | setupViews()
54 | constaintViews()
55 | configureAppearance()
56 | }
57 |
58 | func configure(with title: String, subtitle: String, isDone: Bool, roundedType: CellRoundedType) {
59 | self.title.text = title
60 | self.subtitle.text = subtitle
61 |
62 | checkmarkView.image = isDone ? R.Images.Overview.checkmarkDone : R.Images.Overview.checkmarkNotDone
63 |
64 | switch roundedType {
65 | case .all: self.roundCorners([.allCorners], radius: 5)
66 | case .bottom: self.roundCorners([.bottomLeft, .bottomRight], radius: 5)
67 | case .top: self.roundCorners([.topLeft, .topRight], radius: 5)
68 | case .notRounded: self.roundCorners([.allCorners], radius: 0)
69 | }
70 | }
71 | }
72 |
73 | private extension TrainingCellView {
74 | func setupViews() {
75 | setupView(checkmarkView)
76 | setupView(stackView)
77 | stackView.addArrangedSubview(title)
78 | stackView.addArrangedSubview(subtitle)
79 | setupView(rightArrowView)
80 | }
81 |
82 | func constaintViews() {
83 | NSLayoutConstraint.activate([
84 | checkmarkView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 15),
85 | checkmarkView.centerYAnchor.constraint(equalTo: centerYAnchor),
86 | checkmarkView.heightAnchor.constraint(equalToConstant: 28),
87 | checkmarkView.widthAnchor.constraint(equalTo: checkmarkView.heightAnchor),
88 |
89 | stackView.leadingAnchor.constraint(equalTo: checkmarkView.trailingAnchor, constant: 15),
90 | stackView.centerYAnchor.constraint(equalTo: centerYAnchor),
91 | stackView.trailingAnchor.constraint(equalTo: rightArrowView.leadingAnchor, constant: -15),
92 |
93 | rightArrowView.centerYAnchor.constraint(equalTo: centerYAnchor),
94 | rightArrowView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -15),
95 | rightArrowView.heightAnchor.constraint(equalToConstant: 12),
96 | rightArrowView.widthAnchor.constraint(equalToConstant: 7),
97 | ])
98 | }
99 |
100 | func configureAppearance() {
101 | backgroundColor = .white
102 | }
103 | }
104 |
105 |
--------------------------------------------------------------------------------
/WorkoutApp/Controllers/Session/SessionController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SessionController.swift
3 | // WorkoutApp
4 | //
5 | // Created by Viktor Prikolota on 23.05.2022.
6 | //
7 |
8 | import UIKit
9 |
10 | final class SessionController: WABaseController {
11 | private let timerView = TimerView()
12 | private let statsView = StatsView(with: R.Strings.Session.workoutStats)
13 | private let stepsView = StepsView(with: R.Strings.Session.stepsCounter)
14 |
15 | private let timerDuration = 5.0
16 |
17 | override func navBarLeftButtonHandler() {
18 | if timerView.state == .isStopped {
19 | timerView.startTimer()
20 | } else {
21 | timerView.pauseTimer()
22 | }
23 |
24 | timerView.state = timerView.state == .isRuning ? .isStopped : .isRuning
25 | addNavBarButton(
26 | at: .left,
27 | with: timerView.state == .isRuning
28 | ? R.Strings.Session.navBarPause : R.Strings.Session.navBarStart
29 | )
30 | }
31 |
32 | override func navBarRightButtonHandler() {
33 | timerView.stopTimer()
34 | timerView.state = .isStopped
35 |
36 | addNavBarButton(at: .left, with: R.Strings.Session.navBarStart)
37 | }
38 | }
39 |
40 | extension SessionController {
41 | override func setupViews() {
42 | super.setupViews()
43 |
44 | view.setupView(timerView)
45 | view.setupView(statsView)
46 | view.setupView(stepsView)
47 | }
48 |
49 | override func constraintViews() {
50 | super.constraintViews()
51 |
52 | NSLayoutConstraint.activate([
53 | timerView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 15),
54 | timerView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 15),
55 | timerView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -15),
56 |
57 | statsView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 15),
58 | statsView.topAnchor.constraint(equalTo: timerView.bottomAnchor, constant: 10),
59 | statsView.trailingAnchor.constraint(equalTo: view.centerXAnchor, constant: -7.5),
60 |
61 | stepsView.leadingAnchor.constraint(equalTo: view.centerXAnchor, constant: 7.5),
62 | stepsView.topAnchor.constraint(equalTo: statsView.topAnchor),
63 | stepsView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -15),
64 | stepsView.heightAnchor.constraint(equalTo: statsView.heightAnchor),
65 | ])
66 | }
67 |
68 | override func configureAppearance() {
69 | super.configureAppearance()
70 |
71 | title = R.Strings.NavBar.session
72 | navigationController?.tabBarItem.title = R.Strings.TabBar.title(for: .session)
73 |
74 | addNavBarButton(at: .left, with: R.Strings.Session.navBarStart)
75 | addNavBarButton(at: .right, with: R.Strings.Session.navBarFinish)
76 |
77 | timerView.configure(with: timerDuration, progress: 0)
78 | timerView.callBack = { [weak self] in
79 | self?.navBarRightButtonHandler()
80 | }
81 |
82 | statsView.configure(with: [.heartRate(value: "155"),
83 | .averagePace(value: "8'20''"),
84 | .totalSteps(value: "7,682"),
85 | .totalDistance(value: "8.25")])
86 |
87 | stepsView.configure(with: [.init(value: "8k", heightMultiplier: 1, title: "2/14"),
88 | .init(value: "7k", heightMultiplier: 0.8, title: "2/15"),
89 | .init(value: "5k", heightMultiplier: 0.6, title: "2/16"),
90 | .init(value: "6k", heightMultiplier: 0.3, title: "2/17")])
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/WorkoutApp/Controllers/Progress/ProgressController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ProgressController.swift
3 | // WorkoutApp
4 | //
5 | // Created by Viktor Prikolota on 23.05.2022.
6 | //
7 |
8 | import UIKit
9 |
10 | final class ProgressController: WABaseController {
11 |
12 | private let dailyPerformanceView = DailyPerformanceView(with: R.Strings.Progress.dailyPerformance,
13 | buttonTitle: R.Strings.Progress.last7Days)
14 | private let monthlyPerformanceView = MonthlyPerformanceView(with: R.Strings.Progress.monthlyPerformance,
15 | buttonTitle: R.Strings.Progress.last10Months)
16 |
17 | override func viewDidLoad() {
18 | super.viewDidLoad()
19 | }
20 | }
21 |
22 | extension ProgressController {
23 | override func setupViews() {
24 | super.setupViews()
25 |
26 | view.setupView(dailyPerformanceView)
27 | view.setupView(monthlyPerformanceView)
28 | }
29 |
30 | override func constraintViews() {
31 | super.constraintViews()
32 |
33 | NSLayoutConstraint.activate([
34 | dailyPerformanceView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 15),
35 | dailyPerformanceView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 15),
36 | dailyPerformanceView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -15),
37 | dailyPerformanceView.heightAnchor.constraint(equalTo: dailyPerformanceView.widthAnchor, multiplier: 0.68),
38 |
39 | monthlyPerformanceView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 15),
40 | monthlyPerformanceView.topAnchor.constraint(equalTo: dailyPerformanceView.bottomAnchor, constant: 15),
41 | monthlyPerformanceView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -15),
42 | monthlyPerformanceView.heightAnchor.constraint(equalTo: monthlyPerformanceView.widthAnchor,
43 | multiplier: 1.06),
44 | ])
45 |
46 | }
47 |
48 | override func configureAppearance() {
49 | super.configureAppearance()
50 |
51 | title = R.Strings.NavBar.progress
52 | navigationController?.tabBarItem.title = R.Strings.TabBar.title(for: .progress)
53 |
54 | addNavBarButton(at: .left, with: R.Strings.Progress.navBarLeft)
55 | addNavBarButton(at: .right, with: R.Strings.Progress.navBarRight)
56 |
57 | dailyPerformanceView.configure(with: [.init(value: "1", heightMultiplier: 0.2, title: "Mon"),
58 | .init(value: "2", heightMultiplier: 0.4, title: "Teu"),
59 | .init(value: "3", heightMultiplier: 0.6, title: "Wen"),
60 | .init(value: "4", heightMultiplier: 0.8, title: "Thu"),
61 | .init(value: "5", heightMultiplier: 1, title: "Fri"),
62 | .init(value: "3", heightMultiplier: 0.6, title: "Sat"),
63 | .init(value: "2", heightMultiplier: 0.4, title: "Sun")])
64 |
65 | monthlyPerformanceView.configure(with: [.init(value: 45, title: "Mar"),
66 | .init(value: 55, title: "Apr"),
67 | .init(value: 60, title: "May"),
68 | .init(value: 65, title: "Jun"),
69 | .init(value: 70, title: "Jul"),
70 | .init(value: 80, title: "Aug"),
71 | .init(value: 65, title: "Sep"),
72 | .init(value: 45, title: "Oct"),
73 | .init(value: 30, title: "Nov"),
74 | .init(value: 15, title: "Dec")],
75 | topChartOffset: 10)
76 | }
77 | }
78 |
79 |
--------------------------------------------------------------------------------
/WorkoutApp/Helpers/Resouces.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Resouces.swift
3 | // WorkoutApp
4 | //
5 | // Created by Viktor Prikolota on 23.05.2022.
6 | //
7 |
8 | import UIKit
9 |
10 | enum R {
11 | enum Colors {
12 | static let active = UIColor(hexString: "#437BFE")
13 | static let inactive = UIColor(hexString: "#929DA5")
14 |
15 | static let background = UIColor(hexString: "#F8F9F9")
16 | static let separator = UIColor(hexString: "#E8ECEF")
17 | static let secondary = UIColor(hexString: "#F0F3FF")
18 |
19 | static let titleGray = UIColor(hexString: "#545C77")
20 | }
21 |
22 | enum Strings {
23 | enum TabBar {
24 | static func title(for tab: Tabs) -> String {
25 | switch tab {
26 | case .overview: return "Overview"
27 | case .session: return "Session"
28 | case .progress: return "Progress"
29 | case .settings: return "Settings"
30 | }
31 | }
32 | }
33 |
34 | enum NavBar {
35 | static let overview = "Today"
36 | static let session = "High Intensity Cardio"
37 | static let progress = "Workout Progress"
38 | static let settings = "Settings"
39 | }
40 |
41 | enum Overview {
42 | static let allWorcoutsButton = "All Workouts"
43 | }
44 |
45 | enum Session {
46 | static let navBarStart = "Start"
47 | static let navBarPause = "Pause"
48 | static let navBarFinish = "Finish"
49 |
50 | static let elapsedTime = "Elapsed Time"
51 | static let remainingTime = "Remaining Time"
52 | static let completed = "Completed"
53 | static let remaining = "Remaining"
54 |
55 | static let workoutStats = "Workout stats"
56 | static let averagePace = "Average pace"
57 | static let heartRate = "Heart rate"
58 | static let totalDistance = "Total distance"
59 | static let totalSteps = "Total steps"
60 |
61 | static let stepsCounter = "Steps Counter"
62 | }
63 |
64 | enum Progress {
65 | static let navBarLeft = "Export"
66 | static let navBarRight = "Details"
67 |
68 | static let dailyPerformance = "Daily performance"
69 | static let last7Days = "Last 7 days"
70 | static let monthlyPerformance = "Monthly performance"
71 | static let last10Months = "Last 10 months"
72 | }
73 |
74 | enum Settings {}
75 | }
76 |
77 | enum Images {
78 | enum TabBar {
79 | static func icon(for tab: Tabs) -> UIImage? {
80 | switch tab {
81 | case .overview: return UIImage(named: "overview_tab")
82 | case .session: return UIImage(named: "session_tab")
83 | case .progress: return UIImage(named: "progress_tab")
84 | case .settings: return UIImage(named: "settings_tab")
85 | }
86 | }
87 | }
88 |
89 | enum Common {
90 | static let downArrow = UIImage(named: "down_arrow")
91 | static let add = UIImage(named: "add_button")
92 | }
93 |
94 | enum Overview {
95 | static let checkmarkNotDone = UIImage(named: "checkmark_not_done")
96 | static let checkmarkDone = UIImage(named: "checkmark_done")
97 | static let rightArrow = UIImage(named: "right_arrow")
98 | }
99 |
100 | enum Session {
101 | enum Stats {
102 | static let averagePace = UIImage(named: "stats_averagePace")
103 | static let heartRate = UIImage(named: "stats_heartRate")
104 | static let totalDistance = UIImage(named: "stats_totalDistance")
105 | static let totalSteps = UIImage(named: "stats_totalSteps")
106 | }
107 | }
108 | }
109 |
110 | enum Fonts {
111 | static func helvelticaRegular(with size: CGFloat) -> UIFont {
112 | UIFont(name: "Helvetica", size: size) ?? UIFont()
113 | }
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/WorkoutApp/Controllers/Base/Views/WAChartsView/Views/ChartView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ChartView.swift
3 | // WorkoutApp
4 | //
5 | // Created by Viktor Prikolota on 20.12.2022.
6 | //
7 |
8 | import UIKit
9 |
10 | final class ChartView: WABaseView {
11 |
12 | private let yAxisSeparator: UIView = {
13 | let view = UIView()
14 | view.backgroundColor = R.Colors.separator
15 | return view
16 | }()
17 |
18 | private let xAxisSeparator: UIView = {
19 | let view = UIView()
20 | view.backgroundColor = R.Colors.separator
21 | return view
22 | }()
23 |
24 | func configure(with data: [WAChartsView.Data], topChartOffset: Int) {
25 |
26 | layoutIfNeeded()
27 | drawDashLines()
28 | drawChart(with: data, topChartOffset: topChartOffset)
29 | }
30 | }
31 |
32 | extension ChartView {
33 | override func setupViews() {
34 | super.setupViews()
35 |
36 | setupView(yAxisSeparator)
37 | setupView(xAxisSeparator)
38 | }
39 |
40 | override func constaintViews() {
41 | super.constaintViews()
42 |
43 | NSLayoutConstraint.activate([
44 | yAxisSeparator.leadingAnchor.constraint(equalTo: leadingAnchor),
45 | yAxisSeparator.topAnchor.constraint(equalTo: topAnchor),
46 | yAxisSeparator.bottomAnchor.constraint(equalTo: bottomAnchor),
47 | yAxisSeparator.widthAnchor.constraint(equalToConstant: 1),
48 |
49 | xAxisSeparator.leadingAnchor.constraint(equalTo: leadingAnchor),
50 | xAxisSeparator.trailingAnchor.constraint(equalTo: trailingAnchor),
51 | xAxisSeparator.bottomAnchor.constraint(equalTo: bottomAnchor),
52 | xAxisSeparator.heightAnchor.constraint(equalToConstant: 1)
53 | ])
54 | }
55 |
56 | override func configureAppearance() {
57 | super.configureAppearance()
58 |
59 | backgroundColor = .clear
60 | }
61 | }
62 |
63 | private extension ChartView {
64 | func drawDashLines(with counts: Int = 9) {
65 | (0.. $1.value }).first?.value else { return }
88 | let valuePoints = data.enumerated().map { CGPoint(x: CGFloat($0), y: CGFloat($1.value)) }
89 | let chartHeight = bounds.height / CGFloat(maxValue + topChartOffset)
90 |
91 | let points = valuePoints.map {
92 | let x = bounds.width / CGFloat(valuePoints.count - 1) * $0.x
93 | let y = bounds.height - $0.y * chartHeight
94 | return CGPoint(x: x, y: y)
95 | }
96 |
97 | let chartPath = UIBezierPath()
98 | chartPath.move(to: points[0])
99 |
100 | points.forEach {
101 | chartPath.addLine(to: $0)
102 | drawChartDot(at: $0)
103 | }
104 |
105 | let chartLayer = CAShapeLayer()
106 | chartLayer.path = chartPath.cgPath
107 | chartLayer.fillColor = UIColor.clear.cgColor
108 | chartLayer.strokeColor = R.Colors.active.cgColor
109 | chartLayer.lineWidth = 3
110 | chartLayer.strokeEnd = 1
111 | chartLayer.lineJoin = .round
112 |
113 | layer.addSublayer(chartLayer)
114 | }
115 |
116 | func drawChartDot(at point: CGPoint) {
117 | let dotPath = UIBezierPath()
118 | dotPath.move(to: point)
119 | dotPath.addLine(to: point)
120 |
121 | let dotLayer = CAShapeLayer()
122 | dotLayer.path = dotPath.cgPath
123 | dotLayer.strokeColor = R.Colors.active.cgColor
124 | dotLayer.lineCap = .round
125 | dotLayer.lineWidth = 10
126 |
127 | layer.addSublayer(dotLayer)
128 | }
129 | }
130 |
131 |
--------------------------------------------------------------------------------
/WorkoutApp/Controllers/Session/Views/Timer/ProgressView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ProgressView.swift
3 | // WorkoutApp
4 | //
5 | // Created by Viktor Prikolota on 18.06.2022.
6 | //
7 |
8 | import UIKit
9 |
10 | extension TimerView {
11 | final class ProgressView: UIView {
12 | func drawProgress(with percent: CGFloat) {
13 | layer.sublayers?.removeAll()
14 |
15 | // MARK: - Circle
16 | let circleFrame = UIScreen.main.bounds.width - (15 + 40) * 2
17 | let radius = circleFrame / 2
18 | let center = CGPoint(x: radius, y: radius)
19 | let startAngle = -CGFloat.pi * 7 / 6
20 | let endAngle = CGFloat.pi * 1 / 6
21 |
22 | let circlePath = UIBezierPath(arcCenter: center,
23 | radius: radius,
24 | startAngle: startAngle,
25 | endAngle: endAngle,
26 | clockwise: true)
27 |
28 | let defaultCircleLayer = CAShapeLayer()
29 | defaultCircleLayer.path = circlePath.cgPath
30 | defaultCircleLayer.strokeColor = R.Colors.separator.cgColor
31 | defaultCircleLayer.lineWidth = 20
32 | defaultCircleLayer.strokeEnd = 1
33 | defaultCircleLayer.fillColor = UIColor.clear.cgColor
34 | defaultCircleLayer.lineCap = .round
35 |
36 | let circleLayer = CAShapeLayer()
37 | circleLayer.path = circlePath.cgPath
38 | circleLayer.strokeColor = R.Colors.active.cgColor
39 | circleLayer.lineWidth = 20
40 | circleLayer.strokeEnd = percent
41 | circleLayer.fillColor = UIColor.clear.cgColor
42 | circleLayer.lineCap = .round
43 |
44 | // MARK: - Dot
45 | let dotAngle = CGFloat.pi * (7 / 6 - (8 / 6 * percent))
46 | let dotPoint = CGPoint(x: cos(-dotAngle) * radius + center.x,
47 | y: sin(-dotAngle) * radius + center.y)
48 |
49 | let dotPath = UIBezierPath()
50 | dotPath.move(to: dotPoint)
51 | dotPath.addLine(to: dotPoint)
52 |
53 | let bigDotLayer = CAShapeLayer()
54 | bigDotLayer.path = dotPath.cgPath
55 | bigDotLayer.fillColor = UIColor.clear.cgColor
56 | bigDotLayer.strokeColor = R.Colors.active.cgColor
57 | bigDotLayer.lineCap = .round
58 | bigDotLayer.lineWidth = 20
59 |
60 | let dotLayer = CAShapeLayer()
61 | dotLayer.path = dotPath.cgPath
62 | dotLayer.fillColor = UIColor.clear.cgColor
63 | dotLayer.strokeColor = UIColor.white.cgColor
64 | dotLayer.lineCap = .round
65 | dotLayer.lineWidth = 8
66 |
67 | // MARK: - Bars
68 | let barsFrame = UIScreen.main.bounds.width - (15 + 40 + 25) * 2
69 | let barsRadius = barsFrame / 2
70 |
71 | let barsPath = UIBezierPath(arcCenter: center,
72 | radius: barsRadius,
73 | startAngle: startAngle,
74 | endAngle: endAngle,
75 | clockwise: true)
76 |
77 | let barsLayer = CAShapeLayer()
78 | barsLayer.path = barsPath.cgPath
79 | barsLayer.fillColor = UIColor.clear.cgColor
80 | barsLayer.strokeColor = UIColor.clear.cgColor
81 | barsLayer.lineWidth = 6
82 |
83 | let startBarRadius = barsRadius - barsLayer.lineWidth * 0.5
84 | let endBarRadius = startBarRadius + 6
85 |
86 | var angle: CGFloat = 7 / 6
87 | (1...9).forEach { _ in
88 | let barAngle = CGFloat.pi * angle
89 | let startBarPoint = CGPoint(
90 | x: cos(-barAngle) * startBarRadius + center.x,
91 | y: sin(-barAngle) * startBarRadius + center.y
92 | )
93 |
94 | let endBarPoint = CGPoint(
95 | x: cos(-barAngle) * endBarRadius + center.x,
96 | y: sin(-barAngle) * endBarRadius + center.y
97 | )
98 |
99 | let barPath = UIBezierPath()
100 | barPath.move(to: startBarPoint)
101 | barPath.addLine(to: endBarPoint)
102 |
103 | let barLayer = CAShapeLayer()
104 | barLayer.path = barPath.cgPath
105 | barLayer.fillColor = UIColor.clear.cgColor
106 | barLayer.strokeColor = angle >= (7 / 6 - (8 / 6 * percent))
107 | ? R.Colors.active.cgColor : R.Colors.separator.cgColor
108 | barLayer.lineCap = .round
109 | barLayer.lineWidth = 4
110 |
111 | barsLayer.addSublayer(barLayer)
112 |
113 | angle -= 1 / 6
114 | }
115 |
116 | layer.addSublayer(defaultCircleLayer)
117 | layer.addSublayer(circleLayer)
118 | layer.addSublayer(bigDotLayer)
119 | layer.addSublayer(dotLayer)
120 | layer.addSublayer(barsLayer)
121 | }
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/WorkoutApp/Controllers/Overview/OverviewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // OverviewController.swift
3 | // WorkoutApp
4 | //
5 | // Created by Viktor Prikolota on 23.05.2022.
6 | //
7 |
8 | import UIKit
9 |
10 | struct TraningData {
11 | struct Data {
12 | let title: String
13 | let subtitle: String
14 | let isDone: Bool
15 | }
16 |
17 | let date: Date
18 | let items: [Data]
19 | }
20 |
21 | class OverviewController: WABaseController {
22 |
23 | private let navBar = OverviewNavBar()
24 |
25 | private var dataSource: [TraningData] = []
26 |
27 | private let collectionView: UICollectionView = {
28 | let layout = UICollectionViewFlowLayout()
29 | layout.minimumLineSpacing = 0
30 |
31 | let view = UICollectionView(frame: .zero, collectionViewLayout: layout)
32 | view.showsVerticalScrollIndicator = false
33 | view.backgroundColor = .clear
34 |
35 | return view
36 | }()
37 | }
38 |
39 | extension OverviewController {
40 | override func setupViews() {
41 | super.setupViews()
42 |
43 | view.setupView(navBar)
44 | view.setupView(collectionView)
45 | }
46 |
47 | override func constraintViews() {
48 | super.constraintViews()
49 |
50 | NSLayoutConstraint.activate([
51 | navBar.topAnchor.constraint(equalTo: view.topAnchor),
52 | navBar.leadingAnchor.constraint(equalTo: view.leadingAnchor),
53 | navBar.trailingAnchor.constraint(equalTo: view.trailingAnchor),
54 |
55 | collectionView.topAnchor.constraint(equalTo: navBar.bottomAnchor),
56 | collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16),
57 | collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16),
58 | collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
59 | ])
60 | }
61 |
62 | override func configureAppearance() {
63 | super.configureAppearance()
64 |
65 | navigationController?.navigationBar.isHidden = true
66 |
67 | collectionView.register(TrainingCellView.self, forCellWithReuseIdentifier: TrainingCellView.id)
68 | collectionView.register(SectionHeaderView.self,
69 | forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader,
70 | withReuseIdentifier: SectionHeaderView.id)
71 |
72 | collectionView.delegate = self
73 | collectionView.dataSource = self
74 |
75 | dataSource = [
76 | .init(date: Date(),
77 | items: [
78 | .init(title: "Warm Up Cardio", subtitle: "Stair Climber • 10 minutes", isDone: true),
79 | .init(title: "High Intensity Cardio", subtitle: "Treadmill • 50 minutes", isDone: false),
80 | ]),
81 | .init(date: Date(),
82 | items: [
83 | .init(title: "Warm Up Cardio", subtitle: "Stair Climber • 10 minutes", isDone: false),
84 | .init(title: "Chest Workout", subtitle: "Bench Press • 3 sets • 10 reps", isDone: false),
85 | .init(title: "Tricep Workout", subtitle: "Overhead Extension • 5 sets • 8 reps", isDone: false),
86 | ]),
87 | .init(date: Date(),
88 | items: [
89 | .init(title: "Cardio Interval Workout", subtitle: "Treadmill • 60 minutes", isDone: false),
90 | ])
91 | ]
92 | collectionView.reloadData()
93 | }
94 | }
95 |
96 | // MARK: - UICollectionViewDataSource
97 | extension OverviewController: UICollectionViewDataSource {
98 | func numberOfSections(in collectionView: UICollectionView) -> Int {
99 | dataSource.count
100 | }
101 |
102 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
103 | dataSource[section].items.count
104 | }
105 |
106 | func collectionView(_ collectionView: UICollectionView,
107 | cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
108 | guard let cell = collectionView.dequeueReusableCell(
109 | withReuseIdentifier: TrainingCellView.id, for: indexPath
110 | ) as? TrainingCellView else { return UICollectionViewCell() }
111 |
112 | let item = dataSource[indexPath.section].items[indexPath.row]
113 |
114 | let roundedType: CellRoundedType
115 | if indexPath.row == 0 && indexPath.row == dataSource[indexPath.section].items.count - 1 {
116 | roundedType = .all
117 | } else if indexPath.row == 0 {
118 | roundedType = .top
119 | } else if indexPath.row == dataSource[indexPath.section].items.count - 1 {
120 | roundedType = .bottom
121 | } else {
122 | roundedType = .notRounded
123 | }
124 |
125 | cell.configure(with: item.title, subtitle: item.subtitle, isDone: item.isDone, roundedType: roundedType)
126 | return cell
127 | }
128 |
129 | func collectionView(_ collectionView: UICollectionView,
130 | viewForSupplementaryElementOfKind kind: String,
131 | at indexPath: IndexPath) -> UICollectionReusableView {
132 | guard let view = collectionView.dequeueReusableSupplementaryView(
133 | ofKind: kind, withReuseIdentifier: SectionHeaderView.id, for: indexPath
134 | ) as? SectionHeaderView else { return UICollectionReusableView() }
135 |
136 | view.configure(with: dataSource[indexPath.section].date)
137 | return view
138 | }
139 | }
140 |
141 | // MARK: - UICollectionViewDelegateFlowLayout
142 | extension OverviewController: UICollectionViewDelegateFlowLayout {
143 | func collectionView(_ collectionView: UICollectionView,
144 | layout collectionViewLayout: UICollectionViewLayout,
145 | sizeForItemAt indexPath: IndexPath) -> CGSize {
146 | CGSize(width: collectionView.frame.width, height: 70)
147 | }
148 |
149 | func collectionView(_ collectionView: UICollectionView,
150 | layout collectionViewLayout: UICollectionViewLayout,
151 | referenceSizeForHeaderInSection section: Int) -> CGSize {
152 | CGSize(width: collectionView.frame.width, height: 32)
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/WorkoutApp/Controllers/Session/Views/Timer/TimerView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TimerView.swift
3 | // WorkoutApp
4 | //
5 | // Created by Viktor Prikolota on 18.06.2022.
6 | //
7 |
8 | import UIKit
9 |
10 | enum TimerState {
11 | case isRuning
12 | case isPaused
13 | case isStopped
14 | }
15 |
16 | final class TimerView: WABaseInfoView {
17 |
18 | private let elapsedTimeLable: UILabel = {
19 | let lable = UILabel()
20 | lable.text = R.Strings.Session.elapsedTime
21 | lable.font = R.Fonts.helvelticaRegular(with: 14)
22 | lable.textColor = R.Colors.inactive
23 | lable.textAlignment = .center
24 | return lable
25 | }()
26 |
27 | private let elapsedTimeValueLable: UILabel = {
28 | let lable = UILabel()
29 | lable.font = R.Fonts.helvelticaRegular(with: 46)
30 | lable.textColor = R.Colors.titleGray
31 | lable.textAlignment = .center
32 | return lable
33 | }()
34 |
35 | private let remainingTimeLable: UILabel = {
36 | let lable = UILabel()
37 | lable.text = R.Strings.Session.remainingTime
38 | lable.font = R.Fonts.helvelticaRegular(with: 13)
39 | lable.textColor = R.Colors.inactive
40 | lable.textAlignment = .center
41 | return lable
42 | }()
43 |
44 | private let remainingTimeValueLable: UILabel = {
45 | let lable = UILabel()
46 | lable.font = R.Fonts.helvelticaRegular(with: 13)
47 | lable.textColor = R.Colors.titleGray
48 | lable.textAlignment = .center
49 | return lable
50 | }()
51 |
52 | private let timeStackView: UIStackView = {
53 | let view = UIStackView()
54 | view.axis = .vertical
55 | view.distribution = .fillProportionally
56 | view.spacing = 10
57 | return view
58 | }()
59 |
60 | private let bottomStackView: UIStackView = {
61 | let view = UIStackView()
62 | view.distribution = .fillProportionally
63 | view.spacing = 25
64 | return view
65 | }()
66 |
67 | private let completedPercentView = PercentView()
68 | private let remainigPercetnView = PercentView()
69 |
70 | private let bottomSeparatorView: UIView = {
71 | let view = UIView()
72 | view.backgroundColor = R.Colors.separator
73 | return view
74 | }()
75 |
76 | private let progressView = ProgressView()
77 |
78 | private var timer = Timer()
79 | private var timerProgress: CGFloat = 0
80 | private var timerDuration = 0.0
81 |
82 | var state: TimerState = .isStopped
83 | var callBack: (() -> Void)?
84 |
85 | func configure(with duration: Double, progress: Double) {
86 | timerDuration = duration
87 |
88 | let tempCurrentValue = progress > duration ? duration : progress
89 | let goalValueDevider = duration == 0 ? 1 : duration
90 | let percent = tempCurrentValue / goalValueDevider
91 | let roundedPercent = Int(round(percent * 100))
92 |
93 | elapsedTimeValueLable.text = getDisplayedString(from: Int(tempCurrentValue))
94 | remainingTimeValueLable.text = getDisplayedString(from: Int(duration) - Int(tempCurrentValue))
95 | completedPercentView.configure(with: R.Strings.Session.completed.uppercased(),
96 | andValue: roundedPercent)
97 | remainigPercetnView.configure(with: R.Strings.Session.remaining.uppercased(),
98 | andValue: 100 - roundedPercent)
99 | progressView.drawProgress(with: CGFloat(percent))
100 | }
101 |
102 | func startTimer() {
103 | timer.invalidate()
104 |
105 | timer = Timer.scheduledTimer(withTimeInterval: 0.01,
106 | repeats: true,
107 | block: { [weak self] timer in
108 | guard let self = self else { return }
109 | self.timerProgress += 0.01
110 |
111 | if self.timerProgress > self.timerDuration {
112 | self.timerProgress = self.timerDuration
113 | timer.invalidate()
114 | }
115 |
116 | self.configure(with: self.timerDuration, progress: self.timerProgress)
117 | })
118 | }
119 |
120 | func pauseTimer() {
121 | timer.invalidate()
122 | }
123 |
124 | func stopTimer() {
125 | guard self.timerProgress > 0 else { return }
126 | timer.invalidate()
127 |
128 | timer = Timer.scheduledTimer(withTimeInterval: 0.01,
129 | repeats: true,
130 | block: { [weak self] timer in
131 | guard let self = self else { return }
132 | self.timerProgress -= self.timerDuration * 0.02
133 |
134 | if self.timerProgress <= 0 {
135 | self.timerProgress = 0
136 | timer.invalidate()
137 | }
138 |
139 | self.configure(with: self.timerDuration, progress: self.timerProgress)
140 | })
141 | }
142 | }
143 |
144 | extension TimerView {
145 | override func setupViews() {
146 | super.setupViews()
147 |
148 | setupView(progressView)
149 | setupView(timeStackView)
150 | setupView(bottomStackView)
151 |
152 | [
153 | elapsedTimeLable,
154 | elapsedTimeValueLable,
155 | remainingTimeLable,
156 | remainingTimeValueLable
157 | ].forEach(timeStackView.addArrangedSubview)
158 |
159 | [
160 | completedPercentView,
161 | bottomSeparatorView,
162 | remainigPercetnView
163 | ].forEach(bottomStackView.addArrangedSubview)
164 | }
165 |
166 | override func constaintViews() {
167 | super.constaintViews()
168 |
169 | NSLayoutConstraint.activate([
170 | progressView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 40),
171 | progressView.topAnchor.constraint(equalTo: topAnchor, constant: 40),
172 | progressView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -40),
173 | progressView.heightAnchor.constraint(equalTo: progressView.widthAnchor),
174 | progressView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -40),
175 |
176 | timeStackView.centerYAnchor.constraint(equalTo: progressView.centerYAnchor),
177 | timeStackView.centerXAnchor.constraint(equalTo: progressView.centerXAnchor),
178 |
179 | bottomStackView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -28),
180 | bottomStackView.centerXAnchor.constraint(equalTo: centerXAnchor),
181 | bottomStackView.heightAnchor.constraint(equalToConstant: 35),
182 | bottomStackView.widthAnchor.constraint(equalToConstant: 175),
183 |
184 | bottomSeparatorView.widthAnchor.constraint(equalToConstant: 1)
185 | ])
186 | }
187 |
188 | override func configureAppearance() {
189 | super.configureAppearance()
190 | }
191 | }
192 |
193 | private extension TimerView {
194 | func getDisplayedString(from value: Int) -> String {
195 | let seconds = value % 60
196 | let minutes = (value / 60) % 60
197 | let hours = value / 3600
198 |
199 | let secondsStr = seconds < 10 ? "0\(seconds)" : "\(seconds)"
200 | let minutesStr = minutes < 10 ? "0\(minutes)" : "\(minutes)"
201 | let hoursStr = hours < 10 ? "0\(hours)" : "\(hours)"
202 |
203 | return hours == 0
204 | ? [minutesStr, secondsStr].joined(separator: ":")
205 | : [hoursStr, minutesStr, secondsStr].joined(separator: ":")
206 | }
207 | }
208 |
--------------------------------------------------------------------------------
/WorkoutApp.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 55;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 77040639285E17F9005ED76D /* TimerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77040638285E17F9005ED76D /* TimerView.swift */; };
11 | 7704063C285E1873005ED76D /* ProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7704063B285E1873005ED76D /* ProgressView.swift */; };
12 | 774B893629517830001ED31E /* ChartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 774B893529517830001ED31E /* ChartView.swift */; };
13 | 7766B53A284288C0000FF440 /* WeekView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7766B539284288C0000FF440 /* WeekView.swift */; };
14 | 7766B53C28428992000FF440 /* WeekdayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7766B53B28428992000FF440 /* WeekdayView.swift */; };
15 | 7766B53E28428BF9000FF440 /* Date + ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7766B53D28428BF9000FF440 /* Date + ext.swift */; };
16 | 776BE29928F1974000316C05 /* StatsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 776BE29828F1974000316C05 /* StatsView.swift */; };
17 | 776BE29C28F1978300316C05 /* StatsItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 776BE29B28F1978300316C05 /* StatsItemView.swift */; };
18 | 776BE2A628F1D8EF00316C05 /* StepsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 776BE2A528F1D8EF00316C05 /* StepsView.swift */; };
19 | 776BE2A928F1D97E00316C05 /* WABarsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 776BE2A828F1D97E00316C05 /* WABarsView.swift */; };
20 | 776BE2AC28F1D98B00316C05 /* WABarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 776BE2AB28F1D98B00316C05 /* WABarView.swift */; };
21 | 77709145283BEDAC00AD7841 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77709144283BEDAC00AD7841 /* AppDelegate.swift */; };
22 | 77709147283BEDAC00AD7841 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77709146283BEDAC00AD7841 /* SceneDelegate.swift */; };
23 | 77709149283BEDAC00AD7841 /* OverviewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77709148283BEDAC00AD7841 /* OverviewController.swift */; };
24 | 7770914E283BEDAD00AD7841 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7770914D283BEDAD00AD7841 /* Assets.xcassets */; };
25 | 77709151283BEDAD00AD7841 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7770914F283BEDAD00AD7841 /* LaunchScreen.storyboard */; };
26 | 7770915B283BEEEF00AD7841 /* TabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7770915A283BEEEF00AD7841 /* TabBarController.swift */; };
27 | 7770915F283BEF8D00AD7841 /* UIColor + ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7770915E283BEF8D00AD7841 /* UIColor + ext.swift */; };
28 | 77709162283BEFDE00AD7841 /* Resouces.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77709161283BEFDE00AD7841 /* Resouces.swift */; };
29 | 77709164283BF45500AD7841 /* NavBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77709163283BF45500AD7841 /* NavBarController.swift */; };
30 | 77709168283BF5A100AD7841 /* WABaseController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77709167283BF5A100AD7841 /* WABaseController.swift */; };
31 | 7770916A283BF70400AD7841 /* UIView + ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77709169283BF70400AD7841 /* UIView + ext.swift */; };
32 | 77709172283BF82600AD7841 /* SessionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77709171283BF82600AD7841 /* SessionController.swift */; };
33 | 77709174283BF83D00AD7841 /* ProgressController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77709173283BF83D00AD7841 /* ProgressController.swift */; };
34 | 77709176283BF84E00AD7841 /* SettingsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77709175283BF84E00AD7841 /* SettingsController.swift */; };
35 | 77854D06284BC4EE0095D8A5 /* WABaseInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77854D05284BC4EE0095D8A5 /* WABaseInfoView.swift */; };
36 | 77A322E6294056DC00905950 /* DailyPerformanceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A322E5294056DC00905950 /* DailyPerformanceView.swift */; };
37 | 77A322E92940655500905950 /* MonthlyPerformanceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A322E82940655500905950 /* MonthlyPerformanceView.swift */; };
38 | 77A322ED2940665C00905950 /* WAChartsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A322EC2940665C00905950 /* WAChartsView.swift */; };
39 | 77A322EF294067D000905950 /* XAxisView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A322EE294067D000905950 /* XAxisView.swift */; };
40 | 77A322F129406A0700905950 /* YAxisView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A322F029406A0700905950 /* YAxisView.swift */; };
41 | 77B44802283D4EC100ADDB6E /* WAButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77B44801283D4EC100ADDB6E /* WAButton.swift */; };
42 | 77B44804283FE24400ADDB6E /* OverviewNavBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77B44803283FE24400ADDB6E /* OverviewNavBar.swift */; };
43 | 77B44806283FE26E00ADDB6E /* WABaseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77B44805283FE26E00ADDB6E /* WABaseView.swift */; };
44 | 77C41E652995F135004ABAE6 /* SectionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77C41E642995F135004ABAE6 /* SectionHeaderView.swift */; };
45 | 77C41E672995F707004ABAE6 /* TrainingCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77C41E662995F707004ABAE6 /* TrainingCellView.swift */; };
46 | 77F052AF28C608E600839EF6 /* PercentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77F052AE28C608E600839EF6 /* PercentView.swift */; };
47 | /* End PBXBuildFile section */
48 |
49 | /* Begin PBXFileReference section */
50 | 77040638285E17F9005ED76D /* TimerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimerView.swift; sourceTree = ""; };
51 | 7704063B285E1873005ED76D /* ProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressView.swift; sourceTree = ""; };
52 | 774B893529517830001ED31E /* ChartView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChartView.swift; sourceTree = ""; };
53 | 7766B539284288C0000FF440 /* WeekView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeekView.swift; sourceTree = ""; };
54 | 7766B53B28428992000FF440 /* WeekdayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeekdayView.swift; sourceTree = ""; };
55 | 7766B53D28428BF9000FF440 /* Date + ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date + ext.swift"; sourceTree = ""; };
56 | 776BE29828F1974000316C05 /* StatsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsView.swift; sourceTree = ""; };
57 | 776BE29B28F1978300316C05 /* StatsItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsItemView.swift; sourceTree = ""; };
58 | 776BE2A528F1D8EF00316C05 /* StepsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StepsView.swift; sourceTree = ""; };
59 | 776BE2A828F1D97E00316C05 /* WABarsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WABarsView.swift; sourceTree = ""; };
60 | 776BE2AB28F1D98B00316C05 /* WABarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WABarView.swift; sourceTree = ""; };
61 | 77709141283BEDAC00AD7841 /* WorkoutApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WorkoutApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
62 | 77709144283BEDAC00AD7841 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
63 | 77709146283BEDAC00AD7841 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; };
64 | 77709148283BEDAC00AD7841 /* OverviewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverviewController.swift; sourceTree = ""; };
65 | 7770914D283BEDAD00AD7841 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
66 | 77709150283BEDAD00AD7841 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
67 | 77709152283BEDAD00AD7841 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
68 | 7770915A283BEEEF00AD7841 /* TabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarController.swift; sourceTree = ""; };
69 | 7770915E283BEF8D00AD7841 /* UIColor + ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor + ext.swift"; sourceTree = ""; };
70 | 77709161283BEFDE00AD7841 /* Resouces.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Resouces.swift; sourceTree = ""; };
71 | 77709163283BF45500AD7841 /* NavBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavBarController.swift; sourceTree = ""; };
72 | 77709167283BF5A100AD7841 /* WABaseController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WABaseController.swift; sourceTree = ""; };
73 | 77709169283BF70400AD7841 /* UIView + ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView + ext.swift"; sourceTree = ""; };
74 | 77709171283BF82600AD7841 /* SessionController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionController.swift; sourceTree = ""; };
75 | 77709173283BF83D00AD7841 /* ProgressController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressController.swift; sourceTree = ""; };
76 | 77709175283BF84E00AD7841 /* SettingsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsController.swift; sourceTree = ""; };
77 | 77854D05284BC4EE0095D8A5 /* WABaseInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WABaseInfoView.swift; sourceTree = ""; };
78 | 77A322E5294056DC00905950 /* DailyPerformanceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DailyPerformanceView.swift; sourceTree = ""; };
79 | 77A322E82940655500905950 /* MonthlyPerformanceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MonthlyPerformanceView.swift; sourceTree = ""; };
80 | 77A322EC2940665C00905950 /* WAChartsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WAChartsView.swift; sourceTree = ""; };
81 | 77A322EE294067D000905950 /* XAxisView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XAxisView.swift; sourceTree = ""; };
82 | 77A322F029406A0700905950 /* YAxisView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YAxisView.swift; sourceTree = ""; };
83 | 77B44801283D4EC100ADDB6E /* WAButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WAButton.swift; sourceTree = ""; };
84 | 77B44803283FE24400ADDB6E /* OverviewNavBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverviewNavBar.swift; sourceTree = ""; };
85 | 77B44805283FE26E00ADDB6E /* WABaseView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WABaseView.swift; sourceTree = ""; };
86 | 77C41E642995F135004ABAE6 /* SectionHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionHeaderView.swift; sourceTree = ""; };
87 | 77C41E662995F707004ABAE6 /* TrainingCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrainingCellView.swift; sourceTree = ""; };
88 | 77F052AE28C608E600839EF6 /* PercentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PercentView.swift; sourceTree = ""; };
89 | /* End PBXFileReference section */
90 |
91 | /* Begin PBXFrameworksBuildPhase section */
92 | 7770913E283BEDAC00AD7841 /* Frameworks */ = {
93 | isa = PBXFrameworksBuildPhase;
94 | buildActionMask = 2147483647;
95 | files = (
96 | );
97 | runOnlyForDeploymentPostprocessing = 0;
98 | };
99 | /* End PBXFrameworksBuildPhase section */
100 |
101 | /* Begin PBXGroup section */
102 | 77040637285E17E2005ED76D /* Views */ = {
103 | isa = PBXGroup;
104 | children = (
105 | 7704063A285E1866005ED76D /* Timer */,
106 | 776BE29628F1958800316C05 /* Stats */,
107 | 776BE29728F1958F00316C05 /* Steps */,
108 | );
109 | path = Views;
110 | sourceTree = "";
111 | };
112 | 7704063A285E1866005ED76D /* Timer */ = {
113 | isa = PBXGroup;
114 | children = (
115 | 77040638285E17F9005ED76D /* TimerView.swift */,
116 | 7704063B285E1873005ED76D /* ProgressView.swift */,
117 | 77F052AE28C608E600839EF6 /* PercentView.swift */,
118 | );
119 | path = Timer;
120 | sourceTree = "";
121 | };
122 | 7766B538284288A9000FF440 /* WeekView */ = {
123 | isa = PBXGroup;
124 | children = (
125 | 7766B539284288C0000FF440 /* WeekView.swift */,
126 | 7766B53B28428992000FF440 /* WeekdayView.swift */,
127 | );
128 | path = WeekView;
129 | sourceTree = "";
130 | };
131 | 7766B53F28429588000FF440 /* NavBar */ = {
132 | isa = PBXGroup;
133 | children = (
134 | 7766B538284288A9000FF440 /* WeekView */,
135 | 77B44803283FE24400ADDB6E /* OverviewNavBar.swift */,
136 | );
137 | path = NavBar;
138 | sourceTree = "";
139 | };
140 | 7766B540284295C4000FF440 /* Navigation */ = {
141 | isa = PBXGroup;
142 | children = (
143 | 7770915A283BEEEF00AD7841 /* TabBarController.swift */,
144 | 77709163283BF45500AD7841 /* NavBarController.swift */,
145 | );
146 | path = Navigation;
147 | sourceTree = "";
148 | };
149 | 776BE29628F1958800316C05 /* Stats */ = {
150 | isa = PBXGroup;
151 | children = (
152 | 776BE29A28F1976600316C05 /* Views */,
153 | 776BE29828F1974000316C05 /* StatsView.swift */,
154 | );
155 | path = Stats;
156 | sourceTree = "";
157 | };
158 | 776BE29728F1958F00316C05 /* Steps */ = {
159 | isa = PBXGroup;
160 | children = (
161 | 776BE2A528F1D8EF00316C05 /* StepsView.swift */,
162 | );
163 | path = Steps;
164 | sourceTree = "";
165 | };
166 | 776BE29A28F1976600316C05 /* Views */ = {
167 | isa = PBXGroup;
168 | children = (
169 | 776BE29B28F1978300316C05 /* StatsItemView.swift */,
170 | );
171 | path = Views;
172 | sourceTree = "";
173 | };
174 | 776BE2A728F1D95C00316C05 /* WABarsView */ = {
175 | isa = PBXGroup;
176 | children = (
177 | 776BE2AA28F1D98100316C05 /* Views */,
178 | 776BE2A828F1D97E00316C05 /* WABarsView.swift */,
179 | );
180 | path = WABarsView;
181 | sourceTree = "";
182 | };
183 | 776BE2AA28F1D98100316C05 /* Views */ = {
184 | isa = PBXGroup;
185 | children = (
186 | 776BE2AB28F1D98B00316C05 /* WABarView.swift */,
187 | );
188 | path = Views;
189 | sourceTree = "";
190 | };
191 | 77709138283BEDAC00AD7841 = {
192 | isa = PBXGroup;
193 | children = (
194 | 77709143283BEDAC00AD7841 /* WorkoutApp */,
195 | 77709142283BEDAC00AD7841 /* Products */,
196 | );
197 | sourceTree = "";
198 | };
199 | 77709142283BEDAC00AD7841 /* Products */ = {
200 | isa = PBXGroup;
201 | children = (
202 | 77709141283BEDAC00AD7841 /* WorkoutApp.app */,
203 | );
204 | name = Products;
205 | sourceTree = "";
206 | };
207 | 77709143283BEDAC00AD7841 /* WorkoutApp */ = {
208 | isa = PBXGroup;
209 | children = (
210 | 7770915D283BEF7A00AD7841 /* Helpers */,
211 | 77709158283BEDF000AD7841 /* Application */,
212 | 77709165283BF58C00AD7841 /* Controllers */,
213 | 77709159283BEE0000AD7841 /* SupportingFiles */,
214 | );
215 | path = WorkoutApp;
216 | sourceTree = "";
217 | };
218 | 77709158283BEDF000AD7841 /* Application */ = {
219 | isa = PBXGroup;
220 | children = (
221 | 77709144283BEDAC00AD7841 /* AppDelegate.swift */,
222 | 77709146283BEDAC00AD7841 /* SceneDelegate.swift */,
223 | );
224 | path = Application;
225 | sourceTree = "";
226 | };
227 | 77709159283BEE0000AD7841 /* SupportingFiles */ = {
228 | isa = PBXGroup;
229 | children = (
230 | 7770914D283BEDAD00AD7841 /* Assets.xcassets */,
231 | 7770914F283BEDAD00AD7841 /* LaunchScreen.storyboard */,
232 | 77709152283BEDAD00AD7841 /* Info.plist */,
233 | );
234 | path = SupportingFiles;
235 | sourceTree = "";
236 | };
237 | 7770915D283BEF7A00AD7841 /* Helpers */ = {
238 | isa = PBXGroup;
239 | children = (
240 | 77709160283BEF9500AD7841 /* Extensions */,
241 | 77709161283BEFDE00AD7841 /* Resouces.swift */,
242 | );
243 | path = Helpers;
244 | sourceTree = "";
245 | };
246 | 77709160283BEF9500AD7841 /* Extensions */ = {
247 | isa = PBXGroup;
248 | children = (
249 | 7770915E283BEF8D00AD7841 /* UIColor + ext.swift */,
250 | 77709169283BF70400AD7841 /* UIView + ext.swift */,
251 | 7766B53D28428BF9000FF440 /* Date + ext.swift */,
252 | );
253 | path = Extensions;
254 | sourceTree = "";
255 | };
256 | 77709165283BF58C00AD7841 /* Controllers */ = {
257 | isa = PBXGroup;
258 | children = (
259 | 7766B540284295C4000FF440 /* Navigation */,
260 | 7770916C283BF7FB00AD7841 /* Overview */,
261 | 7770916D283BF80300AD7841 /* Session */,
262 | 7770916E283BF80A00AD7841 /* Progress */,
263 | 7770916F283BF80F00AD7841 /* Settings */,
264 | 7770916B283BF7EF00AD7841 /* Base */,
265 | );
266 | path = Controllers;
267 | sourceTree = "";
268 | };
269 | 7770916B283BF7EF00AD7841 /* Base */ = {
270 | isa = PBXGroup;
271 | children = (
272 | 77854D04284BC4DD0095D8A5 /* Views */,
273 | 77709167283BF5A100AD7841 /* WABaseController.swift */,
274 | );
275 | path = Base;
276 | sourceTree = "";
277 | };
278 | 7770916C283BF7FB00AD7841 /* Overview */ = {
279 | isa = PBXGroup;
280 | children = (
281 | 77B44800283D4E9900ADDB6E /* Views */,
282 | 77709148283BEDAC00AD7841 /* OverviewController.swift */,
283 | );
284 | path = Overview;
285 | sourceTree = "";
286 | };
287 | 7770916D283BF80300AD7841 /* Session */ = {
288 | isa = PBXGroup;
289 | children = (
290 | 77040637285E17E2005ED76D /* Views */,
291 | 77709171283BF82600AD7841 /* SessionController.swift */,
292 | );
293 | path = Session;
294 | sourceTree = "";
295 | };
296 | 7770916E283BF80A00AD7841 /* Progress */ = {
297 | isa = PBXGroup;
298 | children = (
299 | 77A322E2294056B800905950 /* Views */,
300 | 77709173283BF83D00AD7841 /* ProgressController.swift */,
301 | );
302 | path = Progress;
303 | sourceTree = "";
304 | };
305 | 7770916F283BF80F00AD7841 /* Settings */ = {
306 | isa = PBXGroup;
307 | children = (
308 | 77709175283BF84E00AD7841 /* SettingsController.swift */,
309 | );
310 | path = Settings;
311 | sourceTree = "";
312 | };
313 | 77854D04284BC4DD0095D8A5 /* Views */ = {
314 | isa = PBXGroup;
315 | children = (
316 | 77A322EA2940663D00905950 /* WAChartsView */,
317 | 776BE2A728F1D95C00316C05 /* WABarsView */,
318 | 77B44805283FE26E00ADDB6E /* WABaseView.swift */,
319 | 77854D05284BC4EE0095D8A5 /* WABaseInfoView.swift */,
320 | 77B44801283D4EC100ADDB6E /* WAButton.swift */,
321 | );
322 | path = Views;
323 | sourceTree = "";
324 | };
325 | 77A322E2294056B800905950 /* Views */ = {
326 | isa = PBXGroup;
327 | children = (
328 | 77A322E4294056C900905950 /* DailyPerformance */,
329 | 77A322E72940653000905950 /* MonthlyPerformnaceView */,
330 | );
331 | path = Views;
332 | sourceTree = "";
333 | };
334 | 77A322E4294056C900905950 /* DailyPerformance */ = {
335 | isa = PBXGroup;
336 | children = (
337 | 77A322E5294056DC00905950 /* DailyPerformanceView.swift */,
338 | );
339 | path = DailyPerformance;
340 | sourceTree = "";
341 | };
342 | 77A322E72940653000905950 /* MonthlyPerformnaceView */ = {
343 | isa = PBXGroup;
344 | children = (
345 | 77A322E82940655500905950 /* MonthlyPerformanceView.swift */,
346 | );
347 | path = MonthlyPerformnaceView;
348 | sourceTree = "";
349 | };
350 | 77A322EA2940663D00905950 /* WAChartsView */ = {
351 | isa = PBXGroup;
352 | children = (
353 | 77A322EB2940664D00905950 /* Views */,
354 | 77A322EC2940665C00905950 /* WAChartsView.swift */,
355 | );
356 | path = WAChartsView;
357 | sourceTree = "";
358 | };
359 | 77A322EB2940664D00905950 /* Views */ = {
360 | isa = PBXGroup;
361 | children = (
362 | 77A322EE294067D000905950 /* XAxisView.swift */,
363 | 77A322F029406A0700905950 /* YAxisView.swift */,
364 | 774B893529517830001ED31E /* ChartView.swift */,
365 | );
366 | path = Views;
367 | sourceTree = "";
368 | };
369 | 77B44800283D4E9900ADDB6E /* Views */ = {
370 | isa = PBXGroup;
371 | children = (
372 | 77C41E622995F111004ABAE6 /* CollectionView */,
373 | 7766B53F28429588000FF440 /* NavBar */,
374 | );
375 | path = Views;
376 | sourceTree = "";
377 | };
378 | 77C41E622995F111004ABAE6 /* CollectionView */ = {
379 | isa = PBXGroup;
380 | children = (
381 | 77C41E632995F124004ABAE6 /* Views */,
382 | );
383 | path = CollectionView;
384 | sourceTree = "";
385 | };
386 | 77C41E632995F124004ABAE6 /* Views */ = {
387 | isa = PBXGroup;
388 | children = (
389 | 77C41E642995F135004ABAE6 /* SectionHeaderView.swift */,
390 | 77C41E662995F707004ABAE6 /* TrainingCellView.swift */,
391 | );
392 | path = Views;
393 | sourceTree = "";
394 | };
395 | /* End PBXGroup section */
396 |
397 | /* Begin PBXNativeTarget section */
398 | 77709140283BEDAC00AD7841 /* WorkoutApp */ = {
399 | isa = PBXNativeTarget;
400 | buildConfigurationList = 77709155283BEDAD00AD7841 /* Build configuration list for PBXNativeTarget "WorkoutApp" */;
401 | buildPhases = (
402 | 7770913D283BEDAC00AD7841 /* Sources */,
403 | 7770913E283BEDAC00AD7841 /* Frameworks */,
404 | 7770913F283BEDAC00AD7841 /* Resources */,
405 | );
406 | buildRules = (
407 | );
408 | dependencies = (
409 | );
410 | name = WorkoutApp;
411 | productName = WorkoutApp;
412 | productReference = 77709141283BEDAC00AD7841 /* WorkoutApp.app */;
413 | productType = "com.apple.product-type.application";
414 | };
415 | /* End PBXNativeTarget section */
416 |
417 | /* Begin PBXProject section */
418 | 77709139283BEDAC00AD7841 /* Project object */ = {
419 | isa = PBXProject;
420 | attributes = {
421 | BuildIndependentTargetsInParallel = 1;
422 | LastSwiftUpdateCheck = 1330;
423 | LastUpgradeCheck = 1330;
424 | TargetAttributes = {
425 | 77709140283BEDAC00AD7841 = {
426 | CreatedOnToolsVersion = 13.3.1;
427 | };
428 | };
429 | };
430 | buildConfigurationList = 7770913C283BEDAC00AD7841 /* Build configuration list for PBXProject "WorkoutApp" */;
431 | compatibilityVersion = "Xcode 13.0";
432 | developmentRegion = en;
433 | hasScannedForEncodings = 0;
434 | knownRegions = (
435 | en,
436 | Base,
437 | );
438 | mainGroup = 77709138283BEDAC00AD7841;
439 | productRefGroup = 77709142283BEDAC00AD7841 /* Products */;
440 | projectDirPath = "";
441 | projectRoot = "";
442 | targets = (
443 | 77709140283BEDAC00AD7841 /* WorkoutApp */,
444 | );
445 | };
446 | /* End PBXProject section */
447 |
448 | /* Begin PBXResourcesBuildPhase section */
449 | 7770913F283BEDAC00AD7841 /* Resources */ = {
450 | isa = PBXResourcesBuildPhase;
451 | buildActionMask = 2147483647;
452 | files = (
453 | 77709151283BEDAD00AD7841 /* LaunchScreen.storyboard in Resources */,
454 | 7770914E283BEDAD00AD7841 /* Assets.xcassets in Resources */,
455 | );
456 | runOnlyForDeploymentPostprocessing = 0;
457 | };
458 | /* End PBXResourcesBuildPhase section */
459 |
460 | /* Begin PBXSourcesBuildPhase section */
461 | 7770913D283BEDAC00AD7841 /* Sources */ = {
462 | isa = PBXSourcesBuildPhase;
463 | buildActionMask = 2147483647;
464 | files = (
465 | 7766B53A284288C0000FF440 /* WeekView.swift in Sources */,
466 | 7766B53E28428BF9000FF440 /* Date + ext.swift in Sources */,
467 | 77709176283BF84E00AD7841 /* SettingsController.swift in Sources */,
468 | 77709149283BEDAC00AD7841 /* OverviewController.swift in Sources */,
469 | 77B44804283FE24400ADDB6E /* OverviewNavBar.swift in Sources */,
470 | 77A322E92940655500905950 /* MonthlyPerformanceView.swift in Sources */,
471 | 77C41E672995F707004ABAE6 /* TrainingCellView.swift in Sources */,
472 | 7770916A283BF70400AD7841 /* UIView + ext.swift in Sources */,
473 | 7770915B283BEEEF00AD7841 /* TabBarController.swift in Sources */,
474 | 776BE2AC28F1D98B00316C05 /* WABarView.swift in Sources */,
475 | 77709145283BEDAC00AD7841 /* AppDelegate.swift in Sources */,
476 | 77A322E6294056DC00905950 /* DailyPerformanceView.swift in Sources */,
477 | 77A322ED2940665C00905950 /* WAChartsView.swift in Sources */,
478 | 77709172283BF82600AD7841 /* SessionController.swift in Sources */,
479 | 77854D06284BC4EE0095D8A5 /* WABaseInfoView.swift in Sources */,
480 | 77040639285E17F9005ED76D /* TimerView.swift in Sources */,
481 | 7704063C285E1873005ED76D /* ProgressView.swift in Sources */,
482 | 77B44802283D4EC100ADDB6E /* WAButton.swift in Sources */,
483 | 776BE2A628F1D8EF00316C05 /* StepsView.swift in Sources */,
484 | 77A322F129406A0700905950 /* YAxisView.swift in Sources */,
485 | 77A322EF294067D000905950 /* XAxisView.swift in Sources */,
486 | 77709174283BF83D00AD7841 /* ProgressController.swift in Sources */,
487 | 77709164283BF45500AD7841 /* NavBarController.swift in Sources */,
488 | 776BE29C28F1978300316C05 /* StatsItemView.swift in Sources */,
489 | 7770915F283BEF8D00AD7841 /* UIColor + ext.swift in Sources */,
490 | 77F052AF28C608E600839EF6 /* PercentView.swift in Sources */,
491 | 77709162283BEFDE00AD7841 /* Resouces.swift in Sources */,
492 | 77B44806283FE26E00ADDB6E /* WABaseView.swift in Sources */,
493 | 77C41E652995F135004ABAE6 /* SectionHeaderView.swift in Sources */,
494 | 7766B53C28428992000FF440 /* WeekdayView.swift in Sources */,
495 | 776BE2A928F1D97E00316C05 /* WABarsView.swift in Sources */,
496 | 774B893629517830001ED31E /* ChartView.swift in Sources */,
497 | 776BE29928F1974000316C05 /* StatsView.swift in Sources */,
498 | 77709168283BF5A100AD7841 /* WABaseController.swift in Sources */,
499 | 77709147283BEDAC00AD7841 /* SceneDelegate.swift in Sources */,
500 | );
501 | runOnlyForDeploymentPostprocessing = 0;
502 | };
503 | /* End PBXSourcesBuildPhase section */
504 |
505 | /* Begin PBXVariantGroup section */
506 | 7770914F283BEDAD00AD7841 /* LaunchScreen.storyboard */ = {
507 | isa = PBXVariantGroup;
508 | children = (
509 | 77709150283BEDAD00AD7841 /* Base */,
510 | );
511 | name = LaunchScreen.storyboard;
512 | sourceTree = "";
513 | };
514 | /* End PBXVariantGroup section */
515 |
516 | /* Begin XCBuildConfiguration section */
517 | 77709153283BEDAD00AD7841 /* Debug */ = {
518 | isa = XCBuildConfiguration;
519 | buildSettings = {
520 | ALWAYS_SEARCH_USER_PATHS = NO;
521 | CLANG_ANALYZER_NONNULL = YES;
522 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
523 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
524 | CLANG_ENABLE_MODULES = YES;
525 | CLANG_ENABLE_OBJC_ARC = YES;
526 | CLANG_ENABLE_OBJC_WEAK = YES;
527 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
528 | CLANG_WARN_BOOL_CONVERSION = YES;
529 | CLANG_WARN_COMMA = YES;
530 | CLANG_WARN_CONSTANT_CONVERSION = YES;
531 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
532 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
533 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
534 | CLANG_WARN_EMPTY_BODY = YES;
535 | CLANG_WARN_ENUM_CONVERSION = YES;
536 | CLANG_WARN_INFINITE_RECURSION = YES;
537 | CLANG_WARN_INT_CONVERSION = YES;
538 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
539 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
540 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
541 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
542 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
543 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
544 | CLANG_WARN_STRICT_PROTOTYPES = YES;
545 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
546 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
547 | CLANG_WARN_UNREACHABLE_CODE = YES;
548 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
549 | COPY_PHASE_STRIP = NO;
550 | DEBUG_INFORMATION_FORMAT = dwarf;
551 | ENABLE_STRICT_OBJC_MSGSEND = YES;
552 | ENABLE_TESTABILITY = YES;
553 | GCC_C_LANGUAGE_STANDARD = gnu11;
554 | GCC_DYNAMIC_NO_PIC = NO;
555 | GCC_NO_COMMON_BLOCKS = YES;
556 | GCC_OPTIMIZATION_LEVEL = 0;
557 | GCC_PREPROCESSOR_DEFINITIONS = (
558 | "DEBUG=1",
559 | "$(inherited)",
560 | );
561 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
562 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
563 | GCC_WARN_UNDECLARED_SELECTOR = YES;
564 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
565 | GCC_WARN_UNUSED_FUNCTION = YES;
566 | GCC_WARN_UNUSED_VARIABLE = YES;
567 | IPHONEOS_DEPLOYMENT_TARGET = 13.0;
568 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
569 | MTL_FAST_MATH = YES;
570 | ONLY_ACTIVE_ARCH = YES;
571 | SDKROOT = iphoneos;
572 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
573 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
574 | };
575 | name = Debug;
576 | };
577 | 77709154283BEDAD00AD7841 /* Release */ = {
578 | isa = XCBuildConfiguration;
579 | buildSettings = {
580 | ALWAYS_SEARCH_USER_PATHS = NO;
581 | CLANG_ANALYZER_NONNULL = YES;
582 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
583 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
584 | CLANG_ENABLE_MODULES = YES;
585 | CLANG_ENABLE_OBJC_ARC = YES;
586 | CLANG_ENABLE_OBJC_WEAK = YES;
587 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
588 | CLANG_WARN_BOOL_CONVERSION = YES;
589 | CLANG_WARN_COMMA = YES;
590 | CLANG_WARN_CONSTANT_CONVERSION = YES;
591 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
592 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
593 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
594 | CLANG_WARN_EMPTY_BODY = YES;
595 | CLANG_WARN_ENUM_CONVERSION = YES;
596 | CLANG_WARN_INFINITE_RECURSION = YES;
597 | CLANG_WARN_INT_CONVERSION = YES;
598 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
599 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
600 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
601 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
602 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
603 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
604 | CLANG_WARN_STRICT_PROTOTYPES = YES;
605 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
606 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
607 | CLANG_WARN_UNREACHABLE_CODE = YES;
608 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
609 | COPY_PHASE_STRIP = NO;
610 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
611 | ENABLE_NS_ASSERTIONS = NO;
612 | ENABLE_STRICT_OBJC_MSGSEND = YES;
613 | GCC_C_LANGUAGE_STANDARD = gnu11;
614 | GCC_NO_COMMON_BLOCKS = YES;
615 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
616 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
617 | GCC_WARN_UNDECLARED_SELECTOR = YES;
618 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
619 | GCC_WARN_UNUSED_FUNCTION = YES;
620 | GCC_WARN_UNUSED_VARIABLE = YES;
621 | IPHONEOS_DEPLOYMENT_TARGET = 13.0;
622 | MTL_ENABLE_DEBUG_INFO = NO;
623 | MTL_FAST_MATH = YES;
624 | SDKROOT = iphoneos;
625 | SWIFT_COMPILATION_MODE = wholemodule;
626 | SWIFT_OPTIMIZATION_LEVEL = "-O";
627 | VALIDATE_PRODUCT = YES;
628 | };
629 | name = Release;
630 | };
631 | 77709156283BEDAD00AD7841 /* Debug */ = {
632 | isa = XCBuildConfiguration;
633 | buildSettings = {
634 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
635 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
636 | CODE_SIGN_STYLE = Automatic;
637 | CURRENT_PROJECT_VERSION = 1;
638 | GENERATE_INFOPLIST_FILE = YES;
639 | INFOPLIST_FILE = WorkoutApp/SupportingFiles/Info.plist;
640 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
641 | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
642 | INFOPLIST_KEY_UIStatusBarHidden = NO;
643 | INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait;
644 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
645 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
646 | LD_RUNPATH_SEARCH_PATHS = (
647 | "$(inherited)",
648 | "@executable_path/Frameworks",
649 | );
650 | MARKETING_VERSION = 1.0;
651 | PRODUCT_BUNDLE_IDENTIFIER = "VP-P.WorkoutApp";
652 | PRODUCT_NAME = "$(TARGET_NAME)";
653 | SWIFT_EMIT_LOC_STRINGS = YES;
654 | SWIFT_VERSION = 5.0;
655 | TARGETED_DEVICE_FAMILY = 1;
656 | };
657 | name = Debug;
658 | };
659 | 77709157283BEDAD00AD7841 /* Release */ = {
660 | isa = XCBuildConfiguration;
661 | buildSettings = {
662 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
663 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
664 | CODE_SIGN_STYLE = Automatic;
665 | CURRENT_PROJECT_VERSION = 1;
666 | GENERATE_INFOPLIST_FILE = YES;
667 | INFOPLIST_FILE = WorkoutApp/SupportingFiles/Info.plist;
668 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
669 | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
670 | INFOPLIST_KEY_UIStatusBarHidden = NO;
671 | INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait;
672 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
673 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
674 | LD_RUNPATH_SEARCH_PATHS = (
675 | "$(inherited)",
676 | "@executable_path/Frameworks",
677 | );
678 | MARKETING_VERSION = 1.0;
679 | PRODUCT_BUNDLE_IDENTIFIER = "VP-P.WorkoutApp";
680 | PRODUCT_NAME = "$(TARGET_NAME)";
681 | SWIFT_EMIT_LOC_STRINGS = YES;
682 | SWIFT_VERSION = 5.0;
683 | TARGETED_DEVICE_FAMILY = 1;
684 | };
685 | name = Release;
686 | };
687 | /* End XCBuildConfiguration section */
688 |
689 | /* Begin XCConfigurationList section */
690 | 7770913C283BEDAC00AD7841 /* Build configuration list for PBXProject "WorkoutApp" */ = {
691 | isa = XCConfigurationList;
692 | buildConfigurations = (
693 | 77709153283BEDAD00AD7841 /* Debug */,
694 | 77709154283BEDAD00AD7841 /* Release */,
695 | );
696 | defaultConfigurationIsVisible = 0;
697 | defaultConfigurationName = Release;
698 | };
699 | 77709155283BEDAD00AD7841 /* Build configuration list for PBXNativeTarget "WorkoutApp" */ = {
700 | isa = XCConfigurationList;
701 | buildConfigurations = (
702 | 77709156283BEDAD00AD7841 /* Debug */,
703 | 77709157283BEDAD00AD7841 /* Release */,
704 | );
705 | defaultConfigurationIsVisible = 0;
706 | defaultConfigurationName = Release;
707 | };
708 | /* End XCConfigurationList section */
709 | };
710 | rootObject = 77709139283BEDAC00AD7841 /* Project object */;
711 | }
712 |
--------------------------------------------------------------------------------