From a21c96beb1b2df3169058a7c5b054d0c1227bf8b Mon Sep 17 00:00:00 2001 From: Grifon Date: Wed, 13 Dec 2023 14:06:06 +0300 Subject: [PATCH 01/24] =?UTF-8?q?1.=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20=D0=B0=D0=BD=D0=B3=D0=BB=D0=B8=D0=B9=D1=81=D0=BA=D1=83?= =?UTF-8?q?=D1=8E=20=D0=BB=D0=BE=D0=BA=D0=B0=D0=BB=D0=B8=D0=B7=D0=B0=D1=86?= =?UTF-8?q?=D0=B8=D1=8E=202.=D0=A0=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D0=BB=20=D0=B7=D0=B0=D0=BA=D1=80=D0=B5=D0=BF=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B8=20=D0=BE=D1=82=D0=BA=D1=80?= =?UTF-8?q?=D0=B5=D0=BF=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D1=82=D1=80=D0=B5?= =?UTF-8?q?=D0=BA=D0=B5=D1=80=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Tracker.xcodeproj/project.pbxproj | 124 +++++- .../xcschemes/xcschememanagement.plist | 8 + Tracker/Application/SceneDelegate.swift | 3 +- .../IconButtonCell.imageset/Contents.json | 3 + .../Add.imageset/Add tracker@1x.png | Bin .../Add.imageset/Add tracker@2x.png | Bin .../Add.imageset/Add tracker@3x.png | Bin .../Add.imageset/Contents.json | 4 + .../Contents.json | 0 .../Done.imageset/Contents.json | 3 + .../Done.imageset/Done.png | Bin .../Done.imageset/Done@2x.png | Bin .../Done.imageset/Done@3x.png | Bin .../IsPinned.imageset/Contents.json | 23 ++ .../IsPinned.imageset/ic 24x24.png | Bin 0 -> 260 bytes .../IsPinned.imageset/ic 24x24@2x.png | Bin 0 -> 359 bytes .../IsPinned.imageset/ic 24x24@3x.png | Bin 0 -> 508 bytes .../NothingFound.imageset/2@1x.png | Bin .../NothingFound.imageset/2@2x.png | Bin .../NothingFound.imageset/2@3x.png | Bin .../NothingFound.imageset/Contents.json | 0 .../Star.imageset/1@1x.png | Bin .../Star.imageset/1@2x.png | Bin .../Star.imageset/1@3x.png | Bin .../Star.imageset/Contents.json | 0 .../What do we do.imageset/Contents.json | 0 ...\270\320\262\320\260\321\202\321\214_.png" | Bin ...0\320\262\320\260\321\202\321\214_@2x.png" | Bin ...0\320\262\320\260\321\202\321\214_@3x.png" | Bin .../tabBarStatistic.imageset/Contents.json | 6 +- .../tabBarStatistic.imageset/Tab Bar Item.png | Bin 1414 -> 0 bytes .../Tab Bar Item@2x.png | Bin 2754 -> 0 bytes .../Tab Bar Item@3x.png | Bin 4158 -> 0 bytes .../tabBarStatistic.imageset/ic 28x28.png | Bin 0 -> 724 bytes .../tabBarStatistic.imageset/ic 28x28@2x.png | Bin 0 -> 1289 bytes .../tabBarStatistic.imageset/ic 28x28@3x.png | Bin 0 -> 1805 bytes .../tabBarTracker.imageset/Contents.json | 6 +- .../tabBarTracker.imageset/Tab Bar Item.png | Bin 1187 -> 0 bytes .../Tab Bar Item@2x.png | Bin 2487 -> 0 bytes .../Tab Bar Item@3x.png | Bin 3689 -> 0 bytes .../record.circle.fill.png | Bin 0 -> 560 bytes .../record.circle.fill@2x.png | Bin 0 -> 935 bytes .../record.circle.fill@3x.png | Bin 0 -> 1233 bytes .../Model.xcdatamodel/contents | 17 +- Tracker/Domain/DataProvider.swift | 189 --------- .../Domain/Store/PinnedCategoryStore.swift | 77 ++++ .../Domain/Store/TrackerCategoryStore.swift | 92 +++-- Tracker/Domain/Store/TrackerRecordStore.swift | 116 ++++-- Tracker/Domain/Store/TrackerStore.swift | 123 ++++-- Tracker/{Extantion => Extension +}/Date.swift | 0 .../{Extantion => Extension +}/String.swift | 4 + .../StringProtocol.swift | 0 .../{Extantion => Extension +}/UIColor.swift | 0 .../UITextField.swift | 0 Tracker/Models/Enum/ChoiceParametrs.swift | 4 +- Tracker/Models/Enum/Errors.swift | 39 ++ Tracker/Models/Enum/Header.swift | 2 +- Tracker/Models/Enum/NameEvent.swift | 4 +- Tracker/Models/Enum/WeekDay.swift | 28 +- Tracker/Models/PinnTrackerModel.swift | 21 + Tracker/Models/TrackerCategoryModel.swift | 1 + Tracker/Models/TrackerModel.swift | 7 +- Tracker/Models/UpdateTrackerModel.swift | 13 + .../OnboardingPageViewController.swift | 2 +- .../Onboarding/OnboardingViewController.swift | 2 +- .../PropertyWrappers/UserDefaultsBacked.swift | 1 - Tracker/Resources/en.lproj/InfoPlist.strings | 9 + .../Resources/en.lproj/Localizable.strings | 83 ++++ .../en.lproj/Localizable.stringsdict | 36 ++ .../en.lproj/LocalizableDict.stringsdict | 24 ++ Tracker/Resources/ru.lproj/InfoPlist.strings | 9 + .../Resources/ru.lproj/Localizable.strings | 82 ++++ .../ru.lproj/Localizable.stringsdict | 39 ++ .../ru.lproj/LocalizableDict.stringsdict | 30 ++ .../TabBarController/TabBarController.swift | 34 +- .../Cell/ColorCollectionViewCell.swift | 31 -- .../Cell/CreateTrackerTableViewCell.swift | 2 - .../EventSelectionViewController.swift | 2 +- .../Cell/TrackersCollectionViewCell.swift | 89 +++-- .../CreateCategoriesViewController.swift | 133 ++++--- .../CreateTrackerViewController.swift | 22 +- .../NewCategoriViewController.swift | 8 +- .../ScheduleViewController.swift | 5 +- .../TrackersViewController.swift | 374 +++++++++--------- Tracker/ViewModel/CategoriViewModel.swift | 49 --- Tracker/ViewModel/ViewModel.swift | 299 ++++++++++++++ Tracker/ViewModel/trackersCellViewModel.swift | 23 ++ Tracker/ru.lproj/LaunchScreen.strings | 1 + 88 files changed, 1598 insertions(+), 708 deletions(-) rename Tracker/Assets.xcassets/{IconTrackerImage => IconTracker}/Add.imageset/Add tracker@1x.png (100%) rename Tracker/Assets.xcassets/{IconTrackerImage => IconTracker}/Add.imageset/Add tracker@2x.png (100%) rename Tracker/Assets.xcassets/{IconTrackerImage => IconTracker}/Add.imageset/Add tracker@3x.png (100%) rename Tracker/Assets.xcassets/{IconTrackerImage => IconTracker}/Add.imageset/Contents.json (80%) rename Tracker/Assets.xcassets/{IconTrackerImage => IconTracker}/Contents.json (100%) rename Tracker/Assets.xcassets/{IconTrackerImage => IconTracker}/Done.imageset/Contents.json (88%) rename Tracker/Assets.xcassets/{IconTrackerImage => IconTracker}/Done.imageset/Done.png (100%) rename Tracker/Assets.xcassets/{IconTrackerImage => IconTracker}/Done.imageset/Done@2x.png (100%) rename Tracker/Assets.xcassets/{IconTrackerImage => IconTracker}/Done.imageset/Done@3x.png (100%) create mode 100644 Tracker/Assets.xcassets/IconTracker/IsPinned.imageset/Contents.json create mode 100644 Tracker/Assets.xcassets/IconTracker/IsPinned.imageset/ic 24x24.png create mode 100644 Tracker/Assets.xcassets/IconTracker/IsPinned.imageset/ic 24x24@2x.png create mode 100644 Tracker/Assets.xcassets/IconTracker/IsPinned.imageset/ic 24x24@3x.png rename Tracker/Assets.xcassets/{IconTrackerImage => IconTracker}/NothingFound.imageset/2@1x.png (100%) rename Tracker/Assets.xcassets/{IconTrackerImage => IconTracker}/NothingFound.imageset/2@2x.png (100%) rename Tracker/Assets.xcassets/{IconTrackerImage => IconTracker}/NothingFound.imageset/2@3x.png (100%) rename Tracker/Assets.xcassets/{IconTrackerImage => IconTracker}/NothingFound.imageset/Contents.json (100%) rename Tracker/Assets.xcassets/{IconTrackerImage => IconTracker}/Star.imageset/1@1x.png (100%) rename Tracker/Assets.xcassets/{IconTrackerImage => IconTracker}/Star.imageset/1@2x.png (100%) rename Tracker/Assets.xcassets/{IconTrackerImage => IconTracker}/Star.imageset/1@3x.png (100%) rename Tracker/Assets.xcassets/{IconTrackerImage => IconTracker}/Star.imageset/Contents.json (100%) rename Tracker/Assets.xcassets/{IconTrackerImage => IconTracker}/What do we do.imageset/Contents.json (100%) rename "Tracker/Assets.xcassets/IconTrackerImage/What do we do.imageset/\320\247\321\202\320\276 \320\261\321\203\320\264\320\265\320\274 \320\276\321\202\321\201\320\273\320\265\320\266\320\270\320\262\320\260\321\202\321\214_.png" => "Tracker/Assets.xcassets/IconTracker/What do we do.imageset/\320\247\321\202\320\276 \320\261\321\203\320\264\320\265\320\274 \320\276\321\202\321\201\320\273\320\265\320\266\320\270\320\262\320\260\321\202\321\214_.png" (100%) rename "Tracker/Assets.xcassets/IconTrackerImage/What do we do.imageset/\320\247\321\202\320\276 \320\261\321\203\320\264\320\265\320\274 \320\276\321\202\321\201\320\273\320\265\320\266\320\270\320\262\320\260\321\202\321\214_@2x.png" => "Tracker/Assets.xcassets/IconTracker/What do we do.imageset/\320\247\321\202\320\276 \320\261\321\203\320\264\320\265\320\274 \320\276\321\202\321\201\320\273\320\265\320\266\320\270\320\262\320\260\321\202\321\214_@2x.png" (100%) rename "Tracker/Assets.xcassets/IconTrackerImage/What do we do.imageset/\320\247\321\202\320\276 \320\261\321\203\320\264\320\265\320\274 \320\276\321\202\321\201\320\273\320\265\320\266\320\270\320\262\320\260\321\202\321\214_@3x.png" => "Tracker/Assets.xcassets/IconTracker/What do we do.imageset/\320\247\321\202\320\276 \320\261\321\203\320\264\320\265\320\274 \320\276\321\202\321\201\320\273\320\265\320\266\320\270\320\262\320\260\321\202\321\214_@3x.png" (100%) delete mode 100644 Tracker/Assets.xcassets/IconsTabBar/tabBarStatistic.imageset/Tab Bar Item.png delete mode 100644 Tracker/Assets.xcassets/IconsTabBar/tabBarStatistic.imageset/Tab Bar Item@2x.png delete mode 100644 Tracker/Assets.xcassets/IconsTabBar/tabBarStatistic.imageset/Tab Bar Item@3x.png create mode 100644 Tracker/Assets.xcassets/IconsTabBar/tabBarStatistic.imageset/ic 28x28.png create mode 100644 Tracker/Assets.xcassets/IconsTabBar/tabBarStatistic.imageset/ic 28x28@2x.png create mode 100644 Tracker/Assets.xcassets/IconsTabBar/tabBarStatistic.imageset/ic 28x28@3x.png delete mode 100644 Tracker/Assets.xcassets/IconsTabBar/tabBarTracker.imageset/Tab Bar Item.png delete mode 100644 Tracker/Assets.xcassets/IconsTabBar/tabBarTracker.imageset/Tab Bar Item@2x.png delete mode 100644 Tracker/Assets.xcassets/IconsTabBar/tabBarTracker.imageset/Tab Bar Item@3x.png create mode 100644 Tracker/Assets.xcassets/IconsTabBar/tabBarTracker.imageset/record.circle.fill.png create mode 100644 Tracker/Assets.xcassets/IconsTabBar/tabBarTracker.imageset/record.circle.fill@2x.png create mode 100644 Tracker/Assets.xcassets/IconsTabBar/tabBarTracker.imageset/record.circle.fill@3x.png delete mode 100644 Tracker/Domain/DataProvider.swift create mode 100644 Tracker/Domain/Store/PinnedCategoryStore.swift rename Tracker/{Extantion => Extension +}/Date.swift (100%) rename Tracker/{Extantion => Extension +}/String.swift (73%) rename Tracker/{Extantion => Extension +}/StringProtocol.swift (100%) rename Tracker/{Extantion => Extension +}/UIColor.swift (100%) rename Tracker/{Extantion => Extension +}/UITextField.swift (100%) create mode 100644 Tracker/Models/Enum/Errors.swift create mode 100644 Tracker/Models/PinnTrackerModel.swift create mode 100644 Tracker/Models/UpdateTrackerModel.swift create mode 100644 Tracker/Resources/en.lproj/InfoPlist.strings create mode 100644 Tracker/Resources/en.lproj/Localizable.strings create mode 100644 Tracker/Resources/en.lproj/Localizable.stringsdict create mode 100644 Tracker/Resources/en.lproj/LocalizableDict.stringsdict create mode 100644 Tracker/Resources/ru.lproj/InfoPlist.strings create mode 100644 Tracker/Resources/ru.lproj/Localizable.strings create mode 100644 Tracker/Resources/ru.lproj/Localizable.stringsdict create mode 100644 Tracker/Resources/ru.lproj/LocalizableDict.stringsdict delete mode 100644 Tracker/TabBarController/TrackersViews/Cell/ColorCollectionViewCell.swift rename Tracker/TabBarController/TrackersViews/{ViewControllers => Cell}/EventSelectionViewController.swift (98%) delete mode 100644 Tracker/ViewModel/CategoriViewModel.swift create mode 100644 Tracker/ViewModel/ViewModel.swift create mode 100644 Tracker/ViewModel/trackersCellViewModel.swift create mode 100644 Tracker/ru.lproj/LaunchScreen.strings diff --git a/Tracker.xcodeproj/project.pbxproj b/Tracker.xcodeproj/project.pbxproj index f766886..f4fef63 100644 --- a/Tracker.xcodeproj/project.pbxproj +++ b/Tracker.xcodeproj/project.pbxproj @@ -8,11 +8,14 @@ /* Begin PBXBuildFile section */ 1A14106A2ADD5185001D6DE6 /* EmojiColorCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A1410692ADD5185001D6DE6 /* EmojiColorCollectionViewCell.swift */; }; + 1A19A7462B1672A200DD415B /* UpdateTrackerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A19A7452B1672A200DD415B /* UpdateTrackerModel.swift */; }; 1A1E94B52AD952EF00FE9A0C /* CreateCategoriesTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A1E94B42AD952EF00FE9A0C /* CreateCategoriesTableViewCell.swift */; }; 1A21DD952ADE5A6A00583A01 /* DaysValueTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A21DD942ADE5A6A00583A01 /* DaysValueTransformer.swift */; }; + 1A272CE42B27922A00D39A44 /* PinnedCategoryStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A272CE32B27922A00D39A44 /* PinnedCategoryStore.swift */; }; 1A27E6B92AD9871400ED3DF3 /* StringProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A27E6B82AD9871400ED3DF3 /* StringProtocol.swift */; }; 1A2980BD2AC864CB007FE528 /* UIColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A2980BC2AC864CB007FE528 /* UIColor.swift */; }; 1A2F9B652AC82FBF00914B60 /* EventSelectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A2F9B642AC82FBF00914B60 /* EventSelectionViewController.swift */; }; + 1A323DA12B02B0EF00624B54 /* LocalizableDict.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 1A323DA32B02B0EF00624B54 /* LocalizableDict.stringsdict */; }; 1A32FF912ADC72880065E44C /* Header.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A32FF902ADC72880065E44C /* Header.swift */; }; 1A3B83AA2AD56323003C3897 /* NameEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A3B83A92AD56323003C3897 /* NameEvent.swift */; }; 1A3ED44C2ACDDE4A0006EBBB /* ScheduleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A3ED44B2ACDDE4A0006EBBB /* ScheduleViewController.swift */; }; @@ -25,6 +28,7 @@ 1A52EFAF2ADC194C0002BD4B /* Date.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A52EFAE2ADC194C0002BD4B /* Date.swift */; }; 1A598BB62AC6F352009B166D /* TabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A598BB52AC6F352009B166D /* TabBarController.swift */; }; 1A598BB82AC6FAC5009B166D /* TrackersViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A598BB72AC6FAC5009B166D /* TrackersViewController.swift */; }; + 1A66BB192B25D16500838F5F /* PinnTrackerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A66BB182B25D16500838F5F /* PinnTrackerModel.swift */; }; 1A84F3792AC87F4E001DD42F /* GeometricParams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A84F3782AC87F4E001DD42F /* GeometricParams.swift */; }; 1A8A576C2ACD5F4D00C060FA /* CreateTrackerTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A8A576B2ACD5F4D00C060FA /* CreateTrackerTableViewCell.swift */; }; 1A8A576F2ACD65C400C060FA /* ChoiceParametrs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A8A576E2ACD65C400C060FA /* ChoiceParametrs.swift */; }; @@ -38,19 +42,20 @@ 1A8ED5122AEFFADF00BA4EE4 /* OnboardingModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A8ED5112AEFFADF00BA4EE4 /* OnboardingModel.swift */; }; 1A959AF42AE24C44001C0EAE /* UIColorMarshalling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A959AF32AE24C44001C0EAE /* UIColorMarshalling.swift */; }; 1A9AC5AF2AF373F000A9213D /* Observable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A9AC5AE2AF373F000A9213D /* Observable.swift */; }; - 1A9AC5B42AF37AED00A9213D /* CategoriViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A9AC5B32AF37AED00A9213D /* CategoriViewModel.swift */; }; + 1A9AC5B42AF37AED00A9213D /* ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A9AC5B32AF37AED00A9213D /* ViewModel.swift */; }; 1A9E5C962AD2FF6100010C30 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A9E5C952AD2FF6100010C30 /* String.swift */; }; - 1AA3678A2AE5775D009DEEB8 /* DataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AA367892AE5775D009DEEB8 /* DataProvider.swift */; }; 1AA5B2372AD91D1800CB38A2 /* CreateCategoriesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AA5B2362AD91D1800CB38A2 /* CreateCategoriesViewController.swift */; }; 1AA5B2392AD9424C00CB38A2 /* NewCategoriViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AA5B2382AD9424C00CB38A2 /* NewCategoriViewController.swift */; }; + 1AA7BFC92B00E82E00C678FF /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1AA7BFCB2B00E82E00C678FF /* Localizable.strings */; }; 1AAC05C32ACC595800AA4B6B /* CreateTrackerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AAC05C22ACC595800AA4B6B /* CreateTrackerViewController.swift */; }; 1AAD15652AF14E220080EB72 /* UserDefaultsBacked.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AAD15642AF14E220080EB72 /* UserDefaultsBacked.swift */; }; + 1AB0E14F2B036CE8007183D8 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1AB0E1512B036CE8007183D8 /* InfoPlist.strings */; }; 1AB78AA32AC750A900904AB2 /* TrackersCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AB78AA22AC750A900904AB2 /* TrackersCollectionViewCell.swift */; }; 1ABABBF72AFC10AD007281E0 /* CreateCategoryCellModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ABABBF62AFC10AD007281E0 /* CreateCategoryCellModel.swift */; }; 1ABB3D2C2AC9AAB40037B601 /* HeaderReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ABB3D2B2AC9AAB40037B601 /* HeaderReusableView.swift */; }; - 1AD2776F2ACD3E0200F92219 /* ColorCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AD2776E2ACD3E0200F92219 /* ColorCollectionViewCell.swift */; }; 1AD277712ACD4B1800F92219 /* UITextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AD277702ACD4B1800F92219 /* UITextField.swift */; }; 1ADD3D742ACBDBB1005F639F /* TrackerCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ADD3D732ACBDBB1005F639F /* TrackerCollectionView.swift */; }; + 1AE27C2D2B0604A800C4A925 /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AE27C2C2B0604A800C4A925 /* Errors.swift */; }; 1AE989292ACAE13800392315 /* TrackerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AE989282ACAE13800392315 /* TrackerModel.swift */; }; 1AE9892D2ACAE76E00392315 /* WeekDay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AE9892C2ACAE76E00392315 /* WeekDay.swift */; }; 1AE989302ACAF14B00392315 /* TrackerCategoryModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AE9892F2ACAF14B00392315 /* TrackerCategoryModel.swift */; }; @@ -59,11 +64,15 @@ /* Begin PBXFileReference section */ 1A1410692ADD5185001D6DE6 /* EmojiColorCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiColorCollectionViewCell.swift; sourceTree = ""; }; + 1A19A7452B1672A200DD415B /* UpdateTrackerModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateTrackerModel.swift; sourceTree = ""; }; 1A1E94B42AD952EF00FE9A0C /* CreateCategoriesTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateCategoriesTableViewCell.swift; sourceTree = ""; }; 1A21DD942ADE5A6A00583A01 /* DaysValueTransformer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DaysValueTransformer.swift; sourceTree = ""; }; + 1A272CE32B27922A00D39A44 /* PinnedCategoryStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinnedCategoryStore.swift; sourceTree = ""; }; 1A27E6B82AD9871400ED3DF3 /* StringProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringProtocol.swift; sourceTree = ""; }; 1A2980BC2AC864CB007FE528 /* UIColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIColor.swift; sourceTree = ""; }; 1A2F9B642AC82FBF00914B60 /* EventSelectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventSelectionViewController.swift; sourceTree = ""; }; + 1A323DA22B02B0EF00624B54 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ru; path = ru.lproj/LocalizableDict.stringsdict; sourceTree = ""; }; + 1A323DA42B02B0F100624B54 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/LocalizableDict.stringsdict; sourceTree = ""; }; 1A32FF902ADC72880065E44C /* Header.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Header.swift; sourceTree = ""; }; 1A3B83A92AD56323003C3897 /* NameEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NameEvent.swift; sourceTree = ""; }; 1A3ED44B2ACDDE4A0006EBBB /* ScheduleViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScheduleViewController.swift; sourceTree = ""; }; @@ -76,6 +85,7 @@ 1A52EFAE2ADC194C0002BD4B /* Date.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Date.swift; sourceTree = ""; }; 1A598BB52AC6F352009B166D /* TabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarController.swift; sourceTree = ""; }; 1A598BB72AC6FAC5009B166D /* TrackersViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackersViewController.swift; sourceTree = ""; }; + 1A66BB182B25D16500838F5F /* PinnTrackerModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinnTrackerModel.swift; sourceTree = ""; }; 1A84F3782AC87F4E001DD42F /* GeometricParams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeometricParams.swift; sourceTree = ""; }; 1A8A576B2ACD5F4D00C060FA /* CreateTrackerTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateTrackerTableViewCell.swift; sourceTree = ""; }; 1A8A576E2ACD65C400C060FA /* ChoiceParametrs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChoiceParametrs.swift; sourceTree = ""; }; @@ -91,19 +101,23 @@ 1A8ED5112AEFFADF00BA4EE4 /* OnboardingModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingModel.swift; sourceTree = ""; }; 1A959AF32AE24C44001C0EAE /* UIColorMarshalling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIColorMarshalling.swift; sourceTree = ""; }; 1A9AC5AE2AF373F000A9213D /* Observable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Observable.swift; sourceTree = ""; }; - 1A9AC5B32AF37AED00A9213D /* CategoriViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CategoriViewModel.swift; sourceTree = ""; }; + 1A9AC5B32AF37AED00A9213D /* ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModel.swift; sourceTree = ""; }; 1A9E5C952AD2FF6100010C30 /* String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = ""; }; - 1AA367892AE5775D009DEEB8 /* DataProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataProvider.swift; sourceTree = ""; }; 1AA5B2362AD91D1800CB38A2 /* CreateCategoriesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateCategoriesViewController.swift; sourceTree = ""; }; 1AA5B2382AD9424C00CB38A2 /* NewCategoriViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewCategoriViewController.swift; sourceTree = ""; }; + 1AA7BFC82B00E77300C678FF /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/LaunchScreen.strings; sourceTree = ""; }; + 1AA7BFCA2B00E82E00C678FF /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; + 1AA7BFCC2B00E83400C678FF /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = ""; }; 1AAC05C22ACC595800AA4B6B /* CreateTrackerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateTrackerViewController.swift; sourceTree = ""; }; 1AAD15642AF14E220080EB72 /* UserDefaultsBacked.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultsBacked.swift; sourceTree = ""; }; + 1AB0E1502B036CE8007183D8 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + 1AB0E1522B036CEF007183D8 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/InfoPlist.strings; sourceTree = ""; }; 1AB78AA22AC750A900904AB2 /* TrackersCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackersCollectionViewCell.swift; sourceTree = ""; }; 1ABABBF62AFC10AD007281E0 /* CreateCategoryCellModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateCategoryCellModel.swift; sourceTree = ""; }; 1ABB3D2B2AC9AAB40037B601 /* HeaderReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderReusableView.swift; sourceTree = ""; }; - 1AD2776E2ACD3E0200F92219 /* ColorCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorCollectionViewCell.swift; sourceTree = ""; }; 1AD277702ACD4B1800F92219 /* UITextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITextField.swift; sourceTree = ""; }; 1ADD3D732ACBDBB1005F639F /* TrackerCollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerCollectionView.swift; sourceTree = ""; }; + 1AE27C2C2B0604A800C4A925 /* Errors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = ""; }; 1AE989282ACAE13800392315 /* TrackerModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerModel.swift; sourceTree = ""; }; 1AE9892C2ACAE76E00392315 /* WeekDay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeekDay.swift; sourceTree = ""; }; 1AE9892F2ACAF14B00392315 /* TrackerCategoryModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerCategoryModel.swift; sourceTree = ""; }; @@ -138,7 +152,7 @@ path = Helpers; sourceTree = ""; }; - 1A2980BB2AC86499007FE528 /* Extantion */ = { + 1A2980BB2AC86499007FE528 /* Extension + */ = { isa = PBXGroup; children = ( 1A2980BC2AC864CB007FE528 /* UIColor.swift */, @@ -147,13 +161,12 @@ 1A27E6B82AD9871400ED3DF3 /* StringProtocol.swift */, 1A52EFAE2ADC194C0002BD4B /* Date.swift */, ); - path = Extantion; + path = "Extension +"; sourceTree = ""; }; 1A2FC2482AED1E05000156FE /* Domain */ = { isa = PBXGroup; children = ( - 1AA367892AE5775D009DEEB8 /* DataProvider.swift */, 1A45A6762ADE8E770063F08C /* Store */, 1A21DD962ADE652800583A01 /* Helpers */, ); @@ -182,7 +195,6 @@ 1A598BB72AC6FAC5009B166D /* TrackersViewController.swift */, 1AAC05C22ACC595800AA4B6B /* CreateTrackerViewController.swift */, 1A3ED44B2ACDDE4A0006EBBB /* ScheduleViewController.swift */, - 1A2F9B642AC82FBF00914B60 /* EventSelectionViewController.swift */, 1AA5B2362AD91D1800CB38A2 /* CreateCategoriesViewController.swift */, 1AA5B2382AD9424C00CB38A2 /* NewCategoriViewController.swift */, ); @@ -195,6 +207,7 @@ 1A45A6772ADE8E9E0063F08C /* TrackerStore.swift */, 1A45A6792ADE8EDB0063F08C /* TrackerCategoryStore.swift */, 1A45A67B2ADE8F0F0063F08C /* TrackerRecordStore.swift */, + 1A272CE32B27922A00D39A44 /* PinnedCategoryStore.swift */, ); path = Store; sourceTree = ""; @@ -230,6 +243,8 @@ 1AE989312ACAF20500392315 /* TrackerRecordModel.swift */, 1A8ED5112AEFFADF00BA4EE4 /* OnboardingModel.swift */, 1ABABBF62AFC10AD007281E0 /* CreateCategoryCellModel.swift */, + 1A19A7452B1672A200DD415B /* UpdateTrackerModel.swift */, + 1A66BB182B25D16500838F5F /* PinnTrackerModel.swift */, ); path = Models; sourceTree = ""; @@ -249,6 +264,7 @@ 1A8A7FE32AC55D970038CB42 /* Products */, ); sourceTree = ""; + usesTabs = 0; }; 1A8A7FE32AC55D970038CB42 /* Products */ = { isa = PBXGroup; @@ -268,8 +284,9 @@ 1A598BB42AC6F0A9009B166D /* TabBarController */, 1A84F3772AC87F28001DD42F /* Models */, 1A3477CB2AD18D2100EDEAED /* Helpers */, - 1A2980BB2AC86499007FE528 /* Extantion */, + 1A2980BB2AC86499007FE528 /* Extension + */, 1AAD15662AF14E2D0080EB72 /* PropertyWrappers */, + 1AA7BFC52B00E41A00C678FF /* Resources */, 1AF783BD2ACD9D6C0002BB54 /* Application */, 1A8A7FEE2AC55D980038CB42 /* Assets.xcassets */, 1A8A7FF02AC55D980038CB42 /* LaunchScreen.storyboard */, @@ -290,20 +307,30 @@ 1A9AC5B52AF3854A00A9213D /* ViewModel */ = { isa = PBXGroup; children = ( - 1A9AC5B32AF37AED00A9213D /* CategoriViewModel.swift */, + 1A9AC5B32AF37AED00A9213D /* ViewModel.swift */, ); path = ViewModel; sourceTree = ""; }; + 1AA7BFC52B00E41A00C678FF /* Resources */ = { + isa = PBXGroup; + children = ( + 1AA7BFCB2B00E82E00C678FF /* Localizable.strings */, + 1A323DA32B02B0EF00624B54 /* LocalizableDict.stringsdict */, + 1AB0E1512B036CE8007183D8 /* InfoPlist.strings */, + ); + path = Resources; + sourceTree = ""; + }; 1AAC05C02ACC56A500AA4B6B /* Cell */ = { isa = PBXGroup; children = ( 1A8A576B2ACD5F4D00C060FA /* CreateTrackerTableViewCell.swift */, 1AB78AA22AC750A900904AB2 /* TrackersCollectionViewCell.swift */, - 1AD2776E2ACD3E0200F92219 /* ColorCollectionViewCell.swift */, 1A3ED4512ACDEA3E0006EBBB /* WeekDayTableViewCell.swift */, 1A1E94B42AD952EF00FE9A0C /* CreateCategoriesTableViewCell.swift */, 1A1410692ADD5185001D6DE6 /* EmojiColorCollectionViewCell.swift */, + 1A2F9B642AC82FBF00914B60 /* EventSelectionViewController.swift */, ); path = Cell; sourceTree = ""; @@ -332,6 +359,7 @@ 1A8A576E2ACD65C400C060FA /* ChoiceParametrs.swift */, 1A3B83A92AD56323003C3897 /* NameEvent.swift */, 1A32FF902ADC72880065E44C /* Header.swift */, + 1AE27C2C2B0604A800C4A925 /* Errors.swift */, ); path = Enum; sourceTree = ""; @@ -352,6 +380,7 @@ isa = PBXNativeTarget; buildConfigurationList = 1A8A7FF62AC55D980038CB42 /* Build configuration list for PBXNativeTarget "Tracker" */; buildPhases = ( + 1A0775FC2B0A30EA00155ED6 /* SwiftGenPlugin */, 1A8A7FDE2AC55D960038CB42 /* Sources */, 1A8A7FDF2AC55D960038CB42 /* Frameworks */, 1A8A7FE02AC55D960038CB42 /* Resources */, @@ -387,8 +416,11 @@ knownRegions = ( en, Base, + ru, ); mainGroup = 1A8A7FD92AC55D960038CB42; + packageReferences = ( + ); productRefGroup = 1A8A7FE32AC55D970038CB42 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -404,12 +436,36 @@ buildActionMask = 2147483647; files = ( 1A8A7FF22AC55D980038CB42 /* LaunchScreen.storyboard in Resources */, + 1A323DA12B02B0EF00624B54 /* LocalizableDict.stringsdict in Resources */, + 1AA7BFC92B00E82E00C678FF /* Localizable.strings in Resources */, 1A8A7FEF2AC55D980038CB42 /* Assets.xcassets in Resources */, + 1AB0E14F2B036CE8007183D8 /* InfoPlist.strings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ +/* Begin PBXShellScriptBuildPhase section */ + 1A0775FC2B0A30EA00155ED6 /* SwiftGenPlugin */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = SwiftGenPlugin; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"Build Phase - Begin SwiftGen\"\n./runSwiftGenPlugin.sh\necho \"Build Phase - End SwiftGen\"\n"; + }; +/* End PBXShellScriptBuildPhase section */ + /* Begin PBXSourcesBuildPhase section */ 1A8A7FDE2AC55D960038CB42 /* Sources */ = { isa = PBXSourcesBuildPhase; @@ -421,6 +477,7 @@ 1A8A576C2ACD5F4D00C060FA /* CreateTrackerTableViewCell.swift in Sources */, 1A598BB82AC6FAC5009B166D /* TrackersViewController.swift in Sources */, 1A8A7FEA2AC55D970038CB42 /* StatisticViewController.swift in Sources */, + 1A66BB192B25D16500838F5F /* PinnTrackerModel.swift in Sources */, 1A598BB62AC6F352009B166D /* TabBarController.swift in Sources */, 1ABB3D2C2AC9AAB40037B601 /* HeaderReusableView.swift in Sources */, 1A45A67C2ADE8F0F0063F08C /* TrackerRecordStore.swift in Sources */, @@ -428,12 +485,12 @@ 1A45A67A2ADE8EDB0063F08C /* TrackerCategoryStore.swift in Sources */, 1ABABBF72AFC10AD007281E0 /* CreateCategoryCellModel.swift in Sources */, 1A32FF912ADC72880065E44C /* Header.swift in Sources */, + 1A19A7462B1672A200DD415B /* UpdateTrackerModel.swift in Sources */, 1A8A7FE62AC55D970038CB42 /* AppDelegate.swift in Sources */, - 1AD2776F2ACD3E0200F92219 /* ColorCollectionViewCell.swift in Sources */, 1A3B83AA2AD56323003C3897 /* NameEvent.swift in Sources */, 1A45A6782ADE8E9E0063F08C /* TrackerStore.swift in Sources */, - 1AA3678A2AE5775D009DEEB8 /* DataProvider.swift in Sources */, 1A8ED5122AEFFADF00BA4EE4 /* OnboardingModel.swift in Sources */, + 1AE27C2D2B0604A800C4A925 /* Errors.swift in Sources */, 1AE989302ACAF14B00392315 /* TrackerCategoryModel.swift in Sources */, 1A3ED44C2ACDDE4A0006EBBB /* ScheduleViewController.swift in Sources */, 1A2980BD2AC864CB007FE528 /* UIColor.swift in Sources */, @@ -443,8 +500,9 @@ 1ADD3D742ACBDBB1005F639F /* TrackerCollectionView.swift in Sources */, 1A9E5C962AD2FF6100010C30 /* String.swift in Sources */, 1A45A6752ADE8BCC0063F08C /* Model.xcdatamodeld in Sources */, - 1A9AC5B42AF37AED00A9213D /* CategoriViewModel.swift in Sources */, + 1A9AC5B42AF37AED00A9213D /* ViewModel.swift in Sources */, 1A8ED50F2AEFEB5A00BA4EE4 /* OnboardingViewController.swift in Sources */, + 1A272CE42B27922A00D39A44 /* PinnedCategoryStore.swift in Sources */, 1AD277712ACD4B1800F92219 /* UITextField.swift in Sources */, 1A46558A2AD3D2B8006DE343 /* FormatDate.swift in Sources */, 1A52EFAF2ADC194C0002BD4B /* Date.swift in Sources */, @@ -467,14 +525,42 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ + 1A323DA32B02B0EF00624B54 /* LocalizableDict.stringsdict */ = { + isa = PBXVariantGroup; + children = ( + 1A323DA22B02B0EF00624B54 /* ru */, + 1A323DA42B02B0F100624B54 /* en */, + ); + name = LocalizableDict.stringsdict; + sourceTree = ""; + }; 1A8A7FF02AC55D980038CB42 /* LaunchScreen.storyboard */ = { isa = PBXVariantGroup; children = ( 1A8A7FF12AC55D980038CB42 /* Base */, + 1AA7BFC82B00E77300C678FF /* ru */, ); name = LaunchScreen.storyboard; sourceTree = ""; }; + 1AA7BFCB2B00E82E00C678FF /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + 1AA7BFCA2B00E82E00C678FF /* en */, + 1AA7BFCC2B00E83400C678FF /* ru */, + ); + name = Localizable.strings; + sourceTree = ""; + }; + 1AB0E1512B036CE8007183D8 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 1AB0E1502B036CE8007183D8 /* en */, + 1AB0E1522B036CEF007183D8 /* ru */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ @@ -482,6 +568,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; @@ -528,7 +615,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 16.4; + IPHONEOS_DEPLOYMENT_TARGET = 13.4; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -542,6 +629,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; @@ -582,7 +670,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 16.4; + IPHONEOS_DEPLOYMENT_TARGET = 13.4; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; diff --git a/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcschemes/xcschememanagement.plist b/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcschemes/xcschememanagement.plist index 99f961b..b2205bb 100644 --- a/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcschemes/xcschememanagement.plist @@ -10,5 +10,13 @@ 0 + SuppressBuildableAutocreation + + 1A8A7FE12AC55D960038CB42 + + primary + + + diff --git a/Tracker/Application/SceneDelegate.swift b/Tracker/Application/SceneDelegate.swift index 226b2e7..05594d4 100644 --- a/Tracker/Application/SceneDelegate.swift +++ b/Tracker/Application/SceneDelegate.swift @@ -22,7 +22,8 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { let onboardingVC = OnboardingPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal) let oneWisibleVc = OnboardingViewController() - let model = Onboarding(imageName: "onePage", textLable: "Отслеживайте только то, что хотите") + let textLable = NSLocalizedString("textLableOneVC", comment: "") + let model = Onboarding(imageName: "onePage", textLable: textLable) oneWisibleVc.config(model: model) onboardingVC.setViewControllers([oneWisibleVc], direction: .forward, animated: true) rootViewController = onboardingVC diff --git a/Tracker/Assets.xcassets/IconGreateTableView/IconButtonCell.imageset/Contents.json b/Tracker/Assets.xcassets/IconGreateTableView/IconButtonCell.imageset/Contents.json index 106ada9..649c0c6 100644 --- a/Tracker/Assets.xcassets/IconGreateTableView/IconButtonCell.imageset/Contents.json +++ b/Tracker/Assets.xcassets/IconGreateTableView/IconButtonCell.imageset/Contents.json @@ -3,16 +3,19 @@ { "filename" : "ic 24x24@1x.png", "idiom" : "universal", + "language-direction" : "left-to-right", "scale" : "1x" }, { "filename" : "ic 24x24@2x.png", "idiom" : "universal", + "language-direction" : "left-to-right", "scale" : "2x" }, { "filename" : "ic 24x24@3x.png", "idiom" : "universal", + "language-direction" : "left-to-right", "scale" : "3x" } ], diff --git a/Tracker/Assets.xcassets/IconTrackerImage/Add.imageset/Add tracker@1x.png b/Tracker/Assets.xcassets/IconTracker/Add.imageset/Add tracker@1x.png similarity index 100% rename from Tracker/Assets.xcassets/IconTrackerImage/Add.imageset/Add tracker@1x.png rename to Tracker/Assets.xcassets/IconTracker/Add.imageset/Add tracker@1x.png diff --git a/Tracker/Assets.xcassets/IconTrackerImage/Add.imageset/Add tracker@2x.png b/Tracker/Assets.xcassets/IconTracker/Add.imageset/Add tracker@2x.png similarity index 100% rename from Tracker/Assets.xcassets/IconTrackerImage/Add.imageset/Add tracker@2x.png rename to Tracker/Assets.xcassets/IconTracker/Add.imageset/Add tracker@2x.png diff --git a/Tracker/Assets.xcassets/IconTrackerImage/Add.imageset/Add tracker@3x.png b/Tracker/Assets.xcassets/IconTracker/Add.imageset/Add tracker@3x.png similarity index 100% rename from Tracker/Assets.xcassets/IconTrackerImage/Add.imageset/Add tracker@3x.png rename to Tracker/Assets.xcassets/IconTracker/Add.imageset/Add tracker@3x.png diff --git a/Tracker/Assets.xcassets/IconTrackerImage/Add.imageset/Contents.json b/Tracker/Assets.xcassets/IconTracker/Add.imageset/Contents.json similarity index 80% rename from Tracker/Assets.xcassets/IconTrackerImage/Add.imageset/Contents.json rename to Tracker/Assets.xcassets/IconTracker/Add.imageset/Contents.json index e8e3d7c..edab818 100644 --- a/Tracker/Assets.xcassets/IconTrackerImage/Add.imageset/Contents.json +++ b/Tracker/Assets.xcassets/IconTracker/Add.imageset/Contents.json @@ -19,5 +19,9 @@ "info" : { "author" : "xcode", "version" : 1 + }, + "properties" : { + "localizable" : true, + "template-rendering-intent" : "template" } } diff --git a/Tracker/Assets.xcassets/IconTrackerImage/Contents.json b/Tracker/Assets.xcassets/IconTracker/Contents.json similarity index 100% rename from Tracker/Assets.xcassets/IconTrackerImage/Contents.json rename to Tracker/Assets.xcassets/IconTracker/Contents.json diff --git a/Tracker/Assets.xcassets/IconTrackerImage/Done.imageset/Contents.json b/Tracker/Assets.xcassets/IconTracker/Done.imageset/Contents.json similarity index 88% rename from Tracker/Assets.xcassets/IconTrackerImage/Done.imageset/Contents.json rename to Tracker/Assets.xcassets/IconTracker/Done.imageset/Contents.json index 6a4f1a9..cbb678b 100644 --- a/Tracker/Assets.xcassets/IconTrackerImage/Done.imageset/Contents.json +++ b/Tracker/Assets.xcassets/IconTracker/Done.imageset/Contents.json @@ -19,5 +19,8 @@ "info" : { "author" : "xcode", "version" : 1 + }, + "properties" : { + "localizable" : true } } diff --git a/Tracker/Assets.xcassets/IconTrackerImage/Done.imageset/Done.png b/Tracker/Assets.xcassets/IconTracker/Done.imageset/Done.png similarity index 100% rename from Tracker/Assets.xcassets/IconTrackerImage/Done.imageset/Done.png rename to Tracker/Assets.xcassets/IconTracker/Done.imageset/Done.png diff --git a/Tracker/Assets.xcassets/IconTrackerImage/Done.imageset/Done@2x.png b/Tracker/Assets.xcassets/IconTracker/Done.imageset/Done@2x.png similarity index 100% rename from Tracker/Assets.xcassets/IconTrackerImage/Done.imageset/Done@2x.png rename to Tracker/Assets.xcassets/IconTracker/Done.imageset/Done@2x.png diff --git a/Tracker/Assets.xcassets/IconTrackerImage/Done.imageset/Done@3x.png b/Tracker/Assets.xcassets/IconTracker/Done.imageset/Done@3x.png similarity index 100% rename from Tracker/Assets.xcassets/IconTrackerImage/Done.imageset/Done@3x.png rename to Tracker/Assets.xcassets/IconTracker/Done.imageset/Done@3x.png diff --git a/Tracker/Assets.xcassets/IconTracker/IsPinned.imageset/Contents.json b/Tracker/Assets.xcassets/IconTracker/IsPinned.imageset/Contents.json new file mode 100644 index 0000000..ba081de --- /dev/null +++ b/Tracker/Assets.xcassets/IconTracker/IsPinned.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "ic 24x24.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "ic 24x24@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "ic 24x24@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tracker/Assets.xcassets/IconTracker/IsPinned.imageset/ic 24x24.png b/Tracker/Assets.xcassets/IconTracker/IsPinned.imageset/ic 24x24.png new file mode 100644 index 0000000000000000000000000000000000000000..b8b0ab2adce385813f41b83a0c917d01dcf66475 GIT binary patch literal 260 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjoCO|{#S9GG!XV7ZFl!D-1!HlL zyA#8@b22Z19F}xPUq=Rpjs4tz5?O)#nVv3=Ar*{oFJ0tybr4{^@cQZB%Zye5T|p&I zoK_2ktsGb%=Q(YXzrlI>`aBmivuEcb8Wbw#c1$T+*TBz{#dj$BM+(#04{p)l58H11 z<`~NCH>bdJ?OK+71~!jPWono=Pk*S{R#LRC_^Nvso3LJ=%A(c3?C%tKiv3n9o%U+)p#){_du6_pk3&6Ze$Yf-Li+8UM0RzO1`EVgt~T44$rjF6*2UngB#l BU77#@ literal 0 HcmV?d00001 diff --git a/Tracker/Assets.xcassets/IconTracker/IsPinned.imageset/ic 24x24@2x.png b/Tracker/Assets.xcassets/IconTracker/IsPinned.imageset/ic 24x24@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..cff5959660bacdb2698be6c38cfc90e315be29c9 GIT binary patch literal 359 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC&H|6fVg?2=RS;(M3{v?36l5$8 za(7}_cTVOdki(Mh=tNcQ*WF~_h#RA{o91Xz79`Qg_(MN3Muz2S4@3yd&#}X=cgWKvie`u zc#ydC!NEmbQ)jGiQSaul*sEf@epPy|r)~D`uU~d%G0p3^c>B>gffJXkH(Ih81Z}94 zGis=QzVC?sq+dB93JZ7-StUM{mjSwaQT->T_W{ozYG?Va1%?BIr>mdKI;Vst01BFq AumAu6 literal 0 HcmV?d00001 diff --git a/Tracker/Assets.xcassets/IconTracker/IsPinned.imageset/ic 24x24@3x.png b/Tracker/Assets.xcassets/IconTracker/IsPinned.imageset/ic 24x24@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..3f67bb78dc85992f082bdc8cbe3e141169e000eb GIT binary patch literal 508 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 diff --git a/Tracker/Assets.xcassets/IconsTabBar/tabBarStatistic.imageset/Tab Bar Item@3x.png b/Tracker/Assets.xcassets/IconsTabBar/tabBarStatistic.imageset/Tab Bar Item@3x.png deleted file mode 100644 index e4a40effd2795558a70c8e93b348b7855c6cd5b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4158 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 diff --git a/Tracker/Assets.xcassets/IconsTabBar/tabBarTracker.imageset/Tab Bar Item@3x.png b/Tracker/Assets.xcassets/IconsTabBar/tabBarTracker.imageset/Tab Bar Item@3x.png deleted file mode 100644 index 5710a238660a852e0d7aad6ff39206b38fef584f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3689 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 diff --git a/Tracker/Assets.xcassets/IconsTabBar/tabBarTracker.imageset/record.circle.fill.png b/Tracker/Assets.xcassets/IconsTabBar/tabBarTracker.imageset/record.circle.fill.png new file mode 100644 index 0000000000000000000000000000000000000000..9da8e39dcdb8a30d7f30eb9c0f57af51982fca3a GIT binary patch literal 560 zcmV-00?+-4P)x=8mk-*EmGg)pPzQpl!z;iQ zII)3M*0Ae$Z%M<86DHz08bW8skLvkY)3CGXo);g(JVI!_UvHk|GOS3nd0}9(J>K;{ zYrlMfT|`CM0xT?y*Vg-`pizoVhBTm>Jvhd^_!Kpi)^xJL(Pe9)Wu^E1RWOWY^Q~Zy zy>n8Uw%?jFH`%JxqChMBK!(gFi3j{_q-RK2&cZmGRt9DyHYEL!TTf&diX#@mu2+Yh zlfkylE-aNm65A#Tsq6w0+2;yg603Hz6sL!u^-@RDdReovrRSAZIJsp-^lZt)>-Sb) zq@>0c8Hz1uR`A4B}@_>NiK&wVjt##BDKY5x?b)7LqBS8x*AHi`FQ~I<30000KLx229vsP7pW&Z~}+{DLdP{NC_XGAm#-41~{9IM~bq>5bDhFz;cAU?BfxCsnxV`aohPkIsLM9^BbWH_lGwvGMcO zN`CUJ=Mn~zdU_}cJ%#%O@$uOB#lm%R>L&XKA!Q(Q*r;4XUt;O(>F$G&YC%$EdQ;}? zKu;)Vv$YjT%nx`3kitSs^Ljicq;hy9u)cCg7ct{4`3dlN2$~$aiN*1jQFX;!G+`i( zN&7MX8T)cMgeRD=Y<}B0W=f5?@(^AS9nzvhd%Zej@3~$Jw4z-pU5oI5d9Wv~1CC@c zd6Z7#;?Z_$eUM3Y&z7B~nT=XWJh%);XpPKOC>6bcJHn&wiFH@w%cyQ_Lsxt8&-@iJ zkFq0vEx-fEHq=yf9v~WOiJ4r02S%UO%kad7J}H9R!7~#3!I#jZWf-H+xkA`+KRV{nfMz8cOCRM zio68VAkv975A(2{V??^*I!fds=i{~6H?ry(E3Wp^#-DWPnED70L{W5LCv+gimwu&= zq~&fC^V1m%nUKDwvp@P)0MnY!WNSGsHt_9OG%8)iF}+bcjI;4*>>E)5>9BD^1l2=;=IeA!$OEV7<=?D*L?h{0EV|O$%w`FlYb(002ov JPDHLkV1ghXtqcGF literal 0 HcmV?d00001 diff --git a/Tracker/Assets.xcassets/IconsTabBar/tabBarTracker.imageset/record.circle.fill@3x.png b/Tracker/Assets.xcassets/IconsTabBar/tabBarTracker.imageset/record.circle.fill@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..fb59a819eea68dc0b983c9b103a1b0d9bea5b336 GIT binary patch literal 1233 zcmV;?1TOoDP)9?xk4(n%-j^t~MtzbdMzkiPWa=iYPAe{KUmID{!tsUH>2PL>19-NMz+ z4J{nsTs{d~_2MydAUA>KKid^+u||XnGp!Q1FzWjZIws-+V!VJsYyY1+EqK7IKo!N{ z`)o~hoS+nha(69S#N++H?}W&R7lDy5#ZtY;U z#hoM7(#glA?f#EP3e>GYQ#0lSW4Tt^d|SKv+0Zqp>rVGM4<*->q2r~WPm34Z$Iu8$ zg|a<=;uJ3QmQ_Sz1f$y0Mu}x^AtPB;97$pXqr!R4xzI2W!QP&C1kZ>sY>6-YpUH-% zO9x#n6c%oD3v9(Jl31)9YFgPFi#Pf|)U^kX$P;YBiUVj2EvH)Y(d+-^Z-}twB-g1^u%I132~N5MvB5nh7ZoS2{@&}xF9*oLZ?*TU zWbB>~k?|yB{j386-pDFW(Q9=KcpxTF=KmTT(KWaK?_|?5iD(Rp)~6G3fl|-G6-g-Y zgk)alu`UI!){Y9!b6%eKh-^7AQR~n#ikAX!FD?dfB51U3u1Y+o`olRV+C*^z6Rxd4vzRV%ToC7!jZT@Htrj>M6tiK zl?Aa)`k(?h5)(*E3_Cp5h@IXwU)_&^c-~pMc&EL5_?bV~eblJABlq~~$;v>9 zI98xeRqE0Mz}+wANZ7l_0n)!la$W$;=4&KY&H{(DD^s^DbO0S_mZqy7$t~H=ucLag znO?>#ZM<#B=#OxnNJ2?kbNys$I=$LismYUB$2F|W=va>UK^k#N=B}EiW1X^7t#~AA zx9w^1<&%}?sV{*C@;IHnN{UnBQP=t-5^7KtKdC|kxS?kC*)7{?v>Mr8SkLXHbUmdt v##ZFlH8ExZ#vje8Anhm|lI7>VPN}{DRaN2q&o#SV00000NkvXXu0mjfi3dmB literal 0 HcmV?d00001 diff --git a/Tracker/CoreData/Model.xcdatamodeld/Model.xcdatamodel/contents b/Tracker/CoreData/Model.xcdatamodeld/Model.xcdatamodel/contents index f82ef77..bb1e0d5 100644 --- a/Tracker/CoreData/Model.xcdatamodeld/Model.xcdatamodel/contents +++ b/Tracker/CoreData/Model.xcdatamodeld/Model.xcdatamodel/contents @@ -1,16 +1,23 @@ + + + + + + - - - - - + + + + + + diff --git a/Tracker/Domain/DataProvider.swift b/Tracker/Domain/DataProvider.swift deleted file mode 100644 index 31729db..0000000 --- a/Tracker/Domain/DataProvider.swift +++ /dev/null @@ -1,189 +0,0 @@ -// -// TrackerCategoryStore.swift -// Tracker -// -// Created by Григорий Машук on 17.10.23. -// - -import Foundation -import CoreData - -protocol DataProviderDelegate: AnyObject { - func storeCategory(dataProvider: DataProvider, indexPath: IndexPath) -} - -protocol DataProviderprotocol { - var trackerCategory: [TrackerCategory] { get } - var treckersRecords: Set { get } - - func addTracker(_ nameCategory: String, tracker: Tracker) throws - func addNewCategory(_ nameCategori: String, tracker: Tracker) throws - - func addCategory(nameCategory: String) throws - - func addNewTrackerRecord(_ trackerRecord: TrackerRecord) throws - func deleteTrackerRecord(_ trackerRecord: TrackerRecord) throws - func loadTrackerRecord(id: UUID) throws -> Int -} - -//MARK: - DataProvider -final class DataProvider: NSObject { - weak var delegate: DataProviderDelegate? - - private let context: NSManagedObjectContext - - private let trackerStore: TrackerStoreProtocol - private let trackerCategoryStore: TrackerCategoryStoreProtocol - private let trackerRecordStore: TrackerRecordStore - - private var indexPathCategory: IndexPath? - - init(delegate: DataProviderDelegate) { - self.delegate = delegate - let context = AppDelegate.container.viewContext - self.context = context - self.trackerStore = TrackerStore() - self.trackerCategoryStore = TrackerCategoryStore() - self.trackerRecordStore = TrackerRecordStore() - } - - private lazy var fetchedTrackerResultController: NSFetchedResultsController = { - let request = TrackerCoreData.fetchRequest() - request.sortDescriptors = [ - NSSortDescriptor(keyPath: \TrackerCoreData.id, ascending: true) - ] - let fetchedResultController = NSFetchedResultsController(fetchRequest: request, - managedObjectContext: context, - sectionNameKeyPath: nil, - cacheName: nil) - fetchedResultController.delegate = self - try? fetchedResultController.performFetch() - - return fetchedResultController - }() - - private lazy var fetchedCategoryResultController: NSFetchedResultsController = { - let request = TrackerCategoryCoreData.fetchRequest() - request.sortDescriptors = [NSSortDescriptor(keyPath: \TrackerCategoryCoreData.nameCategory, ascending: true)] - let fetchedResultController = NSFetchedResultsController(fetchRequest: request, - managedObjectContext: context, - sectionNameKeyPath: nil, - cacheName: nil) - fetchedResultController.delegate = self - try? fetchedResultController.performFetch() - - return fetchedResultController - }() - - private lazy var fetchedTrackerRecordResultController: NSFetchedResultsController = { - let request = TrackerRecordCoreData.fetchRequest() - request.sortDescriptors = [NSSortDescriptor(keyPath: \TrackerRecordCoreData.date, ascending: true)] - let fetchedResultController = NSFetchedResultsController(fetchRequest: request, - managedObjectContext: context, - sectionNameKeyPath: nil, - cacheName: nil) - try? fetchedResultController.performFetch() - - return fetchedResultController - }() -} - -//MARK: - DataProviderprotocol -extension DataProvider: DataProviderprotocol { - var treckersRecords: Set { - guard let objects = fetchedTrackerRecordResultController.fetchedObjects - else { return [] } - let treckersRecords = trackerRecordStore.treckersRecordsResult(trackerRecordsCoreData: objects) - return treckersRecords - } - - var trackerCategory: [TrackerCategory] { - guard let objects = fetchedCategoryResultController.fetchedObjects - else { return [] } - let treckerCategory = trackersCategoryResult(trackerCategoryCoreData: objects) - - return treckerCategory - } - - func addCategory(nameCategory: String) throws { - try trackerCategoryStore.addCategory(nameCategory) - } - - func addTracker(_ nameCategory: String, tracker: Tracker) throws { - fetchedCategoryResultController.delegate = nil - let _ = fetchedTrackerResultController.fetchedObjects - try trackerStore.addNewTracker(tracker, nameCategory: nameCategory) - } - - func addNewCategory(_ nameCategori: String, tracker: Tracker) throws { - let trackerCoreData = TrackerCoreData(context: context) - try trackerStore.update(trackerCoreData, tracker: tracker) - try trackerCategoryStore.addNewCategory(nameCategory: nameCategori, trackerCoreData: trackerCoreData) - } - - func updateCategory(nameCategory: String, tracker: Tracker) throws { - - } - - func addNewTrackerRecord(_ trackerRecord: TrackerRecord) throws { - try trackerRecordStore.addNewTrackerRecord(trackerRecord) - } - - func deleteTrackerRecord(_ trackerRecord: TrackerRecord) throws { - try trackerRecordStore.deleteTrackerRecord(trackerRecord) - } - - func loadTrackerRecord(id: UUID) throws -> Int { - try trackerRecordStore.loadTrackerRecord(id: id) - } -} - -private extension DataProvider { - func category(from trackerCategoryCoreData: TrackerCategoryCoreData) throws -> TrackerCategory { - guard let nameCategory = trackerCategoryCoreData.nameCategory else { - throw TrackrerCategoryStoreError.decodingErrorInvalidNameCategori - } - - guard let arrayTrackersCoreData = trackerCategoryCoreData.trakers?.allObjects as? [TrackerCoreData] else { throw NSSetError.transformationErrorInvalid } - - let arrayTrackers = try arrayTrackersCoreData.map ({ try self.trackerStore.tracker(from: $0) }) - - return TrackerCategory(nameCategory: nameCategory, arrayTrackers: arrayTrackers) - } - - func trackersCategoryResult(trackerCategoryCoreData: [TrackerCategoryCoreData]) -> [TrackerCategory] { - guard let treckerCategory = try? trackerCategoryCoreData.map ({ try self.category(from: $0) }) - else { return [] } - - return treckerCategory - } -} - -//MARK: - NSFetchedResultsControllerDelegate -extension DataProvider: NSFetchedResultsControllerDelegate { - func controllerWillChangeContent(_ controller: NSFetchedResultsController) { - indexPathCategory = IndexPath() - } - - func controllerDidChangeContent(_ controller: NSFetchedResultsController) { - guard let delegate, - let indexPathCategory - else { return } - delegate.storeCategory(dataProvider: self, indexPath: indexPathCategory) - self.indexPathCategory = nil - } - - func controller( - _ controller: NSFetchedResultsController, - didChange anObject: Any, - at indexPath: IndexPath?, - for type: NSFetchedResultsChangeType, - newIndexPath: IndexPath? - ) { - if case .insert = type { - guard let indexPath = newIndexPath else { fatalError() } - indexPathCategory = indexPath - } - } -} - diff --git a/Tracker/Domain/Store/PinnedCategoryStore.swift b/Tracker/Domain/Store/PinnedCategoryStore.swift new file mode 100644 index 0000000..0c87b5e --- /dev/null +++ b/Tracker/Domain/Store/PinnedCategoryStore.swift @@ -0,0 +1,77 @@ +// +// PinnedCategoryStore.swift +// Tracker +// +// Created by Марина Машук on 11.12.23. +// + +import Foundation +import CoreData + +protocol PinnedCategoryStoreProtocol { + func addPinnedCategory(_ nameCategory: String) -> Result + func deleteAndGetPinnedCategory(_ id: UUID) -> Result +} + +protocol PinnedCategoryStoreDelegate: AnyObject { + func storePinnedCategory(trackerCategoryStore: PinnedCategoryStoreProtocol) +} + +//MARK: - TrackerStore +final class PinnedCategoryStore: NSObject { + weak var delegate: PinnedCategoryStoreDelegate? + + private let context: NSManagedObjectContext + + convenience override init() { + let context = AppDelegate.container.viewContext + self.init(context: context) + } + + init(context: NSManagedObjectContext) { + self.context = context + } +} + +private extension PinnedCategoryStore { + func save(context: NSManagedObjectContext) -> Result { + do { + try context.save() + return .success(()) + } catch { + return .failure(error) + } + } +} + +extension PinnedCategoryStore: PinnedCategoryStoreProtocol { + func addPinnedCategory(_ nameCategory: String) -> Result { + let pinnCategoryCoreData = PinnCategoryCoreData(context: context) + pinnCategoryCoreData.nameCategory = nameCategory + do { + try context.save() + return .success(pinnCategoryCoreData) + } + catch { return .failure(error) } + } + + func deleteAndGetPinnedCategory(_ id: UUID) -> Result { + let request = NSFetchRequest(entityName: "\(PinnCategoryCoreData.self)") + request.returnsObjectsAsFaults = false + guard let keyPath = (\PinnCategoryCoreData.trackerId)._kvcKeyPathString + else { return .failure(StoreErrors.TrackrerStoreError.getTrackerError) } + request.predicate = NSPredicate(format: "%K == %@", keyPath, id as CVarArg) + do { + if let pinnCategoryCoreData = try context.fetch(request).first { + let nameCategory = pinnCategoryCoreData.nameCategory + pinnCategoryCoreData.nameCategory = nil + pinnCategoryCoreData.trackerId = nil + let _ = save(context: context) + return .success(nameCategory) + } + } catch { + return .failure(error) + } + return .failure(StoreErrors.TrackrerStoreError.getTrackerError) + } +} diff --git a/Tracker/Domain/Store/TrackerCategoryStore.swift b/Tracker/Domain/Store/TrackerCategoryStore.swift index de60e97..32d7dc0 100644 --- a/Tracker/Domain/Store/TrackerCategoryStore.swift +++ b/Tracker/Domain/Store/TrackerCategoryStore.swift @@ -5,36 +5,41 @@ // Created by Григорий Машук on 17.10.23. // -import UIKit +import Foundation import CoreData -enum TrackrerCategoryStoreError: Error { - case decodingErrorInvalidNameCategori -} - -enum TrackrerStoreError: Error { - case decodingErrorInvalidName - case decodingErrorInvalidId - case decodingErrorInvalidColor - case decodingErrorInvalidEmoji - case decodingErrorInvalidSchedul +protocol TrackerCategoryStoreProtocol { + func addCategory(_ nameCategory: String) -> Result + func getListTrackerCategoryCoreData() -> [TrackerCategoryCoreData]? + func deleteCategory(nameCategory: String) -> Result } -enum NSSetError: Error { - case transformationErrorInvalid -} - -protocol TrackerCategoryStoreProtocol { - func addNewCategory(nameCategory: String, trackerCoreData: TrackerCoreData) throws - func addCategory(_ nameCategory: String) throws +protocol TrackerCategoryStoreDelegate: AnyObject { + func storeCategory(trackerCategoryStore: TrackerCategoryStoreProtocol) } //MARK: - TrackerCategoryStore final class TrackerCategoryStore: NSObject { + private let textFixed = NSLocalizedString("textFixed", comment: "") + weak var delegate: TrackerCategoryStoreDelegate? + private let context: NSManagedObjectContext - private var indexPathCategory: IndexPath? - + private lazy var fetchedCategoryResultController: NSFetchedResultsController = { + let request = TrackerCategoryCoreData.fetchRequest() + let sortPinned = NSSortDescriptor(keyPath: \TrackerCategoryCoreData.isPinned, ascending: true) + let sortName = NSSortDescriptor(keyPath: \TrackerCategoryCoreData.nameCategory, ascending: true) + request.sortDescriptors = [sortPinned, sortName] + let fetchedResultController = NSFetchedResultsController(fetchRequest: request, + managedObjectContext: context, + sectionNameKeyPath: nil, + cacheName: nil) + fetchedResultController.delegate = self + try? fetchedResultController.performFetch() + + return fetchedResultController + }() + convenience override init() { let context = AppDelegate.container.viewContext self.init(context: context) @@ -45,20 +50,51 @@ final class TrackerCategoryStore: NSObject { } } +private extension TrackerCategoryStore { + func save(context: NSManagedObjectContext) -> Result { + do { + try context.save() + return .success(()) + } catch { + return .failure(error) + } + } +} + //MARK: - TrackerCategoryStoreProtocol extension TrackerCategoryStore: TrackerCategoryStoreProtocol { - func addNewCategory(nameCategory: String, trackerCoreData: TrackerCoreData) throws { + func addCategory(_ nameCategory: String) -> Result { let categoryCoreData = TrackerCategoryCoreData(context: context) categoryCoreData.nameCategory = nameCategory - categoryCoreData.addToTrakers(trackerCoreData) - try context.save() + categoryCoreData.isPinned = nameCategory == textFixed ? false : true + return save(context: context) } - func addCategory(_ nameCategory: String) throws { - let categoryCoreData = TrackerCategoryCoreData(context: context) - categoryCoreData.nameCategory = nameCategory - try context.save() + func getListTrackerCategoryCoreData() -> [TrackerCategoryCoreData]? { + fetchedCategoryResultController.fetchedObjects + } + + func deleteCategory(nameCategory: String) -> Result { + let request = NSFetchRequest(entityName: "\(TrackerCategoryCoreData.self)") + request.returnsObjectsAsFaults = false + request.predicate = NSPredicate(format: "%K == %@", #keyPath(TrackerCategoryCoreData.nameCategory), nameCategory as CVarArg) + do { + if let category = try context.fetch(request).first { + context.delete(category) + return save(context: context) + } + } catch { + return .failure(error) + } + return save(context: context) } } - +//MARK: - NSFetchedResultsControllerDelegate +extension TrackerCategoryStore: NSFetchedResultsControllerDelegate { + func controllerDidChangeContent(_ controller: NSFetchedResultsController) { + guard let delegate + else { return } + delegate.storeCategory(trackerCategoryStore: self) + } +} diff --git a/Tracker/Domain/Store/TrackerRecordStore.swift b/Tracker/Domain/Store/TrackerRecordStore.swift index 3d73add..0ad46a4 100644 --- a/Tracker/Domain/Store/TrackerRecordStore.swift +++ b/Tracker/Domain/Store/TrackerRecordStore.swift @@ -8,22 +8,34 @@ import Foundation import CoreData -enum TrackrerRecordStoreError: Error { - case decodingErrorInvalidId - case decodingErrorInvalidDate - case loadTrackerRecord(Error) +protocol TrackerRecordStoreProtocol { + func updateTrackerRecord(_ trackerRecord: TrackerRecord) -> Result + func loadTrackerRecord(id: UUID) -> Result + func treckersRecordsResult() -> Result, Error> } -protocol TrackerRecordStoreProtocol { - func addNewTrackerRecord(_ trackerRecord: TrackerRecord) throws - func deleteTrackerRecord(_ trackerRecord: TrackerRecord) throws - func loadTrackerRecord(id: UUID) throws -> Int - func treckersRecordsResult(trackerRecordsCoreData: [TrackerRecordCoreData]) throws -> Set +protocol TrackerRecordStoreDelegate: AnyObject { + func trackerRecordStore(trackerRecordStore: TrackerRecordStoreProtocol) } //MARK: - TrackerRecordStore final class TrackerRecordStore: NSObject { + weak var delegate: TrackerRecordStoreDelegate? + private let context: NSManagedObjectContext + + private lazy var fetchedTrackerRecordResultController: NSFetchedResultsController = { + let request = TrackerRecordCoreData.fetchRequest() + request.sortDescriptors = [NSSortDescriptor(keyPath: \TrackerRecordCoreData.date, ascending: true)] + let fetchedResultController = NSFetchedResultsController(fetchRequest: request, + managedObjectContext: context, + sectionNameKeyPath: nil, + cacheName: nil) + fetchedResultController.delegate = self + try? fetchedResultController.performFetch() + + return fetchedResultController + }() convenience override init() { let context = AppDelegate.container.viewContext @@ -36,12 +48,21 @@ final class TrackerRecordStore: NSObject { } private extension TrackerRecordStore { + func save(context: NSManagedObjectContext) -> Result { + do { + try context.save() + return .success(()) + } catch { + return .failure(error) + } + } + func trackerRecord(from trackerRecordCoreDate: TrackerRecordCoreData) throws -> TrackerRecord { guard let id = trackerRecordCoreDate.trackerId else { - throw TrackrerRecordStoreError.decodingErrorInvalidId + throw StoreErrors.TrackrerRecordStoreError.decodingErrorInvalidId } guard let date = trackerRecordCoreDate.date else { - throw TrackrerRecordStoreError.decodingErrorInvalidDate + throw StoreErrors.TrackrerRecordStoreError.decodingErrorInvalidDate } let trackerRecord = TrackerRecord(id: id, date: date) @@ -52,43 +73,72 @@ private extension TrackerRecordStore { trackerRecordCoreData.trackerId = trackerRecord.id trackerRecordCoreData.date = trackerRecord.date } + + func addNewTrackerRecord(_ trackerRecord: TrackerRecord) -> Result { + let trackerRecordCoreData = TrackerRecordCoreData(context: context) + updateExistingTrackerRecord(trackerRecordCoreData, trackerRecord: trackerRecord) + return save(context: context) + } + + func deleteTrackerRecord(_ trackerRecordCoreData: TrackerRecordCoreData) -> Result { + context.delete(trackerRecordCoreData) + return save(context: context) + } } //MARK: - TrackerRecordStoreProtocol extension TrackerRecordStore: TrackerRecordStoreProtocol { - func treckersRecordsResult(trackerRecordsCoreData: [TrackerRecordCoreData]) -> Set { - guard let trackerRecord = try? trackerRecordsCoreData.map ({ try self.trackerRecord(from: $0) }) - else { return [] } - - return Set(trackerRecord) - } - - func addNewTrackerRecord(_ trackerRecord: TrackerRecord) throws { - let trackerRecordCoreData = TrackerRecordCoreData(context: context) - updateExistingTrackerRecord(trackerRecordCoreData, trackerRecord: trackerRecord) - try context.save() + func treckersRecordsResult() -> Result, Error> { + guard let trackerRecordsCoreData = fetchedTrackerRecordResultController.fetchedObjects + else { return .failure(StoreErrors.TrackrerRecordStoreError.loadTrackerRecord) } + do { + let trackerRecord = try trackerRecordsCoreData.map ({ try self.trackerRecord(from: $0) }) + return .success(Set(trackerRecord)) + } catch { + return .failure(error) + } } - func deleteTrackerRecord(_ trackerRecord: TrackerRecord) throws { - let request = NSFetchRequest(entityName: "TrackerRecordCoreData") + func updateTrackerRecord(_ trackerRecord: TrackerRecord) -> Result { + let request = NSFetchRequest(entityName: "\(TrackerRecordCoreData.self)") request.returnsObjectsAsFaults = false request.predicate = NSPredicate(format: "%K == %@ AND %K == %@", #keyPath(TrackerRecordCoreData.date), trackerRecord.date as CVarArg, #keyPath(TrackerRecordCoreData.trackerId), trackerRecord.id as CVarArg) - let trackers = try context.fetch(request) - if let tracker = trackers.first { - context.delete(tracker) - try context.save() + do { + let trackers = try context.fetch(request) + if let tracker = trackers.first { + return deleteTrackerRecord(tracker) + } else { + return addNewTrackerRecord(trackerRecord) + } + } + catch { + return .failure(error) } } - func loadTrackerRecord(id: UUID) throws -> Int { - let request = NSFetchRequest(entityName: "TrackerRecordCoreData") + func loadTrackerRecord(id: UUID) -> Result { + let request = NSFetchRequest(entityName: "\(TrackerRecordCoreData.self)") request.returnsObjectsAsFaults = false - request.predicate = NSPredicate(format: "%K == %@", #keyPath(TrackerRecordCoreData.trackerId), id as CVarArg) - let countTrackers = try context.fetch(request).count - return countTrackers + request.predicate = NSPredicate(format: "%K == %@", + #keyPath(TrackerRecordCoreData.trackerId), + id as CVarArg) + do { + let countTrackers = try context.fetch(request).count + return .success(countTrackers) + } catch { + return .failure(error) + } + } +} + +//MARK: - NSFetchedResultsControllerDelegate +extension TrackerRecordStore: NSFetchedResultsControllerDelegate { + func controllerDidChangeContent(_ controller: NSFetchedResultsController) { + guard let delegate else { return } + delegate.trackerRecordStore(trackerRecordStore: self) } } diff --git a/Tracker/Domain/Store/TrackerStore.swift b/Tracker/Domain/Store/TrackerStore.swift index 59a883f..4905559 100644 --- a/Tracker/Domain/Store/TrackerStore.swift +++ b/Tracker/Domain/Store/TrackerStore.swift @@ -9,11 +9,12 @@ import Foundation import CoreData protocol TrackerStoreProtocol { - func tracker(from trackerCoreData: TrackerCoreData) throws -> Tracker - func trackers(_ objects: [TrackerCoreData]) throws -> [Tracker] - func deleteTracker(_ trackerCoreData: TrackerCoreData) throws - func update(_ trackerCoreData: TrackerCoreData, tracker: Tracker) throws - func addNewTracker(_ tracker: Tracker, nameCategory: String) throws + func addNewTracker(_ tracker: Tracker, nameCategory: String) -> Result + func getTrackers(_ objects: [TrackerCoreData]) -> Result<[Tracker], Error> + func updateTracker(tracker: Tracker) -> Result + func deleteTracker(_ id: UUID) -> Result + + func addPinnedCategory(_ id: UUID, pinnedCategory: PinnCategoryCoreData) -> Result } //MARK: - TrackerStore @@ -31,70 +32,130 @@ final class TrackerStore: NSObject { } } -extension TrackerStore { +//MARK: - Private +private extension TrackerStore { func tracker(from trackerCoreData: TrackerCoreData) throws -> Tracker { guard let id = trackerCoreData.id else { fatalError("not found ID") } guard let name = trackerCoreData.name else { - throw TrackrerStoreError.decodingErrorInvalidName + throw StoreErrors.TrackrerStoreError.decodingErrorInvalidName } guard let emoji = trackerCoreData.emoji else { - throw TrackrerStoreError.decodingErrorInvalidEmoji + throw StoreErrors.TrackrerStoreError.decodingErrorInvalidEmoji } guard let color = trackerCoreData.colorHex else { - throw TrackrerStoreError.decodingErrorInvalidColor + throw StoreErrors.TrackrerStoreError.decodingErrorInvalidColor } guard let schedul = trackerCoreData.schedule as? [WeekDay] else { - throw TrackrerStoreError.decodingErrorInvalidSchedul + throw StoreErrors.TrackrerStoreError.decodingErrorInvalidSchedul } return Tracker(id: id, name: name, color: colorMarshalling.color(from: color), emoji: emoji, schedule: schedul) } - private func updateExistingTrackerRecord(trackerCoreData: TrackerCoreData, tracker: Tracker) throws { + func updateExistingTrackerRecord(trackerCoreData: TrackerCoreData, tracker: Tracker) { trackerCoreData.id = tracker.id trackerCoreData.colorHex = colorMarshalling.hexString(from: tracker.color) trackerCoreData.emoji = tracker.emoji trackerCoreData.name = tracker.name trackerCoreData.schedule = tracker.schedule as NSObject } + + func save(context: NSManagedObjectContext) -> Result { + do { + try context.save() + return .success(()) + } catch { + return .failure(error) + } + } + + func searchTrackerCoreData(id: UUID) throws -> TrackerCoreData? { + let request = NSFetchRequest(entityName: "\(TrackerCoreData.self)") + request.returnsObjectsAsFaults = false + guard let keyPath = (\TrackerCoreData.id)._kvcKeyPathString + else { throw StoreErrors.TrackrerStoreError.getTrackerError } + request.predicate = NSPredicate(format: "%K == %@", keyPath, id as CVarArg) + return try context.fetch(request).first + } } -//MARK: - TrackerStore -extension TrackerStore: TrackerStoreProtocol { - func addNewTracker(_ tracker: Tracker, nameCategory: String) throws { - let request = NSFetchRequest(entityName: "TrackerCategoryCoreData") +//MARK: - TrackerStoreProtocol +extension TrackerStore: TrackerStoreProtocol { + func addNewTracker(_ tracker: Tracker, nameCategory: String) -> Result { + let request = NSFetchRequest(entityName: "\(TrackerCategoryCoreData.self)") request.returnsObjectsAsFaults = false request.predicate = NSPredicate(format: "%K == %@", #keyPath(TrackerCategoryCoreData.nameCategory), nameCategory as CVarArg) - let category = try context.fetch(request).first - let trackerCoreData = TrackerCoreData(context: context) - try updateExistingTrackerRecord(trackerCoreData: trackerCoreData, tracker: tracker) - category?.addToTrakers(trackerCoreData) - try context.save() + do { + if let category = try context.fetch(request).first { + let trackerCoreData = TrackerCoreData(context: context) + updateExistingTrackerRecord(trackerCoreData: trackerCoreData, tracker: tracker) + category.addToTrakers(trackerCoreData) + return save(context: context) + } + } catch { + return .failure(error) + } + return save(context: context) + } + + func updateTracker(tracker: Tracker) -> Result { + do { + if let trackerCoreData = try searchTrackerCoreData(id: tracker.id) { + updateExistingTrackerRecord(trackerCoreData: trackerCoreData, tracker: tracker) + return save(context: context) + } + } catch { + return .failure(error) + } + return save(context: context) } - func deleteTracker(_ trackerCoreData: TrackerCoreData) throws { - context.delete(trackerCoreData) - try context.save() + func deleteTracker(_ id: UUID) -> Result { + do { + if let trackerCoreData = try searchTrackerCoreData(id: id) { + context.delete(trackerCoreData) + return save(context: context) + } + } catch { + return .failure(error) + } + return save(context: context) } - func update(_ trackerCoreData: TrackerCoreData, tracker: Tracker) throws { - try updateExistingTrackerRecord(trackerCoreData: trackerCoreData, tracker: tracker) - try context.save() + func getTrackers(_ objects: [TrackerCoreData]) -> Result<[Tracker], Error> { + do { + let trackers = try objects.map({ try tracker(from: $0) }) + return .success(trackers) + } catch { + return .failure(error) + } } - func trackers(_ objects: [TrackerCoreData]) throws -> [Tracker] { - guard let trackers = try? objects.map({try tracker(from: $0) }) - else { return [] } - - return trackers + func addPinnedCategory(_ id: UUID, pinnedCategory: PinnCategoryCoreData) -> Result { + let request = NSFetchRequest(entityName: "\(TrackerCoreData.self)") + request.returnsObjectsAsFaults = false + guard let keyPath = (\TrackerCoreData.id)._kvcKeyPathString + else { return .failure(StoreErrors.TrackrerStoreError.getTrackerError) } + request.predicate = NSPredicate(format: "%K == %@", keyPath, id as CVarArg) + do { + if let trackerCoreData = try searchTrackerCoreData(id: id) { + pinnedCategory.addToTracker(trackerCoreData) + pinnedCategory.trackerId = id + + return save(context: context) + } + } catch { + return .failure(error) + } + return save(context: context) } } diff --git a/Tracker/Extantion/Date.swift b/Tracker/Extension +/Date.swift similarity index 100% rename from Tracker/Extantion/Date.swift rename to Tracker/Extension +/Date.swift diff --git a/Tracker/Extantion/String.swift b/Tracker/Extension +/String.swift similarity index 73% rename from Tracker/Extantion/String.swift rename to Tracker/Extension +/String.swift index 968e1e8..be5ddde 100644 --- a/Tracker/Extantion/String.swift +++ b/Tracker/Extension +/String.swift @@ -11,4 +11,8 @@ extension String { func firstCharOnly() -> String { return prefix(1).uppercased() + self.dropFirst() } + + var string: String { + NSExpression(forKeyPath: self).keyPath + } } diff --git a/Tracker/Extantion/StringProtocol.swift b/Tracker/Extension +/StringProtocol.swift similarity index 100% rename from Tracker/Extantion/StringProtocol.swift rename to Tracker/Extension +/StringProtocol.swift diff --git a/Tracker/Extantion/UIColor.swift b/Tracker/Extension +/UIColor.swift similarity index 100% rename from Tracker/Extantion/UIColor.swift rename to Tracker/Extension +/UIColor.swift diff --git a/Tracker/Extantion/UITextField.swift b/Tracker/Extension +/UITextField.swift similarity index 100% rename from Tracker/Extantion/UITextField.swift rename to Tracker/Extension +/UITextField.swift diff --git a/Tracker/Models/Enum/ChoiceParametrs.swift b/Tracker/Models/Enum/ChoiceParametrs.swift index 5e11b4d..c2fcddf 100644 --- a/Tracker/Models/Enum/ChoiceParametrs.swift +++ b/Tracker/Models/Enum/ChoiceParametrs.swift @@ -15,9 +15,9 @@ enum ChoiceParametrs { var name: String switch self { case .category: - name = "Категория" + name = NSLocalizedString("category", comment: "") case .schedule: - name = "Расписание" + name = NSLocalizedString("schedule", comment: "") } return name } diff --git a/Tracker/Models/Enum/Errors.swift b/Tracker/Models/Enum/Errors.swift new file mode 100644 index 0000000..a18efcd --- /dev/null +++ b/Tracker/Models/Enum/Errors.swift @@ -0,0 +1,39 @@ +// +// Errors.swift +// Tracker +// +// Created by Григорий Машук on 16.11.23. +// + +import Foundation +enum StoreErrors { + enum TrackrerCategoryStoreError: Error { + case decodingErrorInvalidNameCategori + case getCategoryCoreDataError + } + + enum TrackrerStoreError: Error { + case getTrackerError + case decodingErrorInvalidName + case decodingErrorInvalidId + case decodingErrorInvalidColor + case decodingErrorInvalidEmoji + case decodingErrorInvalidSchedul + } + + enum NSSetError: Error { + case transformationErrorInvalid + } + + enum TrackrerRecordStoreError: Error { + case decodingErrorInvalidId + case decodingErrorInvalidDate + case deleteError + case loadTrackerRecord + case getTrackerRecord + } + + enum PinnedCategoryStoreError: Error { + case decodingErrorInvalidName + } +} diff --git a/Tracker/Models/Enum/Header.swift b/Tracker/Models/Enum/Header.swift index f9a5089..726d1c5 100644 --- a/Tracker/Models/Enum/Header.swift +++ b/Tracker/Models/Enum/Header.swift @@ -15,7 +15,7 @@ enum Header { var name: String switch self { case .color: - name = "Цвет" + name = NSLocalizedString("stringColor", comment: "") case .emoji: name = "Emoji" } diff --git a/Tracker/Models/Enum/NameEvent.swift b/Tracker/Models/Enum/NameEvent.swift index acd2992..168806a 100644 --- a/Tracker/Models/Enum/NameEvent.swift +++ b/Tracker/Models/Enum/NameEvent.swift @@ -15,9 +15,9 @@ enum NameEvent { var name: String switch self { case .habit: - name = "Привычка" + name = NSLocalizedString("nameHabit", comment: "") case .irregularEvent: - name = "Нерегулярное событие" + name = NSLocalizedString("nameIrregularEvent", comment: "") } return name } diff --git a/Tracker/Models/Enum/WeekDay.swift b/Tracker/Models/Enum/WeekDay.swift index 00c1dd6..2d68e20 100644 --- a/Tracker/Models/Enum/WeekDay.swift +++ b/Tracker/Models/Enum/WeekDay.swift @@ -20,19 +20,19 @@ enum WeekDay: Int { var day: String switch self { case .monday: - day = "Понедельник" + day = NSLocalizedString("monday.complete", comment: "") case .tuesday: - day = "Вторник" + day = NSLocalizedString("tuesday.complete", comment: "") case .wednesday: - day = "Среда" + day = NSLocalizedString("wednesday.complete", comment: "") case .thursday: - day = "Четверг" + day = NSLocalizedString("thursday.complete", comment: "") case .friday: - day = "Пятница" + day = NSLocalizedString("friday.complete", comment: "") case .saturday: - day = "Суббота" + day = NSLocalizedString("saturday.complete", comment: "") case .sunday: - day = "Воскресенье" + day = NSLocalizedString("sunday.complete", comment: "") } return day } @@ -41,19 +41,19 @@ enum WeekDay: Int { var briefWordDay: String switch self { case .monday: - briefWordDay = "Пн" + briefWordDay = NSLocalizedString("monday", comment: "") case .tuesday: - briefWordDay = "Вт" + briefWordDay = NSLocalizedString("tuesday", comment: "") case .wednesday: - briefWordDay = "Ср" + briefWordDay = NSLocalizedString("wednesday", comment: "") case .thursday: - briefWordDay = "Чт" + briefWordDay = NSLocalizedString("thursday", comment: "") case .friday: - briefWordDay = "Пт" + briefWordDay = NSLocalizedString("friday", comment: "") case .saturday: - briefWordDay = "Сб" + briefWordDay = NSLocalizedString("saturday", comment: "") case .sunday: - briefWordDay = "Вс" + briefWordDay = NSLocalizedString("sunday", comment: "") } return briefWordDay } diff --git a/Tracker/Models/PinnTrackerModel.swift b/Tracker/Models/PinnTrackerModel.swift new file mode 100644 index 0000000..93871e6 --- /dev/null +++ b/Tracker/Models/PinnTrackerModel.swift @@ -0,0 +1,21 @@ +// +// UnpinModel.swift +// Tracker +// +// Created by Григорий Машук on 10.12.23. +// + +import Foundation + +struct PinnTracker { + let id: UUID + let nameCategory: String +} + +extension PinnTracker: Equatable { + static func == (lhs: PinnTracker, rhs: PinnTracker) -> Bool { + lhs.id == rhs.id + } +} + +extension PinnTracker: Hashable {} diff --git a/Tracker/Models/TrackerCategoryModel.swift b/Tracker/Models/TrackerCategoryModel.swift index 3fc5e06..5387cb3 100644 --- a/Tracker/Models/TrackerCategoryModel.swift +++ b/Tracker/Models/TrackerCategoryModel.swift @@ -10,6 +10,7 @@ import Foundation struct TrackerCategory { let nameCategory: String let arrayTrackers: [Tracker] + let isPinned: Bool } extension TrackerCategory: Hashable { diff --git a/Tracker/Models/TrackerModel.swift b/Tracker/Models/TrackerModel.swift index 3bb1f7c..705df17 100644 --- a/Tracker/Models/TrackerModel.swift +++ b/Tracker/Models/TrackerModel.swift @@ -14,7 +14,12 @@ struct Tracker { let emoji: String let schedule: [WeekDay] - init(id: UUID = UUID() , name: String, color: UIColor, emoji: String, schedule: [WeekDay]) { + init(id: UUID = UUID(), + name: String, + color: UIColor, + emoji: String, + schedule: [WeekDay]) + { self.id = id self.name = name self.color = color diff --git a/Tracker/Models/UpdateTrackerModel.swift b/Tracker/Models/UpdateTrackerModel.swift new file mode 100644 index 0000000..398a957 --- /dev/null +++ b/Tracker/Models/UpdateTrackerModel.swift @@ -0,0 +1,13 @@ +// +// UpdateTrackerModel.swift +// Tracker +// +// Created by Григорий Машук on 28.11.23. +// + +import Foundation + +struct UpdateTracker { + let count: Int + let flag: Bool +} diff --git a/Tracker/Onboarding/OnboardingPageViewController.swift b/Tracker/Onboarding/OnboardingPageViewController.swift index 77a27a9..2528312 100644 --- a/Tracker/Onboarding/OnboardingPageViewController.swift +++ b/Tracker/Onboarding/OnboardingPageViewController.swift @@ -10,7 +10,7 @@ import UIKit //MARK: - OnboardingPageViewController final class OnboardingPageViewController: UIPageViewController { private struct ConstantsOnboardingPageViewController { - static let textLableTwoVC = "Даже если это не литры воды и йога" + static let textLableTwoVC = NSLocalizedString("textLableTwoVC", comment: "") static let imageNameTwoVC = "twoPage" static let numberOfPages = 2 static let currentPage = 0 diff --git a/Tracker/Onboarding/OnboardingViewController.swift b/Tracker/Onboarding/OnboardingViewController.swift index 85b99b8..7af1cb8 100644 --- a/Tracker/Onboarding/OnboardingViewController.swift +++ b/Tracker/Onboarding/OnboardingViewController.swift @@ -10,7 +10,7 @@ import UIKit //MARK: - OnboardingViewController final class OnboardingViewController: UIViewController { private struct ConstantsOnboardingViewController { - static let textButton = "Вот это технологии!" + static let textButton = NSLocalizedString("textButton", comment: "") static let cornerRadiusButton = CGFloat(16) static let fontTextLable = UIFont.systemFont(ofSize: 32, weight: .bold) static let fontTextButton = UIFont.systemFont(ofSize: 16, weight: .medium) diff --git a/Tracker/PropertyWrappers/UserDefaultsBacked.swift b/Tracker/PropertyWrappers/UserDefaultsBacked.swift index 79e823e..15aad90 100644 --- a/Tracker/PropertyWrappers/UserDefaultsBacked.swift +++ b/Tracker/PropertyWrappers/UserDefaultsBacked.swift @@ -19,5 +19,4 @@ struct UserDefaultsBacked { storage.setValue(newValue, forKey: key) } } - } diff --git a/Tracker/Resources/en.lproj/InfoPlist.strings b/Tracker/Resources/en.lproj/InfoPlist.strings new file mode 100644 index 0000000..409f92b --- /dev/null +++ b/Tracker/Resources/en.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* + InfoPlist.strings + Tracker + + Created by Марина Машук on 14.11.23. + +*/ + +"CFBundleDisplayName" = "Tracker"; diff --git a/Tracker/Resources/en.lproj/Localizable.strings b/Tracker/Resources/en.lproj/Localizable.strings new file mode 100644 index 0000000..6af2d41 --- /dev/null +++ b/Tracker/Resources/en.lproj/Localizable.strings @@ -0,0 +1,83 @@ +/* +/* + Localizable.strings + Tracker + + Created by Григорий Машук on 12.11.23. + + */ + +//MARK: - OnboardingViewController +"textLableOneVC" = "Track only what you want"; +"textLableTwoVC" = "Even if it's not liters of water and yoga"; +"textButton" = "These are technologies!"; + +//MARK: - TabBarController +"trackers" = "Trackers"; +"statistics" = "Statistics"; + +//MARK: - TrackersViewController +"headerText" = "Trackers"; +"labelStabText" = "What will we track?"; +"labelNothingFoundText" = "Nothing found"; +"placeholderSearch" = "Search"; +"textCancel" = "Cancel"; +"textFix" = "To fix"; +"textUnpin" = "Unpin"; +"textEdit" = "Edit"; +"textDelete" = "Delete"; +"textFixed" = "Fixed"; + +//MARK: - CreateTrackerViewController +"placeholderTrackerTextField" = "Enter the name of the tracker"; +"textCancelButton" = "Cancel"; +"textGreateButton" = "To create"; +"characterLimit" = "Limit of 38 characters"; +"everyDay" = "Every day"; + +//MARK: - ScheduleViewController +"scheduleLableText" = "Schedule"; +"doneButtonText" = "Done"; + +//MARK: - EventSelectionViewController +"texGreatetLabelName" = "Creating a tracker"; +"newHabit" = "A new habit"; +"newIrregular" = "New irregular event"; + +//MARK: - CreateCategoriesViewController +"categoriLabelText" = "Category"; +"categoriAddButtonText" = "Add a categorycategory"; +"labelStabText" = "Habits and events can be combined in meaning"; + +//MARK: - NewCategoriViewController +"newcategoriLabelText" = "New category"; +"buttonName" = "Done"; +"placeholderCategoryTextField" = "Enter the category name"; + +//MARK: - NameEvent +"nameHabit" = "Habit"; +"nameIrregularEvent" = "IrregularEvent"; + +//MARK: - Header +"stringColor" = "Color"; + +//MARK: - ChoiceParametrs +"category" = "Category"; +"schedule" = "Schedule"; + +//MARK: - WeekDay +"monday.complete" = "Monday"; +"tuesday.complete" = "Tuesday"; +"wednesday.complete" = "Wednesday"; +"thursday.complete" = "Thursday"; +"friday.complete" = "Friday"; +"saturday.complete" = "Saturday"; +"sunday.complete" = "Sunday"; + +"monday" = "Mon"; +"tuesday" = "Tue"; +"wednesday" = "Wed"; +"thursday" = "Thur"; +"friday" = "Fri"; +"saturday" = "Sat"; +"sunday" = "Sun"; diff --git a/Tracker/Resources/en.lproj/Localizable.stringsdict b/Tracker/Resources/en.lproj/Localizable.stringsdict new file mode 100644 index 0000000..bb66e00 --- /dev/null +++ b/Tracker/Resources/en.lproj/Localizable.stringsdict @@ -0,0 +1,36 @@ + + + + + numberOfDay + + StringKey + + %#@days@ + + NSStringLocalizedFormatKey + + VARIABLE + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + + zero + + New item + + one + + two + + few + + many + + other + + + + + diff --git a/Tracker/Resources/en.lproj/LocalizableDict.stringsdict b/Tracker/Resources/en.lproj/LocalizableDict.stringsdict new file mode 100644 index 0000000..7ccb729 --- /dev/null +++ b/Tracker/Resources/en.lproj/LocalizableDict.stringsdict @@ -0,0 +1,24 @@ + + + + + numberOfDays + + NSStringLocalizedFormatKey + %#@days@ + days + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + zero + %d days + one + 1 day + other + %d days + + + + diff --git a/Tracker/Resources/ru.lproj/InfoPlist.strings b/Tracker/Resources/ru.lproj/InfoPlist.strings new file mode 100644 index 0000000..31acaad --- /dev/null +++ b/Tracker/Resources/ru.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* + InfoPlist.strings + Tracker + + Created by Марина Машук on 14.11.23. + +*/ + +"CFBundleDisplayName" = "Трекер"; diff --git a/Tracker/Resources/ru.lproj/Localizable.strings b/Tracker/Resources/ru.lproj/Localizable.strings new file mode 100644 index 0000000..464b8d5 --- /dev/null +++ b/Tracker/Resources/ru.lproj/Localizable.strings @@ -0,0 +1,82 @@ +/* + Localizable.strings + Tracker + + Created by Григорий Машук on 12.11.23. + +*/ + +//MARK: - Onboarding +"textLableOneVC" = "Отслеживайте только то, что хотите"; +"textLableTwoVC" = "Даже если это не литры воды и йога"; +"textButton" = "Вот это технологии!"; + +//MARK: - TabBarController +"trackers" = "Трекеры"; +"statistics" = "Статистика"; + +//MARK: - TrackersViewController +"headerText" = "Трекеры"; +"labelStabText" = "Что будем отслеживать?"; +"labelNothingFoundText" = "Ничего не найдено"; +"placeholderSearch" = "Поиск"; +"textCancel" = "Отменить"; +"textFix" = "Закрепить"; +"textUnpin" = "Открепить"; +"textEdit" = "Редактировать"; +"textDelete" = "Удалить"; +"textFixed" = "Закрепленные"; + +//MARK: - CreateTrackerViewController +"placeholderTrackerTextField" = "Введите название трекера"; +"textCancelButton" = "Отменить"; +"textGreateButton" = "Создать"; +"characterLimit" = "Ограничение 38 символов"; +"everyDay" = "Каждый день"; + +//MARK: - ScheduleViewController +"scheduleLableText" = "Расписание"; +"doneButtonText" = "Готово"; + +//MARK: - EventSelectionViewController +"texGreatetLabelName" = "Создание трекера"; +"newHabit" = "Новая привычка"; +"newIrregular" = "Новое нерегулярное событие"; + +//MARK: - CreateCategoriesViewController +"categoriLabelText" = "Категория"; +"categoriAddButtonText" = "Добавить категорию"; +"labelStabText" = "Привычки и события можно обЪединить по смыслу"; + +//MARK: - NewCategoriViewController +"newcategoriLabelText" = "Новая категория"; +"buttonName" = "Готово"; +"placeholderCategoryTextField" = "Введите название категории"; + +//MARK: - NameEvent +"nameHabit" = "Привычка"; +"nameIrregularEvent" = "Нерегулярное событие"; + +//MARK: - Header +"stringColor" = "Цвет"; + +//MARK: - ChoiceParametrs +"category" = "Категория"; +"schedule" = "Расписание"; + +//MARK: - WeekDay +"monday.complete" = "Понедельник"; +"tuesday.complete" = "Вторник"; +"wednesday.complete" = "Среда"; +"thursday.complete" = "Четверг"; +"friday.complete" = "Пятница"; +"saturday.complete" = "Суббота"; +"sunday.complete" = "Воскресенье"; + +"monday" = "Пн"; +"tuesday" = "Вт"; +"wednesday" = "Ср"; +"thursday" = "Чт"; +"friday" = "Пт"; +"saturday" = "Сб"; +"sunday" = "Вс"; diff --git a/Tracker/Resources/ru.lproj/Localizable.stringsdict b/Tracker/Resources/ru.lproj/Localizable.stringsdict new file mode 100644 index 0000000..e4f12bd --- /dev/null +++ b/Tracker/Resources/ru.lproj/Localizable.stringsdict @@ -0,0 +1,39 @@ + + + + + numberOfDay + + StringKey + + NSStringLocalizedFormatKey + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + other + + + VARIABLE + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + + zero + + New item + + one + + two + + few + + many + + other + + + + + diff --git a/Tracker/Resources/ru.lproj/LocalizableDict.stringsdict b/Tracker/Resources/ru.lproj/LocalizableDict.stringsdict new file mode 100644 index 0000000..09005a6 --- /dev/null +++ b/Tracker/Resources/ru.lproj/LocalizableDict.stringsdict @@ -0,0 +1,30 @@ + + + + + numberOfDays + + NSStringLocalizedFormatKey + %#@days@ + days + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + zero + %d дней + one + %d день + two + %d дня + few + %d дня + many + %d дней + other + %d дня + + + + diff --git a/Tracker/TabBarController/TabBarController.swift b/Tracker/TabBarController/TabBarController.swift index 0af66ec..e6d5425 100644 --- a/Tracker/TabBarController/TabBarController.swift +++ b/Tracker/TabBarController/TabBarController.swift @@ -9,29 +9,49 @@ import UIKit //MARK: - TabBarController final class TabBarController: UITabBarController { - private let tabBarImageTrecker = "tabBarTracker" - private let tabBarImageStatistic = "tabBarStatistic" + private struct ConstantsTabBar { + static let tabBarImageTrecker = "tabBarTracker" + static let tabBarImageStatistic = "tabBarStatistic" + + static let tabBarTitleTrecker = NSLocalizedString("trackers", comment: "") + static let tabBarTitleStatistic = NSLocalizedString("statistics", comment: "") + + static let insertImage = UIEdgeInsets(top: 5, left: 0, bottom: 0, right: 0) + } override func viewDidLoad() { super.viewDidLoad() setupTabBar() let trackerViewController = TrackersViewController() let statisticViewController = StatisticViewController() + let trackerNavigationController = UINavigationController(rootViewController: trackerViewController) let statisticNavigationController = UINavigationController(rootViewController: statisticViewController) viewControllers = [ - generateViewController(vc: trackerNavigationController, imageName: tabBarImageTrecker), - generateViewController(vc: statisticNavigationController, imageName: tabBarImageStatistic) + generateViewController(vc: trackerNavigationController, + imageName: ConstantsTabBar.tabBarImageTrecker, + title: ConstantsTabBar.tabBarTitleTrecker, + insert: ConstantsTabBar.insertImage), + + generateViewController(vc: statisticNavigationController, + imageName: ConstantsTabBar.tabBarImageStatistic, + title: ConstantsTabBar.tabBarTitleStatistic, + insert: ConstantsTabBar.insertImage) ] } } -//MARK: - TabBarController +//MARK: - Setup private extension TabBarController { - func generateViewController(vc: UIViewController, imageName: String) -> UIViewController { + func generateViewController(vc: UIViewController, + imageName: String, + title: String, + insert: UIEdgeInsets ) -> UIViewController + { vc.tabBarItem.image = UIImage(named: imageName) - vc.tabBarItem.imageInsets = UIEdgeInsets(top: 5, left: 0, bottom: 0, right: 0) + vc.tabBarItem.title = title + vc.tabBarItem.imageInsets = insert return vc } diff --git a/Tracker/TabBarController/TrackersViews/Cell/ColorCollectionViewCell.swift b/Tracker/TabBarController/TrackersViews/Cell/ColorCollectionViewCell.swift deleted file mode 100644 index bc1ac6c..0000000 --- a/Tracker/TabBarController/TrackersViews/Cell/ColorCollectionViewCell.swift +++ /dev/null @@ -1,31 +0,0 @@ -// -// ColorCollectionViewCell.swift -// Tracker -// -// Created by Григорий Машук on 4.10.23. -// - -import UIKit - -//MARK: - ColorCollectionViewCell -final class ColorCollectionViewCell: UICollectionViewCell { - - override init(frame: CGRect) { - super.init(frame: frame) - setupColorView() - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -extension ColorCollectionViewCell { - //MARK: - Config - - - //MARK: - SetupUI - private func setupColorView() { - - } -} diff --git a/Tracker/TabBarController/TrackersViews/Cell/CreateTrackerTableViewCell.swift b/Tracker/TabBarController/TrackersViews/Cell/CreateTrackerTableViewCell.swift index 54a0f7b..d7b63b2 100644 --- a/Tracker/TabBarController/TrackersViews/Cell/CreateTrackerTableViewCell.swift +++ b/Tracker/TabBarController/TrackersViews/Cell/CreateTrackerTableViewCell.swift @@ -13,7 +13,6 @@ class CreateTrackerTableViewCell: UITableViewCell { static let iconButton = "IconButtonCell" static let lableFont = UIFont.systemFont(ofSize: 17, weight: .regular) static let choiceButtonSize = CGSize(width: 44, height: 44) -// static let cornerRadiusViewCell = CGFloat(16) } private lazy var lableView: UILabel = { @@ -69,7 +68,6 @@ extension CreateTrackerTableViewCell { private func setupSelf() { backgroundColor = .backgroundNight -// layer.cornerRadius = ConstantsCreateCell.cornerRadiusViewCell layer.masksToBounds = true clickImage.translatesAutoresizingMaskIntoConstraints = false contentView.addSubview(clickImage) diff --git a/Tracker/TabBarController/TrackersViews/ViewControllers/EventSelectionViewController.swift b/Tracker/TabBarController/TrackersViews/Cell/EventSelectionViewController.swift similarity index 98% rename from Tracker/TabBarController/TrackersViews/ViewControllers/EventSelectionViewController.swift rename to Tracker/TabBarController/TrackersViews/Cell/EventSelectionViewController.swift index 5bb1b24..2996e68 100644 --- a/Tracker/TabBarController/TrackersViews/ViewControllers/EventSelectionViewController.swift +++ b/Tracker/TabBarController/TrackersViews/Cell/EventSelectionViewController.swift @@ -18,7 +18,7 @@ protocol EventSelectionViewControllerDelegate: AnyObject { //MARK: - EventSelectionViewController final class EventSelectionViewController: UIViewController { private struct ConstantsEventVc { - static let texGreatetLabelName = "Создание трекера" + static let texGreatetLabelName = NSLocalizedString("texGreatetLabelName", comment: "") static let textFont = UIFont.systemFont(ofSize: 16, weight: .medium) static let cornerRadius = CGFloat(16) static let lableGreateImage = UIColor(named: "blackDay") diff --git a/Tracker/TabBarController/TrackersViews/Cell/TrackersCollectionViewCell.swift b/Tracker/TabBarController/TrackersViews/Cell/TrackersCollectionViewCell.swift index c2334c9..ccaccf4 100644 --- a/Tracker/TabBarController/TrackersViews/Cell/TrackersCollectionViewCell.swift +++ b/Tracker/TabBarController/TrackersViews/Cell/TrackersCollectionViewCell.swift @@ -17,24 +17,24 @@ final class TrackersCollectionViewCell: UICollectionViewCell { private struct ConstantsTrackerCell { static let addButtonImageAdd = "Add" static let adButttonImageDone = "Done" - + static let isPinnedImage = "IsPinned" + static let cornerRadiusColorView = CGFloat(16) static let borderWidthColorView = CGFloat(1) - + static let fontLabelEmoji = UIFont.systemFont(ofSize: 16, weight: .medium) static let fontTextLable = UIFont.systemFont(ofSize: 12, weight: .medium) static let fontLableDayCounter = UIFont.systemFont(ofSize: 12, weight: .medium) - + static let sizeLableView = CGSize(width: 34, height: 34) static let sizeAddButton = CGSize(width: 34, height: 34) + static let sizeIsPinnedImage = CGSize(width: 24, height: 24) } - private var count: Int = 0 - private var isSelectedAddButton: Bool = false - weak var delegate: TrackersCollectionViewCellDelegate? + private var isSelectedAddButton: Bool = false - lazy var colorView: UIView = { + private lazy var colorView: UIView = { let colorView = UIView() colorView.layer.masksToBounds = true colorView.layer.cornerRadius = ConstantsTrackerCell.cornerRadiusColorView @@ -44,7 +44,7 @@ final class TrackersCollectionViewCell: UICollectionViewCell { return colorView }() - lazy var emojiLable: UILabel = { + private lazy var emojiLable: UILabel = { let emojiLable = UILabel() emojiLable.font = ConstantsTrackerCell.fontLabelEmoji emojiLable.layer.cornerRadius = ConstantsTrackerCell.cornerRadiusColorView @@ -55,7 +55,15 @@ final class TrackersCollectionViewCell: UICollectionViewCell { return emojiLable }() - lazy var nameTrackerLabel: UILabel = { + private lazy var isPinnedImageView: UIImageView = { + let isPinnedImageView = UIImageView() + isPinnedImageView.backgroundColor = .clear + isPinnedImageView.image = UIImage(named: ConstantsTrackerCell.isPinnedImage) + + return isPinnedImageView + }() + + private lazy var nameTrackerLabel: UILabel = { let nameTrackerLabel = UILabel() nameTrackerLabel.numberOfLines = 2 nameTrackerLabel.font = ConstantsTrackerCell.fontTextLable @@ -65,19 +73,16 @@ final class TrackersCollectionViewCell: UICollectionViewCell { return nameTrackerLabel }() - lazy var dayCounterLable: UILabel = { + private lazy var dayCounterLable: UILabel = { let dayCounterLable = UILabel() dayCounterLable.textColor = .blackDay dayCounterLable.font = ConstantsTrackerCell.fontLableDayCounter - dayCounterLable.text = "\(count) дней" return dayCounterLable }() - lazy var addButton: UIButton = { + private lazy var addButton: UIButton = { let addButton = UIButton(type: .custom) - let image = UIImage(named: ConstantsTrackerCell.addButtonImageAdd)?.withRenderingMode(.alwaysTemplate) - addButton.setImage(image, for: .normal) addButton.tintColor = .whiteDay addButton.backgroundColor = .lightGray addButton.layer.cornerRadius = ConstantsTrackerCell.sizeAddButton.width / 2 @@ -122,7 +127,7 @@ final class TrackersCollectionViewCell: UICollectionViewCell { } required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") + super.init(coder: coder) } } @@ -131,38 +136,25 @@ extension TrackersCollectionViewCell { @objc private func didTapAddButton() { guard let delegate else { return } - delegate.didTrackerCompleted(self) + delegate.didTrackerCompleted(self) } //MARK: - Configuration - //метод правильного окончания слова "день" в зависимости от значения count - func endingWordDay(count: Int) -> String { - var result: String - switch (count % 10) { - case 1: result = "\(count) день" - case 2: result = "\(count) дня" - case 3: result = "\(count) дня" - case 4: result = "\(count) дня" - default: result = "\(count) дней" - } - return result - } //метод обновления счетчика выполнения трекера и imageButton - func updateLableCountAndImageAddButton(count: Int, flag: Bool) { - switch flag { + func updateLableCountAndImageAddButton(_ updateModel: UpdateTracker) { + let textLable = String.localizedStringWithFormat(NSLocalizedString("numberOfDays", + tableName: "LocalizableDict", + comment: ""), + updateModel.count) + dayCounterLable.text = textLable + switch updateModel.flag { case true: let image = UIImage(named: ConstantsTrackerCell.adButttonImageDone)?.withRenderingMode(.alwaysTemplate) addButton.setImage(image, for: .normal) - self.count = count - let textLable = endingWordDay(count: count) - dayCounterLable.text = textLable case false: let image = UIImage(named: ConstantsTrackerCell.addButtonImageAdd)?.withRenderingMode(.alwaysTemplate) addButton.setImage(image, for: .normal) - self.count = count - let textLable = endingWordDay(count: count) - dayCounterLable.text = textLable } } @@ -171,20 +163,27 @@ extension TrackersCollectionViewCell { backgroundAddButtonView.isHidden = isHidden } + func setPinned(flag: Bool) { + isPinnedImageView.isHidden = flag + } + func isEnableAddButton(flag: Bool) { addButton.isEnabled = flag } - func setIsSelectedAddButton(flag: Bool) { + + private func setIsSelectedAddButton(flag: Bool) { isSelectedAddButton = flag } - //метод конфигурации ячейки - func config(tracker: Tracker, count: Int, isComplited: Bool) { + func config(tracker: Tracker) { colorView.backgroundColor = tracker.color emojiLable.text = tracker.emoji addButton.backgroundColor = tracker.color nameTrackerLabel.text = tracker.name - updateLableCountAndImageAddButton(count: count, flag: isComplited) + } + + func getView() -> UIView { + colorView } //MARK: - SetupUI @@ -239,13 +238,12 @@ extension TrackersCollectionViewCell { } private func setupColorView() { - [emojiLable, nameTrackerLabel].forEach { + [emojiLable, nameTrackerLabel, isPinnedImageView].forEach { $0.translatesAutoresizingMaskIntoConstraints = false colorView.addSubview($0) } NSLayoutConstraint.activate([ - emojiLable.topAnchor.constraint(equalTo: colorView.topAnchor, constant: 12), emojiLable.leadingAnchor.constraint(equalTo: colorView.leadingAnchor, constant: 12), emojiLable.heightAnchor.constraint(equalToConstant: ConstantsTrackerCell.sizeLableView.height), @@ -253,7 +251,12 @@ extension TrackersCollectionViewCell { nameTrackerLabel.leadingAnchor.constraint(equalTo: colorView.leadingAnchor, constant: 12), nameTrackerLabel.trailingAnchor.constraint(equalTo: colorView.trailingAnchor, constant: -12), - nameTrackerLabel.bottomAnchor.constraint(equalTo: colorView.bottomAnchor, constant: -12) + nameTrackerLabel.bottomAnchor.constraint(equalTo: colorView.bottomAnchor, constant: -12), + + isPinnedImageView.topAnchor.constraint(equalTo: colorView.topAnchor, constant: 12), + isPinnedImageView.trailingAnchor.constraint(equalTo: colorView.trailingAnchor), + isPinnedImageView.heightAnchor.constraint(equalToConstant: ConstantsTrackerCell.sizeIsPinnedImage.height), + isPinnedImageView.widthAnchor.constraint(equalToConstant: ConstantsTrackerCell.sizeIsPinnedImage.width) ]) } } diff --git a/Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift b/Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift index 1183677..3f298b8 100644 --- a/Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift +++ b/Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift @@ -15,23 +15,28 @@ protocol CreateCategoriesViewControllerDelegate: AnyObject { //MARK: - CreateCategoriesViewController final class CreateCategoriesViewController: UIViewController { private struct ConstantsCreateCatVc { - static let categoriLabelText = "Категория" - static let categoriAddButtonText = "Добавить категорию" + static let textFixed = NSLocalizedString("textFixed", comment: "") static let imageViewImageStab = "Star" - static let labelStabText = "Привычки и события можно обЪединить по смыслу" + static let alertActionErrorTitle = "Ok" + static let categoriLabelText = NSLocalizedString("categoriLabelText", comment: "") + static let categoriAddButtonText = NSLocalizedString("categoriAddButtonText", comment: "") + static let labelStabText = NSLocalizedString("labelStabText", comment: "") + + static let lableTextStabNumberOfLines = 2 + static let selectionCategoryTableViewRowHeight = CGFloat(75) static let cornerRadius = CGFloat(16) static let categoriLabelFont = UIFont.systemFont(ofSize: 16, weight: .medium) static let labelStabTextFont = UIFont.systemFont(ofSize: 12, weight: .medium) static let lableFont = UIFont.systemFont(ofSize: 17, weight: .regular) - static let insertSeparatorTableView = UIEdgeInsets(top: 0, left: 12, bottom: 0, right: 12) + static let insertSeparatorTableView = UIEdgeInsets(top: .zero, left: 12, bottom: .zero, right: 12) static let backgroundColorCell: UIColor = .backgroundNight } weak var delegate: CreateCategoriesViewControllerDelegate? - private var viewModel: CategoriViewModel? + private var viewModel: ViewModelProtocol? private lazy var categoriLabel: UILabel = { let categoriLabel = UILabel() @@ -56,7 +61,7 @@ final class CreateCategoriesViewController: UIViewController { let lableTextStab = UILabel() lableTextStab.text = ConstantsCreateCatVc.labelStabText lableTextStab.font = ConstantsCreateCatVc.labelStabTextFont - lableTextStab.numberOfLines = 2 + lableTextStab.numberOfLines = ConstantsCreateCatVc.lableTextStabNumberOfLines lableTextStab.textAlignment = .center return lableTextStab @@ -71,7 +76,7 @@ final class CreateCategoriesViewController: UIViewController { return conteinerStabView }() - private lazy var selectionCategoriTableView: UITableView = { + private lazy var selectionCategoryTableView: UITableView = { let selectionCategoriTableView = UITableView() selectionCategoriTableView.dataSource = self selectionCategoriTableView.delegate = self @@ -93,19 +98,22 @@ final class CreateCategoriesViewController: UIViewController { createCategoriButton.layer.masksToBounds = true createCategoriButton.translatesAutoresizingMaskIntoConstraints = false - createCategoriButton.addTarget(self, action: #selector(didTapСategoriButton), for: .touchUpInside) + createCategoriButton.addTarget(self, action: #selector(didTapСategoryButton), for: .touchUpInside) return createCategoriButton }() override func viewDidLoad() { super.viewDidLoad() - guard let viewModel else { return } + guard let viewModel = viewModel as? ViewModel else { return } bind() view.backgroundColor = .whiteDay setupUIElement() - showStabView(flag: !viewModel.category.isEmpty) - selectionCategoriTableView.rowHeight = 75 + resultTypeHandler(viewModel.category) { [weak self] cat in + guard let self else { return } + self.showStabView(flag: !cat.isEmpty) + } + selectionCategoryTableView.rowHeight = ConstantsCreateCatVc.selectionCategoryTableViewRowHeight let tapGesture = UITapGestureRecognizer(target: self, action: nil) tapGesture.cancelsTouchesInView = false @@ -114,13 +122,13 @@ final class CreateCategoriesViewController: UIViewController { } extension CreateCategoriesViewController { - func config(viewModel: CategoriViewModel?) { + func config(viewModel: ViewModel?) { self.viewModel = viewModel } //MARK: - обработка событий @objc - private func didTapСategoriButton() { + private func didTapСategoryButton() { let newCategoryVC = NewCategoriViewController() newCategoryVC.delegate = self newCategoryVC.modalPresentationStyle = .formSheet @@ -133,15 +141,43 @@ extension CreateCategoriesViewController { } private func bind() { - viewModel?.$category.bind { [weak self] _ in - guard let self, - let viewModel - else { return } - showStabView(flag: !viewModel.category.isEmpty) - selectionCategoriTableView.reloadData() + guard let viewModel = viewModel as? ViewModel else { return } + viewModel.$category.bind { [weak self] _ in + guard let self else { return } + resultTypeHandler(viewModel.category) { cat in + self.showStabView(flag: !cat.isEmpty) + } + selectionCategoryTableView.reloadData() } } - + + private func resultTypeHandler(_ value: Result, handler: (Value) -> Void) { + switch value { + case .success(let newValue): + handler(newValue) + case .failure(let error): + showMessageErrorAlert(message: error.localizedDescription) + } + } + + private func resultTypeHandlerGetValue(_ value: Result) -> Value? { + switch value { + case .success(let newValue): + return newValue + case .failure(let error): + showMessageErrorAlert(message: error.localizedDescription) + return nil + } + } + + private func showMessageErrorAlert(message: String) { + let alert = UIAlertController(title: nil, message: message, preferredStyle: .alert) + let action = UIAlertAction(title: ConstantsCreateCatVc.alertActionErrorTitle, style: .cancel) { _ in + alert.dismiss(animated: true) + } + alert.addAction(action) + } + //MARK: - SetupUI private func setupUIElement() { setupCategoriButton() @@ -172,14 +208,14 @@ extension CreateCategoriesViewController { } private func setupTableView() { - view.addSubview(selectionCategoriTableView) - selectionCategoriTableView.translatesAutoresizingMaskIntoConstraints = false + view.addSubview(selectionCategoryTableView) + selectionCategoryTableView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ - selectionCategoriTableView.topAnchor.constraint(equalTo: categoriLabel.bottomAnchor, constant: 24), - selectionCategoriTableView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16), - selectionCategoriTableView.trailingAnchor.constraint(equalTo: view.trailingAnchor,constant: -16), - selectionCategoriTableView.bottomAnchor.constraint(equalTo: createCategoriButton.topAnchor) + selectionCategoryTableView.topAnchor.constraint(equalTo: categoriLabel.bottomAnchor, constant: 24), + selectionCategoryTableView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16), + selectionCategoryTableView.trailingAnchor.constraint(equalTo: view.trailingAnchor,constant: -16), + selectionCategoryTableView.bottomAnchor.constraint(equalTo: createCategoriButton.topAnchor) ]) } @@ -208,35 +244,37 @@ extension CreateCategoriesViewController { //MARK: - UITableViewDataSource extension CreateCategoriesViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - guard let viewModel else { return 0 } - return viewModel.category.count + guard let viewModel, + let count = resultTypeHandlerGetValue(viewModel.getCategory())?.count + else { return .zero } + return count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { guard let cell = tableView.dequeueReusableCell(withIdentifier: "\(CreateCategoriesTableViewCell.self)") as? CreateCategoriesTableViewCell, - let viewModel + let viewModel, + let isSelected = resultTypeHandlerGetValue(viewModel.isCategorySelected(at: indexPath.row)), + let text = resultTypeHandlerGetValue(viewModel.createNameCategory(at: indexPath.row)), + let count = resultTypeHandlerGetValue(viewModel.getCategory())?.count else { return UITableViewCell() } - let isSelected = viewModel.isCategorySelected(at: indexPath.row) - let text = viewModel.createNameCategory(at: indexPath.row) cell.showSelectedImage(flag: isSelected) let model = CreateCategoryCellModel(text: text, color: ConstantsCreateCatVc.backgroundColorCell) cell.config(model: model) - if viewModel.category.count > 1 { - cell.separatorInset = UIEdgeInsets(top: 0, left: 12, bottom: 0, right: 12) + if count > 1 { + cell.separatorInset = ConstantsCreateCatVc.insertSeparatorTableView } - if indexPath.row == viewModel.category.count - 1 { + if indexPath.row == count - 1 { cell.setupCornerRadius(cornerRadius: ConstantsCreateCatVc.cornerRadius, maskedCorners: [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]) - cell.separatorInset = UIEdgeInsets(top: 0, left: view.bounds.width, bottom: 0, right: 0) + cell.separatorInset = UIEdgeInsets(top: .zero, left: view.bounds.width, bottom: .zero, right: .zero) } - if indexPath.row == 0 { + if indexPath.row == .zero { cell.setupCornerRadius(cornerRadius: ConstantsCreateCatVc.cornerRadius, maskedCorners: [.layerMinXMinYCorner, .layerMaxXMinYCorner]) } - return cell } } @@ -249,21 +287,28 @@ extension CreateCategoriesViewController: UITableViewDelegate { let viewModel else { return } cell.showSelectedImage(flag: false) - viewModel.selectСategory(at: indexPath.row) - let nameCategory = viewModel.createNameCategory(at: indexPath.row) - delegate.createCategoriesViewController(vc: self, nameCategory: nameCategory) + resultTypeHandler(viewModel.selectСategory(at: indexPath.row)) {} + resultTypeHandler(viewModel.createNameCategory(at: indexPath.row)) { [weak self] cat in + guard let self else { return } + delegate.createCategoriesViewController(vc: self, nameCategory: cat) + } } } //MARK: - NewCategoriViewControllerDelegate extension CreateCategoriesViewController: NewCategoriViewControllerDelegate { func didNewCategoriName(_ vc: UIViewController, nameCategori: String) { - guard let viewModel else { return } + guard let viewModel, + let category = resultTypeHandlerGetValue(viewModel.getCategory()) + else { return } +// if category.contains(where: { $0.nameCategory == ConstantsCreateCatVc.textFixed}) { +// } else { +// resultTypeHandler(viewModel.addCategory(nameCategory: ConstantsCreateCatVc.textFixed)) {} +// } let nameFirstUppercased = nameCategori.lowercased().firstUppercased - if viewModel.category.filter({ $0.nameCategory == nameFirstUppercased }).count > 0 { + if let _ = category.filter({ $0.nameCategory == nameFirstUppercased }).first { return } - - try? viewModel.addCategory(nameCategory: nameFirstUppercased) + resultTypeHandler(viewModel.addCategory(nameCategory: nameFirstUppercased)) {} } } diff --git a/Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift b/Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift index 099f416..a9402ce 100644 --- a/Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift +++ b/Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift @@ -16,13 +16,13 @@ protocol CreateTrackerViewControllerDelegate: AnyObject { //MARK: - CreateTrackerViewController final class CreateTrackerViewController: UIViewController{ private struct ConstantsCreateVc { - static let newHabit = "Новая привычка" - static let newIrregular = "Новое нерегулярное событие" - static let placeholderTextField = "Введите название трекера" - static let textCancelButton = "Отменить" - static let textGreateButton = "Создать" - static let characterLimit = "Ограничение 38 символов" - static let everyDay = "Каждый день" + static let newHabit = NSLocalizedString("newHabit", comment: "") + static let newIrregular = NSLocalizedString("newIrregular", comment: "") + static let placeholderTrackerTextField = NSLocalizedString("placeholderTrackerTextField", comment: "") + static let textCancelButton = NSLocalizedString("textCancelButton", comment: "") + static let textGreateButton = NSLocalizedString("textGreateButton", comment: "") + static let characterLimit = NSLocalizedString("characterLimit", comment: "") + static let everyDay = NSLocalizedString("everyDay", comment: "") static let emptyString = "" static let restrictionsTextField = 38 @@ -97,7 +97,7 @@ final class CreateTrackerViewController: UIViewController{ private lazy var createNameTextField: UITextField = { let createNameTextField = UITextField() - createNameTextField.placeholder = ConstantsCreateVc.placeholderTextField + createNameTextField.placeholder = ConstantsCreateVc.placeholderTrackerTextField createNameTextField.font = ConstantsCreateVc.createNameTextFieldFont createNameTextField.indent(size: ConstantsCreateVc.leftIndentTextField) createNameTextField.backgroundColor = .backgroundNight @@ -329,6 +329,10 @@ extension CreateTrackerViewController { isSchedul = isSchedul ? !isSchedul : isSchedul } + func flowEditing(traker: Tracker) { + + } + func presentViewController(vc: UIViewController, modalStyle: UIModalPresentationStyle) { vc.modalPresentationStyle = modalStyle present(vc, animated: true) @@ -446,7 +450,7 @@ extension CreateTrackerViewController: UITableViewDelegate { switch listSettings[indexPath.row] { case .category: let greateCategoriViewController = CreateCategoriesViewController() - let viewModel = CategoriViewModel() + let viewModel = ViewModel() greateCategoriViewController.config(viewModel: viewModel) greateCategoriViewController.delegate = self presentViewController(vc: greateCategoriViewController, modalStyle: .formSheet) diff --git a/Tracker/TabBarController/TrackersViews/ViewControllers/NewCategoriViewController.swift b/Tracker/TabBarController/TrackersViews/ViewControllers/NewCategoriViewController.swift index f7d0de3..2477336 100644 --- a/Tracker/TabBarController/TrackersViews/ViewControllers/NewCategoriViewController.swift +++ b/Tracker/TabBarController/TrackersViews/ViewControllers/NewCategoriViewController.swift @@ -14,9 +14,9 @@ protocol NewCategoriViewControllerDelegate: AnyObject { //MARK: - NewCategoriViewController class NewCategoriViewController: UIViewController { private struct ConstantsNewCatVc { - static let newcategoriLabelText = "Новая категория" - static let buttonName = "Готово" - static let placeholderTextField = "Введите название категории" + static let newcategoriLabelText = NSLocalizedString("newcategoriLabelText", comment: "") + static let buttonName = NSLocalizedString("buttonName", comment: "") + static let placeholderCategoryTextField = NSLocalizedString("placeholderCategoryTextField", comment: "") static let cornerRadius = CGFloat(16) static let leftIndentTextField = CGFloat(12) @@ -61,7 +61,7 @@ class NewCategoriViewController: UIViewController { private lazy var createNameTextField: UITextField = { let createNameTextField = UITextField() createNameTextField.delegate = self - createNameTextField.placeholder = ConstantsNewCatVc.placeholderTextField + createNameTextField.placeholder = ConstantsNewCatVc.placeholderCategoryTextField createNameTextField.font = ConstantsNewCatVc.textFieldFont createNameTextField.indent(size: ConstantsNewCatVc.leftIndentTextField) createNameTextField.backgroundColor = .backgroundNight diff --git a/Tracker/TabBarController/TrackersViews/ViewControllers/ScheduleViewController.swift b/Tracker/TabBarController/TrackersViews/ViewControllers/ScheduleViewController.swift index ff893af..87456b1 100644 --- a/Tracker/TabBarController/TrackersViews/ViewControllers/ScheduleViewController.swift +++ b/Tracker/TabBarController/TrackersViews/ViewControllers/ScheduleViewController.swift @@ -14,8 +14,9 @@ protocol ScheduleViewControllerDelegate: AnyObject { //MARK: - ScheduleViewController final class ScheduleViewController: UIViewController { private struct ConstantsShedulVc { - static let scheduleLableText = "Расписание" - static let doneButtonText = "Готово" + static let scheduleLableText = NSLocalizedString("scheduleLableText", comment: "") + static let doneButtonText = NSLocalizedString("doneButtonText", comment: "") + static let spacingVerticalStack = CGFloat(20) static let cornerRadiusUIElement = CGFloat(16) static let heightCell = CGFloat(75) diff --git a/Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift b/Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift index 591947f..4553949 100644 --- a/Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift +++ b/Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift @@ -7,43 +7,36 @@ import UIKit -enum StoreError: Error { - case modelNotFound - case failedToLoadPersistentContainer(Error) - case failedToRecordModel(Error) - case failedToDeleteModel(Error) - case failedToUpdateModel(Error) -} - //MARK: - TrackersViewController final class TrackersViewController: UIViewController { private struct ConstantsTrackerVc { static let adButtonImageName = "Add" + static let isPinnedImage = "isPinned" static let imageStar = "Star" static let imageNothingFound = "NothingFound" - - static let headerText = "Трекеры" - static let labelStabText = "Что будем отслеживать?" - static let labelNothingFoundText = "Ничего не найдено" - static let placeholderSearch = "Поиск" - static let textCancel = "Отменить" static let forKeyTextCancel = "cancelButtonText" - static let rusLocale = "ru_Ru" + + static let headerText = NSLocalizedString("headerText", comment: "") + static let labelStabText = NSLocalizedString("labelStabText", comment: "") + static let labelNothingFoundText = NSLocalizedString("labelNothingFoundText", comment: "") + static let placeholderSearch = NSLocalizedString("placeholderSearch", comment: "") + static let textCancel = NSLocalizedString("textCancel", comment: "") + static let textFix = NSLocalizedString("textFix", comment: "") + static let textUnpin = NSLocalizedString("textUnpin", comment: "") + static let textEdit = NSLocalizedString("textEdit", comment: "") + static let textDelete = NSLocalizedString("textDelete", comment: "") + static let textFixed = NSLocalizedString("textFixed", comment: "") static let datePickerCornerRadius = CGFloat(8) static let heightCollectionView = CGFloat(148) static let fontLableTextStab = UIFont.systemFont(ofSize: 12, weight: .medium) static let fontLabelHeader = UIFont.boldSystemFont(ofSize: 34) + + static let firstWeekday = 2 } - private var flagStab: Bool = false - private var categories: [TrackerCategory] = [] - private var completedTrackers: Set = [] - private var currentDate: Date { datePicker.date } - private var visibleCategories: [TrackerCategory] = [] - - private var dataProvider: DataProvider? + private var viewModel: ViewModelProtocol? private lazy var horisontalStack: UIStackView = { let horisontalStack = UIStackView() @@ -74,10 +67,10 @@ final class TrackersViewController: UIViewController { let datePicker = UIDatePicker() datePicker.datePickerMode = .date datePicker.preferredDatePickerStyle = .compact - datePicker.locale = Locale(identifier: ConstantsTrackerVc.rusLocale) - datePicker.calendar.firstWeekday = 2 + datePicker.calendar.firstWeekday = ConstantsTrackerVc.firstWeekday datePicker.layer.cornerRadius = ConstantsTrackerVc.datePickerCornerRadius datePicker.layer.masksToBounds = true + datePicker.locale = .current datePicker.addTarget(self, action: #selector(actionForTapDatePicker), for: .valueChanged) return datePicker @@ -96,8 +89,11 @@ final class TrackersViewController: UIViewController { private lazy var trackerCollectionView: TrackerCollectionView = { let trackerCollectionView = greateTrackerCollectionView() + trackerCollectionView.delegate = self + trackerCollectionView.dataSource = self trackerCollectionView.backgroundColor = .clear trackerCollectionView.showsVerticalScrollIndicator = false + trackerCollectionView.allowsMultipleSelection = false registerCellAndHeader(collectionView: trackerCollectionView) return trackerCollectionView @@ -127,34 +123,58 @@ final class TrackersViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - dataProvider = DataProvider(delegate: self) - guard let dataProvider else { return } - completedTrackers = dataProvider.treckersRecords - categories = dataProvider.trackerCategory - visibleCategories = categories view.backgroundColor = .white setupUIElement() - showListTrackersForDay(trackerCategory: categories) + viewModel = ViewModel() + bind() + guard let viewModel = viewModel as? ViewModel else { return } + viewModel.getShowListTrackersForDay(date: datePicker.date) + guard let visibleCategories = resultTypeHandlerGetValue(viewModel.visibleCategory) + else { return } + self.showStabView(flag: !visibleCategories.isEmpty) } } -extension TrackersViewController { +private extension TrackersViewController { + func bind() { + guard let viewModel = viewModel as? ViewModel else { return } + viewModel.$category.bind { [weak self] _ in + guard let self else { return } + viewModel.getShowListTrackersForDay(date: self.datePicker.date) + } + + viewModel.$indexPath.bind { [weak self] indexPath in + guard let self else { return } + self.trackerCollectionView.reloadItems(at: [indexPath]) + } + + viewModel.$visibleCategory.bind { [weak self] result in + guard let self, + let cat = resultTypeHandlerGetValue(result) + else { return } + self.showStabView(flag: !cat.isEmpty) + self.trackerCollectionView.reloadData() + } + } + //MARK: - обработка событий @objc - private func actionForTapDatePicker() { + func actionForTapDatePicker() { + guard let viewModel else { return } chengeStab(text: ConstantsTrackerVc.labelStabText, nameImage: ConstantsTrackerVc.imageStar) - showListTrackersForDay(trackerCategory: categories) + viewModel.getShowListTrackersForDay(date: datePicker.date) + } @objc - private func didTapLeftNavBarItem() { + func didTapLeftNavBarItem() { let greateVC = EventSelectionViewController() greateVC.delegate = self greateVC.modalPresentationStyle = .formSheet present(greateVC, animated: true) } - private func showMessageErrorAlert(message: String) { + func showMessageErrorAlert(message: String) { let alert = UIAlertController(title: nil, message: message, preferredStyle: .alert) let action = UIAlertAction(title: "Ok", style: .cancel) { _ in alert.dismiss(animated: true) @@ -162,71 +182,12 @@ extension TrackersViewController { alert.addAction(action) } - //MARK: - Логика - - //метод показа трекеров после фильтрации по дням - private func showListTrackersForDay(trackerCategory: [TrackerCategory]) { - let listCategories = filterListTrackersWeekDay(trackerCategory: trackerCategory, date: currentDate) - guard let dataProvider else { return } - visibleCategories = dataProvider.trackerCategory - updateTrackerCollectionView(trackerCategory: listCategories) - } - - //метод обновления коллекции - private func updateTrackerCollectionView(trackerCategory: [TrackerCategory]) { - visibleCategories = trackerCategory - showStabView(flag: !trackerCategory.isEmpty) - trackerCollectionView.reloadData() - } - //метод изменения заглушки - private func chengeStab(text: String, nameImage: String) { + func chengeStab(text: String, nameImage: String) { lableTextStab.text = text imageViewStab.image = UIImage(named: nameImage) } - //метод сравнения дней недели - private func comparisonWeekDays(date: Date, day: WeekDay) -> Bool { - FormatDate.shared.greateWeekDayInt(date: date) == day.rawValue - } - - //метод отфильтровывает трекеры по заданному дню недели - private func filterListTrackersWeekDay(trackerCategory: [TrackerCategory], date: Date) -> [TrackerCategory] { - var listCategories: [TrackerCategory] = [] - for cat in 0.. [TrackerCategory] { - var listCategories: [TrackerCategory] = [] - categories.forEach { category in - let trackerList = category.arrayTrackers.filter { $0.name.lowercased().hasPrefix(word.lowercased()) } - if !trackerList.isEmpty { - listCategories.append( TrackerCategory(nameCategory: category.nameCategory, arrayTrackers: trackerList)) - } - } - return listCategories - } - //функция для сравнения двух дат, вернет true, если дата больше или равна текущей func dateComparison(date: Date, currentDate: Date) -> Bool { let result = Calendar.current.compare(date, to: currentDate, toGranularity: .day) @@ -243,13 +204,32 @@ extension TrackersViewController { } //метод показа/скрытия заглушки - private func showStabView(flag: Bool) { + func showStabView(flag: Bool) { [imageViewStab, lableTextStab].forEach { $0.isHidden = flag } } + + func resultTypeHandler(_ listCategory: Result, handler: (Value) -> Void) { + switch listCategory { + case .success(let newValue): + handler(newValue) + case .failure(let error): + showMessageErrorAlert(message: error.localizedDescription) + } + } + + func resultTypeHandlerGetValue(_ value: Result) -> Value? { + switch value { + case .success(let newValue): + return newValue + case .failure(let error): + showMessageErrorAlert(message: error.localizedDescription) + return nil + } + } //метод создания кастомной CollectionView - private func greateTrackerCollectionView() -> TrackerCollectionView { - let params = GeometricParams(cellCount: 2, leftInset: 0, rightInset: 0, cellSpacing: 12) + func greateTrackerCollectionView() -> TrackerCollectionView { + let params = GeometricParams(cellCount: 2, leftInset: .zero, rightInset: .zero, cellSpacing: 12) let layout = UICollectionViewFlowLayout() let trackerCollectionView = TrackerCollectionView(frame: .zero, collectionViewLayout: layout, @@ -257,7 +237,7 @@ extension TrackersViewController { return trackerCollectionView } - private func registerCellAndHeader(collectionView: TrackerCollectionView) { + func registerCellAndHeader(collectionView: TrackerCollectionView) { collectionView.register(TrackersCollectionViewCell.self, forCellWithReuseIdentifier: "\(TrackersCollectionViewCell.self)") collectionView.register(HeaderReusableView.self, @@ -266,14 +246,14 @@ extension TrackersViewController { } //MARK: - SetupUI - private func setupUIElement() { + func setupUIElement() { setupVerticalSteck() setupContentView() setupStabView() setupNavBarItem() } - private func setupNavBarItem() { + func setupNavBarItem() { guard let navBar = navigationController?.navigationBar, let topItem = navBar.topItem else { return } @@ -282,7 +262,7 @@ extension TrackersViewController { action: #selector(didTapLeftNavBarItem)) topItem.rightBarButtonItem = UIBarButtonItem(customView: datePicker) - topItem.leftBarButtonItem?.imageInsets = UIEdgeInsets(top: 0, left: -10, bottom: 0, right: 0) + topItem.leftBarButtonItem?.imageInsets = UIEdgeInsets(top: .zero, left: -10, bottom: .zero, right: .zero) topItem.leftBarButtonItem?.tintColor = .blackDay navBar.backgroundColor = .clear @@ -300,7 +280,7 @@ extension TrackersViewController { searcheController.searchBar.setValue(ConstantsTrackerVc.textCancel, forKey: ConstantsTrackerVc.forKeyTextCancel) } - private func setupContentView() { + func setupContentView() { trackerCollectionView.translatesAutoresizingMaskIntoConstraints = false contentView.addSubview(trackerCollectionView) @@ -310,12 +290,9 @@ extension TrackersViewController { trackerCollectionView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), trackerCollectionView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor) ]) - - trackerCollectionView.delegate = self - trackerCollectionView.dataSource = self } - private func setupVerticalSteck() { + func setupVerticalSteck() { view.addSubview(verticalStack) contentView.translatesAutoresizingMaskIntoConstraints = false contentView.backgroundColor = .clear @@ -329,7 +306,7 @@ extension TrackersViewController { ]) } - private func setupStabView() { + func setupStabView() { [imageViewStab, lableTextStab].forEach { view.addSubview($0) $0.translatesAutoresizingMaskIntoConstraints = false @@ -349,21 +326,28 @@ extension TrackersViewController { //MARK: - UICollectionViewDataSource extension TrackersViewController: UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - visibleCategories[section].arrayTrackers.count + guard let viewModel = viewModel as? ViewModel, + let itemsCount = resultTypeHandlerGetValue(viewModel.visibleCategory)?[section].arrayTrackers.count + else { return .zero } + return itemsCount } func numberOfSections(in collectionView: UICollectionView) -> Int { - visibleCategories.count + guard let viewModel = viewModel as? ViewModel, + let sectionCount = resultTypeHandlerGetValue(viewModel.visibleCategory)?.count + else { return .zero } + return sectionCount } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "\(TrackersCollectionViewCell.self)", for: indexPath) as? TrackersCollectionViewCell, - let dataProvider + let viewModel = viewModel as? ViewModel, + let visibleCategories = resultTypeHandlerGetValue(viewModel.visibleCategory) else { return UICollectionViewCell() } cell.delegate = self let tracker = visibleCategories[indexPath.section].arrayTrackers[indexPath.row] - let dateComparison = Calendar.current.compare(Date(), to: currentDate, toGranularity: .day) + let dateComparison = Calendar.current.compare(Date(), to: datePicker.date, toGranularity: .day) switch dateComparison { case .orderedAscending: cell.isEnableAddButton(flag: false) @@ -375,21 +359,86 @@ extension TrackersViewController: UICollectionViewDataSource { cell.isEnableAddButton(flag: true) cell.updateBackgraundAddButton(isHidden: true) } - - do { - let count = try dataProvider.loadTrackerRecord(id: tracker.id) - let isComplited = completedTrackers.contains (where: { $0.id == tracker.id && Calendar.current.isDate(currentDate, equalTo: $0.date, toGranularity: .day)}) - cell.config(tracker: tracker, count: count, isComplited: isComplited) - } - catch { - let errorCount = TrackrerRecordStoreError.loadTrackerRecord - showMessageErrorAlert(message: "\(String(describing: errorCount))") + cell.setPinned(flag: visibleCategories[indexPath.section].isPinned) + resultTypeHandler(viewModel.loadTrackerRecord(id: tracker.id)) { [weak self] count in + guard let self, + let isComplited = self.resultTypeHandlerGetValue(viewModel.getIsComplited(tracker: tracker, + date: self.datePicker.date)) + else { return } + let updateModel = UpdateTracker(count: count, flag: isComplited) + cell.config(tracker: tracker) + cell.updateLableCountAndImageAddButton(updateModel) } - return cell } } +extension TrackersViewController: UICollectionViewDelegate { + func collectionView(_ collectionView: UICollectionView, + contextMenuConfiguration configuration: UIContextMenuConfiguration, + highlightPreviewForItemAt indexPath: IndexPath) -> UITargetedPreview? { + guard let cell = trackerCollectionView.cellForItem(at: indexPath) as? TrackersCollectionViewCell + else { return nil } + let previewTarget = UIPreviewTarget(container: self.trackerCollectionView, center: cell.center) + return .init(view: cell.getView(), parameters: UIPreviewParameters(), target: previewTarget) + } + + func collectionView(_ collectionView: UICollectionView, + contextMenuConfigurationForItemsAt indexPaths: [IndexPath], + point: CGPoint) -> UIContextMenuConfiguration? { + guard let indexPath = indexPaths.first, + let viewModel = viewModel as? ViewModel, + let visibleCategories = resultTypeHandlerGetValue(viewModel.visibleCategory) + else { return nil } + let selectorCategory = visibleCategories[indexPath.section] + let selectedTracker = selectorCategory.arrayTrackers[indexPath.row] + let firstContextText = !selectorCategory.isPinned ? ConstantsTrackerVc.textUnpin : ConstantsTrackerVc.textFix + let secondContentText = ConstantsTrackerVc.textEdit + let thirdContentText = ConstantsTrackerVc.textDelete + return UIContextMenuConfiguration(actionProvider: { _ in + return UIMenu(children: [ + UIAction(title: firstContextText) { [weak self] _ in + guard let self, + let category = self.resultTypeHandlerGetValue(viewModel.getCategory()) + else { return } + let tracker = Tracker( id: selectedTracker.id, + name: selectedTracker.name, + color: selectedTracker.color, + emoji: selectedTracker.emoji, + schedule: selectedTracker.schedule) + if selectorCategory.isPinned { + resultTypeHandler(viewModel.addPinnedCategory(id: selectedTracker.id, nameCategory: selectorCategory.nameCategory)) {} + self.resultTypeHandler(viewModel.deleteTracker(tracker.id)) {} + let filterNameCategory = category.filter { $0.nameCategory == ConstantsTrackerVc.textFixed } + if let _ = filterNameCategory.first?.nameCategory { + self.resultTypeHandler(viewModel.addNewTracker(tracker, nameCategory: ConstantsTrackerVc.textFixed)) {} + } else { + self.resultTypeHandler(viewModel.addCategory(nameCategory: ConstantsTrackerVc.textFixed)) {} + self.resultTypeHandler(viewModel.addNewTracker(tracker, nameCategory: ConstantsTrackerVc.textFixed)) {} + } + } else { + if let nameCategory = resultTypeHandlerGetValue(viewModel.deleteAndGetPinnedCategory(id: selectedTracker.id)) { + if let category = resultTypeHandlerGetValue(viewModel.category)?.filter({ $0.nameCategory == nameCategory }).first { + self.resultTypeHandler(viewModel.addNewTracker(tracker, nameCategory: category.nameCategory)) {} + } else { + guard let nameCategory else { return } + self.resultTypeHandler(viewModel.addCategory(nameCategory: nameCategory)) {} + self.resultTypeHandler(viewModel.addNewTracker(tracker, nameCategory: nameCategory)) {} + } + } + self.resultTypeHandler(viewModel.deleteTracker(selectedTracker.id)) {} + } + }, + UIAction(title: secondContentText) { [weak self] _ in + guard let self else { return } + self.resultTypeHandler(viewModel.updateTracker(tracker: selectedTracker)) {} + }, + UIAction(title: thirdContentText, attributes: .destructive) { _ in } + ]) + }) + } +} + //MARK: - UICollectionViewDelegateFlowLayout extension TrackersViewController: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { @@ -406,6 +455,9 @@ extension TrackersViewController: UICollectionViewDelegateFlowLayout { } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize { + guard let viewModel = viewModel as? ViewModel, + let visibleCategories = resultTypeHandlerGetValue(viewModel.visibleCategory) + else { return CGSize() } let indexPath = IndexPath(row: visibleCategories.count, section: section) let headerView = self.collectionView(collectionView, viewForSupplementaryElementOfKind: UICollectionView.elementKindSectionHeader, at: indexPath) @@ -415,7 +467,10 @@ extension TrackersViewController: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { switch kind { case UICollectionView.elementKindSectionHeader: - guard let supplementary = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "\(HeaderReusableView.self)", for: indexPath) as? HeaderReusableView else { return UICollectionReusableView() } + guard let supplementary = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "\(HeaderReusableView.self)", for: indexPath) as? HeaderReusableView, + let viewModel = viewModel as? ViewModel, + let visibleCategories = resultTypeHandlerGetValue(viewModel.visibleCategory) + else { return UICollectionReusableView() } supplementary.label.text = visibleCategories[indexPath.section].nameCategory return supplementary default: @@ -428,65 +483,35 @@ extension TrackersViewController: UICollectionViewDelegateFlowLayout { extension TrackersViewController: TrackersCollectionViewCellDelegate { func didTrackerCompleted(_ cell: UICollectionViewCell) { guard let trackerCell = cell as? TrackersCollectionViewCell, - let indexPath = trackerCollectionView.indexPath(for: trackerCell) + let indexPath = trackerCollectionView.indexPath(for: trackerCell), + let viewModel = viewModel as? ViewModel, + let visibleCategories = resultTypeHandlerGetValue(viewModel.visibleCategory) else { return } - /* делаем проверку. Сравниваем свойство currentDate с текущей датой, если currentDate - больше чем текучая дата,отмечать трекер выполненным нельзя, выходим из функции */ let tracker = visibleCategories[indexPath.section].arrayTrackers[indexPath.row] - let recordTracker = completedTrackers.first(where: { $0.id == tracker.id && - Calendar.current.isDate($0.date, inSameDayAs: currentDate) }) - updateCompleted(recordTracker: recordTracker, - cell: trackerCell, - indexPath: indexPath, - flag: recordTracker == nil, - tracker: tracker) - } - - private func updateCompleted(recordTracker: TrackerRecord?, - cell: TrackersCollectionViewCell, - indexPath: IndexPath, - flag: Bool, - tracker: Tracker) { - if let recordTracker { - do { - try dataProvider?.deleteTrackerRecord(recordTracker) - } catch { - let deleteError = StoreError.failedToRecordModel(error) - showMessageErrorAlert(message: "\(deleteError)") - } - completedTrackers.remove(recordTracker) - trackerCollectionView.reloadItems(at: [indexPath]) - return - } - let newTracker = TrackerRecord(id: tracker.id, date: currentDate) - do { - try dataProvider?.addNewTrackerRecord(newTracker) - } catch { - let recordError = StoreError.failedToRecordModel(error) - showMessageErrorAlert(message: "\(recordError)") - } - completedTrackers.insert(newTracker) - trackerCollectionView.reloadItems(at: [indexPath]) + viewModel.setIndexPath(indexPath) + resultTypeHandler(viewModel.updateCompletedTrackers(tracker: tracker, date: datePicker.date)) {} } } //MARK: - UISearchResultsUpdating extension TrackersViewController: UISearchResultsUpdating { func updateSearchResults(for searchController: UISearchController) { - guard let word = searchController.searchBar.text else { return } + guard let word = searchController.searchBar.text, + let viewModel + else { return } if !word.isEmpty { - let newCategories = filterListTrackersName(trackerCategory: categories, word: word) - updateTrackerCollectionView(trackerCategory: newCategories) + guard let seachCategory = resultTypeHandlerGetValue(viewModel.filterListTrackersName(word: word)) else { return } + viewModel.getShowListTrackerSearchForName(seachCategory) return } if word.isEmpty { - showListTrackersForDay(trackerCategory: categories) + viewModel.getShowListTrackersForDay(date: datePicker.date) chengeStab(text: ConstantsTrackerVc.labelNothingFoundText, nameImage: ConstantsTrackerVc.imageNothingFound) } if !searchController.isActive { - showListTrackersForDay(trackerCategory: categories) + viewModel.getShowListTrackersForDay(date: datePicker.date) chengeStab(text: ConstantsTrackerVc.labelStabText, nameImage: ConstantsTrackerVc.imageStar) } } @@ -495,26 +520,11 @@ extension TrackersViewController: UISearchResultsUpdating { //MARK: - EventSelectionViewControllerDelegate extension TrackersViewController: EventSelectionViewControllerDelegate { func eventSelectionViewController(vc: UIViewController, nameCategories: String, tracker: Tracker) { - guard let dataProvider else { return } - do { - try dataProvider.addTracker(nameCategories, tracker: tracker) - } catch { - let addError = StoreError.failedToRecordModel(error) - showMessageErrorAlert(message: "\(addError)") - } + guard let viewModel else { return } + resultTypeHandler(viewModel.addNewTracker(tracker, nameCategory: nameCategories)) {} } func eventSelectionViewControllerDidCancel(_ vc: EventSelectionViewController) { vc.dismiss(animated: false) } } - -//MARK: - TrackerCategoryStoreDelegate -extension TrackersViewController: DataProviderDelegate { - func storeCategory(dataProvider store: DataProvider, indexPath: IndexPath) { - guard let dataProvider else { return } - visibleCategories = dataProvider.trackerCategory - categories = visibleCategories - showListTrackersForDay(trackerCategory: categories) - } -} diff --git a/Tracker/ViewModel/CategoriViewModel.swift b/Tracker/ViewModel/CategoriViewModel.swift deleted file mode 100644 index 7e25dd3..0000000 --- a/Tracker/ViewModel/CategoriViewModel.swift +++ /dev/null @@ -1,49 +0,0 @@ -// -// CategoriViewModel.swift -// Tracker -// -// Created by Григорий Машук on 2.11.23. -// - -import UIKit - -//MARK: - CategoriViewModel -final class CategoriViewModel { - @Observable<[TrackerCategory]> private(set) var category = [] - @UserDefaultsBacked(key: "select_name_category") var selectNameCategory - @Observable private(set) var categoryIndexPath = IndexPath() - - private var model: DataProvider? - - init() { - model = DataProvider(delegate: self) - category = model?.trackerCategory ?? [] - } -} - -extension CategoriViewModel { - func addCategory(nameCategory: String) throws { - guard let model else { return } - try model.addCategory(nameCategory: nameCategory) - } - - func selectСategory(at index: Int) { - selectNameCategory = category[index].nameCategory - } - - func createNameCategory(at index: Int) -> String { - category[index].nameCategory - } - - func isCategorySelected(at index: Int) -> Bool { - category[index].nameCategory != selectNameCategory - } -} - -//MARK: - DataProviderDelegate -extension CategoriViewModel: DataProviderDelegate { - func storeCategory(dataProvider: DataProvider, indexPath: IndexPath) { - category = dataProvider.trackerCategory - categoryIndexPath = indexPath - } -} diff --git a/Tracker/ViewModel/ViewModel.swift b/Tracker/ViewModel/ViewModel.swift new file mode 100644 index 0000000..9f9983e --- /dev/null +++ b/Tracker/ViewModel/ViewModel.swift @@ -0,0 +1,299 @@ +// +// CategoriViewModel.swift +// Tracker +// +// Created by Григорий Машук on 2.11.23. +// + +import Foundation + +protocol ViewModelProtocol { + func isCategorySelected(at index: Int) -> Result + func getCategory() -> Result<[TrackerCategory], Error> + func deleteTracker(_ id: UUID) -> Result + func createNameCategory(at index: Int) -> Result + func selectСategory(at index: Int) -> Result + func addCategory(nameCategory: String) -> Result + func deleteCategory(nameCategory: String) -> Result + + func treckersRecordsResult() -> Result, Error> + func loadTrackerRecord(id: UUID) -> Result + func updateCompletedTrackers(tracker: Tracker, date: Date) -> Result + + func addNewTracker(_ tracker: Tracker, nameCategory: String) -> Result + func updateTracker(tracker: Tracker) -> Result + + func getShowListTrackersForDay(date: Date) + func getShowListTrackerSearchForName(_ searchCategory: [TrackerCategory]) + func filterListTrackersName(word: String) -> Result<[TrackerCategory], Error> + + func setIndexPath(_ indexPath: IndexPath) + + func addPinnedCategory(id: UUID, nameCategory: String) -> Result + func deleteAndGetPinnedCategory(id: UUID) -> Result +} + +//MARK: - ViewModel +final class ViewModel { + @Observable> private(set) var category: Result<[TrackerCategory], Error> + @Observable private(set) var indexPath: IndexPath + @Observable> private(set) var visibleCategory: Result<[TrackerCategory], Error> + @UserDefaultsBacked(key: "select_name_category") private var selectNameCategory: String? + + private var completedTrackers: Result, Error> + + private let trackerCategoryStore: TrackerCategoryStoreProtocol + private let trackerRecordStore: TrackerRecordStoreProtocol + private let trackerStore: TrackerStoreProtocol + private let pinnedCategoryStore: PinnedCategoryStoreProtocol + + convenience init() { + let trackerCategoryStore = TrackerCategoryStore() + let trackerRecordStore = TrackerRecordStore() + let trackerStore = TrackerStore() + let pinnedCategoryStore = PinnedCategoryStore() + self.init(trackerCategoryStore: trackerCategoryStore, + trackerRecordStore: trackerRecordStore, + trackerStore: trackerStore, + pinnedCategoryStore: pinnedCategoryStore, + category: .success([]), + completedTrackers: .success(Set()), + visibleCategory: .success([]), + indexPath: IndexPath()) + trackerCategoryStore.delegate = self + trackerRecordStore.delegate = self + category = getCategory() + completedTrackers = treckersRecordsResult() + } + + init(trackerCategoryStore: TrackerCategoryStoreProtocol, + trackerRecordStore: TrackerRecordStoreProtocol, + trackerStore: TrackerStoreProtocol, + pinnedCategoryStore: PinnedCategoryStoreProtocol, + category: Result<[TrackerCategory], Error>, + completedTrackers: Result, Error>, + visibleCategory: Result<[TrackerCategory], Error>, + indexPath: IndexPath) + { + self.trackerCategoryStore = trackerCategoryStore + self.trackerRecordStore = trackerRecordStore + self.trackerStore = trackerStore + self.pinnedCategoryStore = pinnedCategoryStore + self.category = category + self.completedTrackers = completedTrackers + self.visibleCategory = visibleCategory + self.indexPath = indexPath + } +} +//MARK: - Extension +private extension ViewModel { + func category(from trackerCategoryCoreData: TrackerCategoryCoreData) throws -> TrackerCategory { + guard let nameCategory = trackerCategoryCoreData.nameCategory else { + throw StoreErrors.TrackrerCategoryStoreError.decodingErrorInvalidNameCategori + } + guard let arrayTrackersCoreData = trackerCategoryCoreData.trakers?.allObjects as? [TrackerCoreData] + else { throw StoreErrors.NSSetError.transformationErrorInvalid } + let result = trackerStore.getTrackers(arrayTrackersCoreData) + switch result { + case .success(let trackers): + return TrackerCategory(nameCategory: nameCategory, + arrayTrackers: trackers, + isPinned: trackerCategoryCoreData.isPinned) + case .failure(let error): + throw error + } + } + + func comparisonWeekDays(date: Date, day: WeekDay) -> Bool { + FormatDate.shared.greateWeekDayInt(date: date) == day.rawValue + } + + func filterListTrackersWeekDay(trackerCategory: [TrackerCategory], date: Date) -> [TrackerCategory] { + var listCategories: [TrackerCategory] = [] + for cat in .zero.. Result { + trackerRecordStore.updateTrackerRecord(trackerRecord) + } + +} + +//MARK: - CategoriViewModelProtocol +extension ViewModel: ViewModelProtocol { + func deleteCategory(nameCategory: String) -> Result { + trackerCategoryStore.deleteCategory(nameCategory: nameCategory) + } + + func deleteTracker(_ id: UUID) -> Result { + trackerStore.deleteTracker(id) + } + + func addCategory(nameCategory: String) -> Result { + trackerCategoryStore.addCategory(nameCategory) + } + + func isCategorySelected(at index: Int) -> Result { + switch category { + case .success(let category): + return .success(category[index].nameCategory != selectNameCategory) + case .failure(let error): + return .failure(error) + } + } + + func getCategory() -> Result<[TrackerCategory], Error> { + let trackerCategoryCoreData = trackerCategoryStore.getListTrackerCategoryCoreData() + guard let trackerCategoryCoreData else { return .failure(StoreErrors.TrackrerStoreError.getTrackerError) } + do { + let listCategory = try trackerCategoryCoreData.map ({ try category(from: $0) }) + return .success(listCategory) + } catch { + return .failure(error) + } + } + + func createNameCategory(at index: Int) -> Result { + switch category { + case .success(let category): + let name = category[index].nameCategory + return .success(name) + case .failure(let error): + return .failure(error) + } + } + + func selectСategory(at index: Int) -> Result { + switch category { + case .success(let category): + selectNameCategory = category[index].nameCategory + return .success(()) + case .failure(let error): + return .failure(error) + } + } + + func treckersRecordsResult() -> Result, Error> { + trackerRecordStore.treckersRecordsResult() + } + + func loadTrackerRecord(id: UUID) -> Result { + trackerRecordStore.loadTrackerRecord(id: id) + } + + func addNewTracker(_ tracker: Tracker, nameCategory: String) -> Result { + let trackerCoreData = trackerStore.addNewTracker(tracker, nameCategory: nameCategory) + return trackerCoreData + } + + func updateTracker(tracker: Tracker) -> Result { + trackerStore.updateTracker(tracker: tracker) + } + + func getShowListTrackersForDay(date: Date) { + switch category { + case .success(let trackerCategory): + let filterList = filterListTrackersWeekDay(trackerCategory: trackerCategory, date: date) + visibleCategory = .success(filterList) + case .failure(let error): + visibleCategory = .failure(error) + } + } + + func filterListTrackersName(word: String) -> Result<[TrackerCategory], Error> { + var listCategories: [TrackerCategory] = [] + switch category { + case .success(let cat): + cat.forEach { category in + let trackerList = category.arrayTrackers.filter { $0.name.lowercased().hasPrefix(word.lowercased()) } + if !trackerList.isEmpty { + listCategories.append(TrackerCategory(nameCategory: category.nameCategory, + arrayTrackers: trackerList, + isPinned: category.isPinned)) + } + } + return .success(listCategories) + case .failure(let error): + return .failure(error) + } + } + + func updateCompletedTrackers(tracker: Tracker, date: Date) -> Result { + switch completedTrackers { + case .success(let compTrack): + if let recordTracker = compTrack.first(where: { $0.id == tracker.id && + Calendar.current.isDate($0.date, inSameDayAs: date) }) { + return updateTrackerRecord(recordTracker) + } + return updateTrackerRecord(TrackerRecord(id: tracker.id, date: date)) + case .failure(let error): + return .failure(error) + } + } + + func getShowListTrackerSearchForName(_ searchCategory: [TrackerCategory]) { + visibleCategory = .success(searchCategory) + } + + func getIsComplited(tracker: Tracker, date: Date) -> Result{ + switch completedTrackers { + case .success(let compTrack): + return .success(compTrack.contains (where: { $0.id == tracker.id && Calendar.current.isDate(date, equalTo: $0.date, toGranularity: .day)})) + case .failure(let error): + return .failure(error) + } + } + + func setIndexPath(_ indexPath: IndexPath) { + self.indexPath = indexPath + } + + func addPinnedCategory(id: UUID, nameCategory: String) -> Result { + let pinnCategoryCoreData = pinnedCategoryStore.addPinnedCategory(nameCategory) + switch pinnCategoryCoreData { + case .success(let pinnCategoryCoreData): + return trackerStore.addPinnedCategory(id, pinnedCategory: pinnCategoryCoreData) + case .failure(let error): + return .failure(error) + } + } + + func deleteAndGetPinnedCategory(id: UUID) -> Result { + pinnedCategoryStore.deleteAndGetPinnedCategory(id) + } +} + +//MARK: - TrackerCategoryStoreDelegate +extension ViewModel: TrackerCategoryStoreDelegate { + func storeCategory(trackerCategoryStore: TrackerCategoryStoreProtocol) { + category = getCategory() + } +} + +//MARK: - TrackerRecordStoreDelegate +extension ViewModel: TrackerRecordStoreDelegate { + func trackerRecordStore(trackerRecordStore: TrackerRecordStoreProtocol) { + completedTrackers = trackerRecordStore.treckersRecordsResult() + self.indexPath = indexPath + } +} diff --git a/Tracker/ViewModel/trackersCellViewModel.swift b/Tracker/ViewModel/trackersCellViewModel.swift new file mode 100644 index 0000000..6274257 --- /dev/null +++ b/Tracker/ViewModel/trackersCellViewModel.swift @@ -0,0 +1,23 @@ +// +// ViewModelTrackersCell.swift +// Tracker +// +// Created by Григорий Машук on 5.12.23. +// + +import Foundation + +final class ViewModelTrackersCell { + @Observable private(set) var indexPath: IndexPath + @Observable, Error>> private(set) var trackerRecord: Result, Error> + + private let trackerRecordStore: TrackerRecordStore + + convenience init + + init(indexPath: IndexPath, trackerRecord: Result, Error>, trackerRecordStore: TrackerRecordStore) { + self.indexPath = indexPath + self.trackerRecord = trackerRecord + self.trackerRecordStore = trackerRecordStore + } +} diff --git a/Tracker/ru.lproj/LaunchScreen.strings b/Tracker/ru.lproj/LaunchScreen.strings new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/Tracker/ru.lproj/LaunchScreen.strings @@ -0,0 +1 @@ + From 363ca953bbf26a76def323ff339f1daf194102fe Mon Sep 17 00:00:00 2001 From: Grifon Date: Wed, 13 Dec 2023 14:09:43 +0300 Subject: [PATCH 02/24] =?UTF-8?q?=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xcshareddata/xcschemes/Tracker.xcscheme | 77 + .../xcdebugger/Breakpoints_v2.xcbkptlist | 5291 +++++++++++++++++ 2 files changed, 5368 insertions(+) create mode 100644 Tracker.xcodeproj/xcshareddata/xcschemes/Tracker.xcscheme create mode 100644 Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist diff --git a/Tracker.xcodeproj/xcshareddata/xcschemes/Tracker.xcscheme b/Tracker.xcodeproj/xcshareddata/xcschemes/Tracker.xcscheme new file mode 100644 index 0000000..9c67c1b --- /dev/null +++ b/Tracker.xcodeproj/xcshareddata/xcschemes/Tracker.xcscheme @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 0000000..4de1a4b --- /dev/null +++ b/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,5291 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 2120afd0e8e37fbbf2baa04cb457bd941a2a2100 Mon Sep 17 00:00:00 2001 From: Grifon Date: Sat, 16 Dec 2023 13:18:00 +0300 Subject: [PATCH 03/24] added tracker editing --- Tracker.xcodeproj/project.pbxproj | 34 +- .../xcdebugger/Breakpoints_v2.xcbkptlist | 2794 ++++++++++++++--- .../Domain/Store/TrackerCategoryStore.swift | 2 +- Tracker/Domain/Store/TrackerStore.swift | 107 +- Tracker/Helpers/HandlerResultType.swift | 37 + .../TabBarController/TabBarController.swift | 2 +- ...ll.swift => ColorCollectionViewCell.swift} | 37 +- .../Cell/CreateTrackerTableViewCell.swift | 3 +- .../Cell/EmojiCollectionViewCell.swift | 62 + .../Cell/TrackersCollectionViewCell.swift | 5 +- .../CreateCategoriesViewController.swift | 40 +- .../CreateTrackerViewController.swift | 336 +- .../EventSelectionViewController.swift | 18 +- .../ScheduleViewController.swift | 33 +- .../TrackersViewController.swift | 173 +- Tracker/ViewModel/CategoryViewModel.swift | 157 + Tracker/ViewModel/ShedulViewModel.swift | 26 + ...ViewModel.swift => TrackerViewModel.swift} | 26 +- 18 files changed, 3065 insertions(+), 827 deletions(-) create mode 100644 Tracker/Helpers/HandlerResultType.swift rename Tracker/TabBarController/TrackersViews/Cell/{EmojiColorCollectionViewCell.swift => ColorCollectionViewCell.swift} (64%) create mode 100644 Tracker/TabBarController/TrackersViews/Cell/EmojiCollectionViewCell.swift rename Tracker/TabBarController/TrackersViews/{Cell => ViewControllers}/EventSelectionViewController.swift (89%) create mode 100644 Tracker/ViewModel/CategoryViewModel.swift create mode 100644 Tracker/ViewModel/ShedulViewModel.swift rename Tracker/ViewModel/{ViewModel.swift => TrackerViewModel.swift} (93%) diff --git a/Tracker.xcodeproj/project.pbxproj b/Tracker.xcodeproj/project.pbxproj index f4fef63..cdbbb79 100644 --- a/Tracker.xcodeproj/project.pbxproj +++ b/Tracker.xcodeproj/project.pbxproj @@ -7,7 +7,7 @@ objects = { /* Begin PBXBuildFile section */ - 1A14106A2ADD5185001D6DE6 /* EmojiColorCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A1410692ADD5185001D6DE6 /* EmojiColorCollectionViewCell.swift */; }; + 1A14106A2ADD5185001D6DE6 /* ColorCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A1410692ADD5185001D6DE6 /* ColorCollectionViewCell.swift */; }; 1A19A7462B1672A200DD415B /* UpdateTrackerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A19A7452B1672A200DD415B /* UpdateTrackerModel.swift */; }; 1A1E94B52AD952EF00FE9A0C /* CreateCategoriesTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A1E94B42AD952EF00FE9A0C /* CreateCategoriesTableViewCell.swift */; }; 1A21DD952ADE5A6A00583A01 /* DaysValueTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A21DD942ADE5A6A00583A01 /* DaysValueTransformer.swift */; }; @@ -29,6 +29,9 @@ 1A598BB62AC6F352009B166D /* TabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A598BB52AC6F352009B166D /* TabBarController.swift */; }; 1A598BB82AC6FAC5009B166D /* TrackersViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A598BB72AC6FAC5009B166D /* TrackersViewController.swift */; }; 1A66BB192B25D16500838F5F /* PinnTrackerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A66BB182B25D16500838F5F /* PinnTrackerModel.swift */; }; + 1A704D3F2B2B76C60035C94D /* EmojiCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A704D3E2B2B76C60035C94D /* EmojiCollectionViewCell.swift */; }; + 1A760A9D2B29EDC900B0C1CC /* CategoryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A760A9C2B29EDC900B0C1CC /* CategoryViewModel.swift */; }; + 1A83156E2B2C786000097DE7 /* HandlerResultType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A83156D2B2C786000097DE7 /* HandlerResultType.swift */; }; 1A84F3792AC87F4E001DD42F /* GeometricParams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A84F3782AC87F4E001DD42F /* GeometricParams.swift */; }; 1A8A576C2ACD5F4D00C060FA /* CreateTrackerTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A8A576B2ACD5F4D00C060FA /* CreateTrackerTableViewCell.swift */; }; 1A8A576F2ACD65C400C060FA /* ChoiceParametrs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A8A576E2ACD65C400C060FA /* ChoiceParametrs.swift */; }; @@ -42,7 +45,7 @@ 1A8ED5122AEFFADF00BA4EE4 /* OnboardingModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A8ED5112AEFFADF00BA4EE4 /* OnboardingModel.swift */; }; 1A959AF42AE24C44001C0EAE /* UIColorMarshalling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A959AF32AE24C44001C0EAE /* UIColorMarshalling.swift */; }; 1A9AC5AF2AF373F000A9213D /* Observable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A9AC5AE2AF373F000A9213D /* Observable.swift */; }; - 1A9AC5B42AF37AED00A9213D /* ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A9AC5B32AF37AED00A9213D /* ViewModel.swift */; }; + 1A9AC5B42AF37AED00A9213D /* TrackerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A9AC5B32AF37AED00A9213D /* TrackerViewModel.swift */; }; 1A9E5C962AD2FF6100010C30 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A9E5C952AD2FF6100010C30 /* String.swift */; }; 1AA5B2372AD91D1800CB38A2 /* CreateCategoriesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AA5B2362AD91D1800CB38A2 /* CreateCategoriesViewController.swift */; }; 1AA5B2392AD9424C00CB38A2 /* NewCategoriViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AA5B2382AD9424C00CB38A2 /* NewCategoriViewController.swift */; }; @@ -60,10 +63,11 @@ 1AE9892D2ACAE76E00392315 /* WeekDay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AE9892C2ACAE76E00392315 /* WeekDay.swift */; }; 1AE989302ACAF14B00392315 /* TrackerCategoryModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AE9892F2ACAF14B00392315 /* TrackerCategoryModel.swift */; }; 1AE989322ACAF20500392315 /* TrackerRecordModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AE989312ACAF20500392315 /* TrackerRecordModel.swift */; }; + 1AF05E062B2B011D0099D022 /* ShedulViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AF05E052B2B011D0099D022 /* ShedulViewModel.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ - 1A1410692ADD5185001D6DE6 /* EmojiColorCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiColorCollectionViewCell.swift; sourceTree = ""; }; + 1A1410692ADD5185001D6DE6 /* ColorCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorCollectionViewCell.swift; sourceTree = ""; }; 1A19A7452B1672A200DD415B /* UpdateTrackerModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateTrackerModel.swift; sourceTree = ""; }; 1A1E94B42AD952EF00FE9A0C /* CreateCategoriesTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateCategoriesTableViewCell.swift; sourceTree = ""; }; 1A21DD942ADE5A6A00583A01 /* DaysValueTransformer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DaysValueTransformer.swift; sourceTree = ""; }; @@ -86,6 +90,9 @@ 1A598BB52AC6F352009B166D /* TabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarController.swift; sourceTree = ""; }; 1A598BB72AC6FAC5009B166D /* TrackersViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackersViewController.swift; sourceTree = ""; }; 1A66BB182B25D16500838F5F /* PinnTrackerModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinnTrackerModel.swift; sourceTree = ""; }; + 1A704D3E2B2B76C60035C94D /* EmojiCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiCollectionViewCell.swift; sourceTree = ""; }; + 1A760A9C2B29EDC900B0C1CC /* CategoryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CategoryViewModel.swift; sourceTree = ""; }; + 1A83156D2B2C786000097DE7 /* HandlerResultType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandlerResultType.swift; sourceTree = ""; }; 1A84F3782AC87F4E001DD42F /* GeometricParams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeometricParams.swift; sourceTree = ""; }; 1A8A576B2ACD5F4D00C060FA /* CreateTrackerTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateTrackerTableViewCell.swift; sourceTree = ""; }; 1A8A576E2ACD65C400C060FA /* ChoiceParametrs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChoiceParametrs.swift; sourceTree = ""; }; @@ -101,7 +108,7 @@ 1A8ED5112AEFFADF00BA4EE4 /* OnboardingModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingModel.swift; sourceTree = ""; }; 1A959AF32AE24C44001C0EAE /* UIColorMarshalling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIColorMarshalling.swift; sourceTree = ""; }; 1A9AC5AE2AF373F000A9213D /* Observable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Observable.swift; sourceTree = ""; }; - 1A9AC5B32AF37AED00A9213D /* ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModel.swift; sourceTree = ""; }; + 1A9AC5B32AF37AED00A9213D /* TrackerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerViewModel.swift; sourceTree = ""; }; 1A9E5C952AD2FF6100010C30 /* String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = ""; }; 1AA5B2362AD91D1800CB38A2 /* CreateCategoriesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateCategoriesViewController.swift; sourceTree = ""; }; 1AA5B2382AD9424C00CB38A2 /* NewCategoriViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewCategoriViewController.swift; sourceTree = ""; }; @@ -122,6 +129,7 @@ 1AE9892C2ACAE76E00392315 /* WeekDay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeekDay.swift; sourceTree = ""; }; 1AE9892F2ACAF14B00392315 /* TrackerCategoryModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerCategoryModel.swift; sourceTree = ""; }; 1AE989312ACAF20500392315 /* TrackerRecordModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerRecordModel.swift; sourceTree = ""; }; + 1AF05E052B2B011D0099D022 /* ShedulViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShedulViewModel.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -177,6 +185,7 @@ isa = PBXGroup; children = ( 1A4655892AD3D2B8006DE343 /* FormatDate.swift */, + 1A83156D2B2C786000097DE7 /* HandlerResultType.swift */, ); path = Helpers; sourceTree = ""; @@ -196,6 +205,7 @@ 1AAC05C22ACC595800AA4B6B /* CreateTrackerViewController.swift */, 1A3ED44B2ACDDE4A0006EBBB /* ScheduleViewController.swift */, 1AA5B2362AD91D1800CB38A2 /* CreateCategoriesViewController.swift */, + 1A2F9B642AC82FBF00914B60 /* EventSelectionViewController.swift */, 1AA5B2382AD9424C00CB38A2 /* NewCategoriViewController.swift */, ); path = ViewControllers; @@ -307,7 +317,9 @@ 1A9AC5B52AF3854A00A9213D /* ViewModel */ = { isa = PBXGroup; children = ( - 1A9AC5B32AF37AED00A9213D /* ViewModel.swift */, + 1A9AC5B32AF37AED00A9213D /* TrackerViewModel.swift */, + 1A760A9C2B29EDC900B0C1CC /* CategoryViewModel.swift */, + 1AF05E052B2B011D0099D022 /* ShedulViewModel.swift */, ); path = ViewModel; sourceTree = ""; @@ -329,8 +341,8 @@ 1AB78AA22AC750A900904AB2 /* TrackersCollectionViewCell.swift */, 1A3ED4512ACDEA3E0006EBBB /* WeekDayTableViewCell.swift */, 1A1E94B42AD952EF00FE9A0C /* CreateCategoriesTableViewCell.swift */, - 1A1410692ADD5185001D6DE6 /* EmojiColorCollectionViewCell.swift */, - 1A2F9B642AC82FBF00914B60 /* EventSelectionViewController.swift */, + 1A1410692ADD5185001D6DE6 /* ColorCollectionViewCell.swift */, + 1A704D3E2B2B76C60035C94D /* EmojiCollectionViewCell.swift */, ); path = Cell; sourceTree = ""; @@ -485,6 +497,7 @@ 1A45A67A2ADE8EDB0063F08C /* TrackerCategoryStore.swift in Sources */, 1ABABBF72AFC10AD007281E0 /* CreateCategoryCellModel.swift in Sources */, 1A32FF912ADC72880065E44C /* Header.swift in Sources */, + 1A760A9D2B29EDC900B0C1CC /* CategoryViewModel.swift in Sources */, 1A19A7462B1672A200DD415B /* UpdateTrackerModel.swift in Sources */, 1A8A7FE62AC55D970038CB42 /* AppDelegate.swift in Sources */, 1A3B83AA2AD56323003C3897 /* NameEvent.swift in Sources */, @@ -495,12 +508,13 @@ 1A3ED44C2ACDDE4A0006EBBB /* ScheduleViewController.swift in Sources */, 1A2980BD2AC864CB007FE528 /* UIColor.swift in Sources */, 1AAD15652AF14E220080EB72 /* UserDefaultsBacked.swift in Sources */, - 1A14106A2ADD5185001D6DE6 /* EmojiColorCollectionViewCell.swift in Sources */, + 1A83156E2B2C786000097DE7 /* HandlerResultType.swift in Sources */, + 1A14106A2ADD5185001D6DE6 /* ColorCollectionViewCell.swift in Sources */, 1A8A576F2ACD65C400C060FA /* ChoiceParametrs.swift in Sources */, 1ADD3D742ACBDBB1005F639F /* TrackerCollectionView.swift in Sources */, 1A9E5C962AD2FF6100010C30 /* String.swift in Sources */, 1A45A6752ADE8BCC0063F08C /* Model.xcdatamodeld in Sources */, - 1A9AC5B42AF37AED00A9213D /* ViewModel.swift in Sources */, + 1A9AC5B42AF37AED00A9213D /* TrackerViewModel.swift in Sources */, 1A8ED50F2AEFEB5A00BA4EE4 /* OnboardingViewController.swift in Sources */, 1A272CE42B27922A00D39A44 /* PinnedCategoryStore.swift in Sources */, 1AD277712ACD4B1800F92219 /* UITextField.swift in Sources */, @@ -519,6 +533,8 @@ 1AA5B2372AD91D1800CB38A2 /* CreateCategoriesViewController.swift in Sources */, 1A8A7FE82AC55D970038CB42 /* SceneDelegate.swift in Sources */, 1A27E6B92AD9871400ED3DF3 /* StringProtocol.swift in Sources */, + 1A704D3F2B2B76C60035C94D /* EmojiCollectionViewCell.swift in Sources */, + 1AF05E062B2B011D0099D022 /* ShedulViewModel.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index 4de1a4b..58149d2 100644 --- a/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -674,8 +674,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/ScheduleViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "141" - endingLineNumber = "141" + startingLineNumber = "149" + endingLineNumber = "149" landmarkName = "tableView(_:cellForRowAt:)" landmarkType = "7"> @@ -690,8 +690,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/ScheduleViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "172" - endingLineNumber = "172" + startingLineNumber = "179" + endingLineNumber = "179" landmarkName = "addDayInListkDay(cell:flag:)" landmarkType = "7"> @@ -706,8 +706,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/ScheduleViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "174" - endingLineNumber = "174" + startingLineNumber = "181" + endingLineNumber = "181" landmarkName = "addDayInListkDay(cell:flag:)" landmarkType = "7"> @@ -754,8 +754,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/ScheduleViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "175" - endingLineNumber = "175" + startingLineNumber = "182" + endingLineNumber = "182" landmarkName = "addDayInListkDay(cell:flag:)" landmarkType = "7"> @@ -802,8 +802,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/ScheduleViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "100" - endingLineNumber = "100" + startingLineNumber = "108" + endingLineNumber = "108" landmarkName = "updateListWeekDay(flag:day:)" landmarkType = "7"> @@ -818,8 +818,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/ScheduleViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "102" - endingLineNumber = "102" + startingLineNumber = "110" + endingLineNumber = "110" landmarkName = "updateListWeekDay(flag:day:)" landmarkType = "7"> @@ -834,8 +834,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/ScheduleViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "108" - endingLineNumber = "108" + startingLineNumber = "116" + endingLineNumber = "116" landmarkName = "updateListWeekDay(flag:day:)" landmarkType = "7"> @@ -866,8 +866,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "470" - endingLineNumber = "470" + startingLineNumber = "466" + endingLineNumber = "466" landmarkName = "collectionView(_:viewForSupplementaryElementOfKind:at:)" landmarkType = "7"> @@ -1064,8 +1064,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "468" - endingLineNumber = "468" + startingLineNumber = "464" + endingLineNumber = "464" landmarkName = "collectionView(_:viewForSupplementaryElementOfKind:at:)" landmarkType = "7"> @@ -1172,8 +1172,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "453" - endingLineNumber = "453" + startingLineNumber = "449" + endingLineNumber = "449" landmarkName = "collectionView(_:layout:insetForSectionAt:)" landmarkType = "7"> @@ -1188,8 +1188,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "446" - endingLineNumber = "446" + startingLineNumber = "442" + endingLineNumber = "442" landmarkName = "collectionView(_:layout:sizeForItemAt:)" landmarkType = "7"> @@ -1204,8 +1204,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "467" - endingLineNumber = "467" + startingLineNumber = "463" + endingLineNumber = "463" landmarkName = "collectionView(_:viewForSupplementaryElementOfKind:at:)" landmarkType = "7"> @@ -1312,8 +1312,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "243" - endingLineNumber = "243" + startingLineNumber = "231" + endingLineNumber = "231" landmarkName = "registerCellAndHeader(collectionView:)" landmarkType = "7"> @@ -1328,8 +1328,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "462" - endingLineNumber = "462" + startingLineNumber = "458" + endingLineNumber = "458" landmarkName = "collectionView(_:layout:referenceSizeForHeaderInSection:)" landmarkType = "7"> @@ -1439,10 +1439,10 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/EventSelectionViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "143" - endingLineNumber = "143" - landmarkName = "createTrackerViewControllerDidCancel(_:)" - landmarkType = "7"> + startingLineNumber = "144" + endingLineNumber = "144" + landmarkName = "EventSelectionViewController" + landmarkType = "21"> @@ -1519,8 +1519,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "502" - endingLineNumber = "502" + startingLineNumber = "484" + endingLineNumber = "484" landmarkName = "updateSearchResults(for:)" landmarkType = "7"> @@ -1693,9 +1693,9 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/EventSelectionViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "142" - endingLineNumber = "142" - landmarkName = "createTrackerViewControllerDidCancel(_:)" + startingLineNumber = "143" + endingLineNumber = "143" + landmarkName = "trackerViewControllerDidCancel(_:)" landmarkType = "7"> @@ -1803,8 +1803,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "509" - endingLineNumber = "509" + startingLineNumber = "491" + endingLineNumber = "491" landmarkName = "updateSearchResults(for:)" landmarkType = "7"> @@ -1841,22 +1841,6 @@ landmarkType = "7"> - - - - @@ -2185,8 +2169,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "576" - endingLineNumber = "576" + startingLineNumber = "566" + endingLineNumber = "566" landmarkName = "collectionView(_:viewForSupplementaryElementOfKind:at:)" landmarkType = "7"> @@ -2207,144 +2191,6 @@ landmarkType = "24"> - - - - - - - - - - - - - - - - - - - - - - @@ -3078,8 +2924,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "422" - endingLineNumber = "422" + startingLineNumber = "423" + endingLineNumber = "423" landmarkName = "tableView(_:cellForRowAt:)" landmarkType = "7"> @@ -3094,8 +2940,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/ScheduleViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "90" - endingLineNumber = "90" + startingLineNumber = "98" + endingLineNumber = "98" landmarkName = "didTapDoneButton()" landmarkType = "7"> @@ -3206,8 +3052,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "285" - endingLineNumber = "285" + startingLineNumber = "287" + endingLineNumber = "287" landmarkName = "tableView(_:didSelectRowAt:)" landmarkType = "7"> @@ -3284,8 +3130,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "284" - endingLineNumber = "284" + startingLineNumber = "286" + endingLineNumber = "286" landmarkName = "tableView(_:didSelectRowAt:)" landmarkType = "7"> @@ -3300,8 +3146,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "262" - endingLineNumber = "262" + startingLineNumber = "264" + endingLineNumber = "264" landmarkName = "tableView(_:cellForRowAt:)" landmarkType = "7"> @@ -3316,8 +3162,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "271" - endingLineNumber = "271" + startingLineNumber = "273" + endingLineNumber = "273" landmarkName = "tableView(_:cellForRowAt:)" landmarkType = "7"> @@ -3332,8 +3178,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "265" - endingLineNumber = "265" + startingLineNumber = "267" + endingLineNumber = "267" landmarkName = "tableView(_:cellForRowAt:)" landmarkType = "7"> @@ -3879,22 +3725,6 @@ landmarkType = "7"> - - - - @@ -3946,218 +3776,75 @@ + startingLineNumber = "58" + endingLineNumber = "58" + landmarkName = "pageViewController(_:viewControllerAfter:)" + landmarkType = "7"> - - - - - - - - - - - - - - + startingLineNumber = "9" + endingLineNumber = "9" + landmarkName = "unknown" + landmarkType = "0"> - - - - - - - - - - - - - - - - @@ -4192,8 +3879,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "397" - endingLineNumber = "397" + startingLineNumber = "385" + endingLineNumber = "385" landmarkName = "collectionView(_:contextMenuConfigurationForItemsAt:point:)" landmarkType = "7"> @@ -4208,8 +3895,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "386" - endingLineNumber = "386" + startingLineNumber = "374" + endingLineNumber = "374" landmarkName = "collectionView(_:contextMenuConfigurationForItemsAt:point:)" landmarkType = "7"> @@ -4350,8 +4037,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "125" - endingLineNumber = "125" + startingLineNumber = "169" + endingLineNumber = "169" landmarkName = "deleteTracker(_:)" landmarkType = "7"> @@ -4459,8 +4146,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "130" - endingLineNumber = "130" + startingLineNumber = "174" + endingLineNumber = "174" landmarkName = "deleteTracker(_:)" landmarkType = "7"> @@ -4605,22 +4292,6 @@ - - - - @@ -4746,6 +4417,36 @@ endingLineNumber = "142" offsetFromSymbolStart = "40"> + + + + @@ -4759,8 +4460,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "148" - endingLineNumber = "148" + startingLineNumber = "152" + endingLineNumber = "152" landmarkName = "bind()" landmarkType = "7"> @@ -4775,8 +4476,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "524" - endingLineNumber = "524" + startingLineNumber = "520" + endingLineNumber = "520" landmarkName = "eventSelectionViewController(vc:nameCategories:tracker:)" landmarkType = "7"> @@ -4949,28 +4650,12 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "101" - endingLineNumber = "101" + startingLineNumber = "125" + endingLineNumber = "125" landmarkName = "addNewTracker(_:nameCategory:)" landmarkType = "7"> - - - - @@ -5061,8 +4746,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "148" - endingLineNumber = "148" + startingLineNumber = "163" + endingLineNumber = "163" landmarkName = "bind()" landmarkType = "7"> @@ -5126,6 +4811,21 @@ endingLineNumber = "177" offsetFromSymbolStart = "144"> + + @@ -5187,8 +4887,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "156" - endingLineNumber = "156" + startingLineNumber = "171" + endingLineNumber = "171" landmarkName = "bind()" landmarkType = "7"> @@ -5219,8 +4919,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "150" - endingLineNumber = "150" + startingLineNumber = "189" + endingLineNumber = "189" landmarkName = "addPinnedCategory(_:pinnedCategory:)" landmarkType = "7"> @@ -5287,5 +4987,2183 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tracker/Domain/Store/TrackerCategoryStore.swift b/Tracker/Domain/Store/TrackerCategoryStore.swift index 32d7dc0..494f149 100644 --- a/Tracker/Domain/Store/TrackerCategoryStore.swift +++ b/Tracker/Domain/Store/TrackerCategoryStore.swift @@ -22,7 +22,7 @@ protocol TrackerCategoryStoreDelegate: AnyObject { final class TrackerCategoryStore: NSObject { private let textFixed = NSLocalizedString("textFixed", comment: "") weak var delegate: TrackerCategoryStoreDelegate? - + private let context: NSManagedObjectContext private lazy var fetchedCategoryResultController: NSFetchedResultsController = { diff --git a/Tracker/Domain/Store/TrackerStore.swift b/Tracker/Domain/Store/TrackerStore.swift index 4905559..da4f1bd 100644 --- a/Tracker/Domain/Store/TrackerStore.swift +++ b/Tracker/Domain/Store/TrackerStore.swift @@ -11,17 +11,36 @@ import CoreData protocol TrackerStoreProtocol { func addNewTracker(_ tracker: Tracker, nameCategory: String) -> Result func getTrackers(_ objects: [TrackerCoreData]) -> Result<[Tracker], Error> - func updateTracker(tracker: Tracker) -> Result + func updateTracker(tracker: Tracker, nameCategory: String) -> Result func deleteTracker(_ id: UUID) -> Result - func addPinnedCategory(_ id: UUID, pinnedCategory: PinnCategoryCoreData) -> Result } +protocol TrackerStoreDelegate: AnyObject { + func updateTracker(_ trackerCoreData: TrackerStoreProtocol) +} + //MARK: - TrackerStore final class TrackerStore: NSObject { + weak var delegate: TrackerStoreDelegate? + private let context: NSManagedObjectContext private let colorMarshalling = UIColorMarshalling() + private lazy var fetchedTrackerResultController: NSFetchedResultsController = { + let request = TrackerCoreData.fetchRequest() + let sortName = NSSortDescriptor(keyPath: \TrackerCoreData.name, ascending: true) + request.sortDescriptors = [sortName] + let fetchedResultController = NSFetchedResultsController(fetchRequest: request, + managedObjectContext: context, + sectionNameKeyPath: nil, + cacheName: nil) + fetchedResultController.delegate = self + try? fetchedResultController.performFetch() + + return fetchedResultController + }() + convenience override init() { let context = AppDelegate.container.viewContext self.init(context: context) @@ -36,7 +55,8 @@ final class TrackerStore: NSObject { private extension TrackerStore { func tracker(from trackerCoreData: TrackerCoreData) throws -> Tracker { guard let id = trackerCoreData.id else { - fatalError("not found ID") + assertionFailure("not found ID") + throw StoreErrors.TrackrerStoreError.decodingErrorInvalidId } guard let name = trackerCoreData.name else { @@ -66,7 +86,7 @@ private extension TrackerStore { trackerCoreData.schedule = tracker.schedule as NSObject } - func save(context: NSManagedObjectContext) -> Result { + func save() -> Result { do { try context.save() return .success(()) @@ -75,7 +95,7 @@ private extension TrackerStore { } } - func searchTrackerCoreData(id: UUID) throws -> TrackerCoreData? { + func searchTracker(id: UUID) throws -> TrackerCoreData? { let request = NSFetchRequest(entityName: "\(TrackerCoreData.self)") request.returnsObjectsAsFaults = false guard let keyPath = (\TrackerCoreData.id)._kvcKeyPathString @@ -83,51 +103,75 @@ private extension TrackerStore { request.predicate = NSPredicate(format: "%K == %@", keyPath, id as CVarArg) return try context.fetch(request).first } + + func searchCategory(name: String) throws -> TrackerCategoryCoreData? { + let request = NSFetchRequest(entityName: "\(TrackerCategoryCoreData.self)") + request.returnsObjectsAsFaults = false + request.predicate = NSPredicate(format: "%K == %@", + #keyPath(TrackerCategoryCoreData.nameCategory), + name as CVarArg) + return try context.fetch(request).first + } } //MARK: - TrackerStoreProtocol extension TrackerStore: TrackerStoreProtocol { func addNewTracker(_ tracker: Tracker, nameCategory: String) -> Result { - let request = NSFetchRequest(entityName: "\(TrackerCategoryCoreData.self)") - request.returnsObjectsAsFaults = false - request.predicate = NSPredicate(format: "%K == %@", - #keyPath(TrackerCategoryCoreData.nameCategory), - nameCategory as CVarArg) do { - if let category = try context.fetch(request).first { + if let category = try searchCategory(name: nameCategory) { let trackerCoreData = TrackerCoreData(context: context) updateExistingTrackerRecord(trackerCoreData: trackerCoreData, tracker: tracker) category.addToTrakers(trackerCoreData) - return save(context: context) + return save() } } catch { return .failure(error) } - return save(context: context) + return save() } - - func updateTracker(tracker: Tracker) -> Result { + + func updateTracker(tracker: Tracker, nameCategory: String) -> Result { + let _ = fetchedTrackerResultController do { - if let trackerCoreData = try searchTrackerCoreData(id: tracker.id) { - updateExistingTrackerRecord(trackerCoreData: trackerCoreData, tracker: tracker) - return save(context: context) + if let categoryCD = try searchCategory(name: nameCategory) { + do { + if let trackerCD = try searchTracker(id: tracker.id) { + trackerCD.category = categoryCD + updateExistingTrackerRecord(trackerCoreData: trackerCD, tracker: tracker) + return save() + } + } catch { + return .failure(error) + } + } else { + let catCD = TrackerCategoryCoreData(context: context) + catCD.nameCategory = nameCategory + do { + if let trackerCD = try searchTracker(id: tracker.id) { + trackerCD.category = catCD + updateExistingTrackerRecord(trackerCoreData: trackerCD, tracker: tracker) + return save() + } + } catch { + return .failure(error) + } } } catch { return .failure(error) } - return save(context: context) + return save() } func deleteTracker(_ id: UUID) -> Result { do { - if let trackerCoreData = try searchTrackerCoreData(id: id) { + if let trackerCoreData = try searchTracker(id: id) { context.delete(trackerCoreData) - return save(context: context) + return save() } } catch { return .failure(error) } - return save(context: context) + return save() } func getTrackers(_ objects: [TrackerCoreData]) -> Result<[Tracker], Error> { @@ -140,22 +184,23 @@ extension TrackerStore: TrackerStoreProtocol { } func addPinnedCategory(_ id: UUID, pinnedCategory: PinnCategoryCoreData) -> Result { - let request = NSFetchRequest(entityName: "\(TrackerCoreData.self)") - request.returnsObjectsAsFaults = false - guard let keyPath = (\TrackerCoreData.id)._kvcKeyPathString - else { return .failure(StoreErrors.TrackrerStoreError.getTrackerError) } - request.predicate = NSPredicate(format: "%K == %@", keyPath, id as CVarArg) do { - if let trackerCoreData = try searchTrackerCoreData(id: id) { - pinnedCategory.addToTracker(trackerCoreData) + if let trackerCD = try searchTracker(id: id) { + pinnedCategory.addToTracker(trackerCD) pinnedCategory.trackerId = id - return save(context: context) + return save() } } catch { return .failure(error) } - return save(context: context) + return save() } } +extension TrackerStore: NSFetchedResultsControllerDelegate { + func controllerDidChangeContent(_ controller: NSFetchedResultsController) { + guard let delegate else { return } + delegate.updateTracker(self) + } +} diff --git a/Tracker/Helpers/HandlerResultType.swift b/Tracker/Helpers/HandlerResultType.swift new file mode 100644 index 0000000..7c06b51 --- /dev/null +++ b/Tracker/Helpers/HandlerResultType.swift @@ -0,0 +1,37 @@ +// +// HandlerResultType.swift +// Tracker +// +// Created by Григррий Машук on 15.12.23. +// + +import UIKit + +final class HandlerResultType { + func resultTypeHandler(_ listCategory: Result, handler: (Value) -> Void) { + switch listCategory { + case .success(let newValue): + handler(newValue) + case .failure(let error): + showMessageErrorAlert(message: error.localizedDescription) + } + } + + func resultTypeHandlerGetValue(_ value: Result) -> Value? { + switch value { + case .success(let newValue): + return newValue + case .failure(let error): + showMessageErrorAlert(message: error.localizedDescription) + return nil + } + } + + func showMessageErrorAlert(message: String) { + let alert = UIAlertController(title: nil, message: message, preferredStyle: .alert) + let action = UIAlertAction(title: "Ok", style: .cancel) { _ in + alert.dismiss(animated: true) + } + alert.addAction(action) + } +} diff --git a/Tracker/TabBarController/TabBarController.swift b/Tracker/TabBarController/TabBarController.swift index e6d5425..1868f8f 100644 --- a/Tracker/TabBarController/TabBarController.swift +++ b/Tracker/TabBarController/TabBarController.swift @@ -22,7 +22,7 @@ final class TabBarController: UITabBarController { override func viewDidLoad() { super.viewDidLoad() setupTabBar() - let trackerViewController = TrackersViewController() + let trackerViewController = TrackersViewController(viewModel: TrackerViewModel()) let statisticViewController = StatisticViewController() let trackerNavigationController = UINavigationController(rootViewController: trackerViewController) diff --git a/Tracker/TabBarController/TrackersViews/Cell/EmojiColorCollectionViewCell.swift b/Tracker/TabBarController/TrackersViews/Cell/ColorCollectionViewCell.swift similarity index 64% rename from Tracker/TabBarController/TrackersViews/Cell/EmojiColorCollectionViewCell.swift rename to Tracker/TabBarController/TrackersViews/Cell/ColorCollectionViewCell.swift index 08986a0..1027184 100644 --- a/Tracker/TabBarController/TrackersViews/Cell/EmojiColorCollectionViewCell.swift +++ b/Tracker/TabBarController/TrackersViews/Cell/ColorCollectionViewCell.swift @@ -1,5 +1,5 @@ // -// EmojiColorCollectionViewCell.swift +// ColorCollectionViewCell.swift // Tracker // // Created by Григорий Машук on 4.10.23. @@ -8,26 +8,13 @@ import UIKit //MARK: - ColorCollectionViewCell -final class EmojiColorCollectionViewCell: UICollectionViewCell { +final class ColorCollectionViewCell: UICollectionViewCell { private struct ConstantsCell { static let cornerRadius = CGFloat(8) static let borderWidth = CGFloat(3) - static let font = UIFont.systemFont(ofSize: 32, weight: .medium) static let alfaComponent = CGFloat(0.3) } - - private var emojiView: UILabel = { - let emojiView = UILabel() - emojiView.textAlignment = .center - emojiView.layer.cornerRadius = ConstantsCell.cornerRadius - emojiView.layer.masksToBounds = true - emojiView.backgroundColor = .clear - emojiView.font = ConstantsCell.font - emojiView.translatesAutoresizingMaskIntoConstraints = false - - return emojiView - }() - + private var contentColorView: UIView = { let contentColorView = UIView() contentColorView.layer.masksToBounds = true @@ -51,23 +38,16 @@ final class EmojiColorCollectionViewCell: UICollectionViewCell { } required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") + super.init(coder: coder) + assertionFailure("init(coder:) has not been implemented") } } -extension EmojiColorCollectionViewCell { +extension ColorCollectionViewCell { //MARK: - Config - func configEmoji(emoji: String) { - emojiView.text = emoji - } - func configColor(color: UIColor) { colorView.backgroundColor = color } - //MARK: - Selected - func emojiSelection(isBackground: Bool) { - emojiView.backgroundColor = isBackground ? .grayDay : .clear - } func colorSelection(color: UIColor, flag: Bool) { contentColorView.layer.cornerRadius = flag ? ConstantsCell.cornerRadius : 0 @@ -78,15 +58,10 @@ extension EmojiColorCollectionViewCell { //MARK: - SetupUI private func setupColorView() { - addSubview(emojiView) addSubview(contentColorView) contentColorView.addSubview(colorView) NSLayoutConstraint.activate([ - emojiView.centerXAnchor.constraint(equalTo: centerXAnchor), - emojiView.centerYAnchor.constraint(equalTo: centerYAnchor), - emojiView.heightAnchor.constraint(equalToConstant: 52), - emojiView.widthAnchor.constraint(equalToConstant: 52), contentColorView.centerXAnchor.constraint(equalTo: centerXAnchor), contentColorView.centerYAnchor.constraint(equalTo: centerYAnchor), contentColorView.heightAnchor.constraint(equalToConstant: 52), diff --git a/Tracker/TabBarController/TrackersViews/Cell/CreateTrackerTableViewCell.swift b/Tracker/TabBarController/TrackersViews/Cell/CreateTrackerTableViewCell.swift index d7b63b2..db975f8 100644 --- a/Tracker/TabBarController/TrackersViews/Cell/CreateTrackerTableViewCell.swift +++ b/Tracker/TabBarController/TrackersViews/Cell/CreateTrackerTableViewCell.swift @@ -55,7 +55,8 @@ class CreateTrackerTableViewCell: UITableViewCell { } required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") + super.init(coder: coder) + assertionFailure("init(coder:) has not been implemented") } } diff --git a/Tracker/TabBarController/TrackersViews/Cell/EmojiCollectionViewCell.swift b/Tracker/TabBarController/TrackersViews/Cell/EmojiCollectionViewCell.swift new file mode 100644 index 0000000..1a2504d --- /dev/null +++ b/Tracker/TabBarController/TrackersViews/Cell/EmojiCollectionViewCell.swift @@ -0,0 +1,62 @@ +// +// EmojiCollectionViewCell.swift +// Tracker +// +// Created by Григорий Машук on 4.10.23. +// + +import UIKit + +//MARK: - EmojiCollectionViewCell +final class EmojiCollectionViewCell: UICollectionViewCell { + private struct ConstantsCell { + static let cornerRadius = CGFloat(8) + static let font = UIFont.systemFont(ofSize: 32, weight: .medium) + } + + private var emojiView: UILabel = { + let emojiView = UILabel() + emojiView.textAlignment = .center + emojiView.layer.cornerRadius = ConstantsCell.cornerRadius + emojiView.layer.masksToBounds = true + emojiView.backgroundColor = .clear + emojiView.font = ConstantsCell.font + emojiView.translatesAutoresizingMaskIntoConstraints = false + + return emojiView + }() + + override init(frame: CGRect) { + super.init(frame: frame) + setupColorView() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + assertionFailure("init(coder:) has not been implemented") + } +} + +extension EmojiCollectionViewCell { + //MARK: - Config + func configEmoji(emoji: String) { + emojiView.text = emoji + } + + //MARK: - Selected + func emojiSelection(isBackground: Bool) { + emojiView.backgroundColor = isBackground ? .grayDay : .clear + } + + //MARK: - SetupUI + private func setupColorView() { + addSubview(emojiView) + + NSLayoutConstraint.activate([ + emojiView.centerXAnchor.constraint(equalTo: centerXAnchor), + emojiView.centerYAnchor.constraint(equalTo: centerYAnchor), + emojiView.heightAnchor.constraint(equalToConstant: 52), + emojiView.widthAnchor.constraint(equalToConstant: 52), + ]) + } +} diff --git a/Tracker/TabBarController/TrackersViews/Cell/TrackersCollectionViewCell.swift b/Tracker/TabBarController/TrackersViews/Cell/TrackersCollectionViewCell.swift index ccaccf4..20198ce 100644 --- a/Tracker/TabBarController/TrackersViews/Cell/TrackersCollectionViewCell.swift +++ b/Tracker/TabBarController/TrackersViews/Cell/TrackersCollectionViewCell.swift @@ -18,6 +18,7 @@ final class TrackersCollectionViewCell: UICollectionViewCell { static let addButtonImageAdd = "Add" static let adButttonImageDone = "Done" static let isPinnedImage = "IsPinned" + static let numberOfDays = NSLocalizedString("numberOfDays", tableName: "LocalizableDict", comment: "") static let cornerRadiusColorView = CGFloat(16) static let borderWidthColorView = CGFloat(1) @@ -143,9 +144,7 @@ extension TrackersCollectionViewCell { //метод обновления счетчика выполнения трекера и imageButton func updateLableCountAndImageAddButton(_ updateModel: UpdateTracker) { - let textLable = String.localizedStringWithFormat(NSLocalizedString("numberOfDays", - tableName: "LocalizableDict", - comment: ""), + let textLable = String.localizedStringWithFormat(ConstantsTrackerCell.numberOfDays, updateModel.count) dayCounterLable.text = textLable switch updateModel.flag { diff --git a/Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift b/Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift index 3f298b8..349c8ca 100644 --- a/Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift +++ b/Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift @@ -15,13 +15,13 @@ protocol CreateCategoriesViewControllerDelegate: AnyObject { //MARK: - CreateCategoriesViewController final class CreateCategoriesViewController: UIViewController { private struct ConstantsCreateCatVc { - static let textFixed = NSLocalizedString("textFixed", comment: "") static let imageViewImageStab = "Star" static let alertActionErrorTitle = "Ok" static let categoriLabelText = NSLocalizedString("categoriLabelText", comment: "") static let categoriAddButtonText = NSLocalizedString("categoriAddButtonText", comment: "") static let labelStabText = NSLocalizedString("labelStabText", comment: "") + static let textFixed = NSLocalizedString("textFixed", comment: "") static let lableTextStabNumberOfLines = 2 static let selectionCategoryTableViewRowHeight = CGFloat(75) @@ -35,8 +35,7 @@ final class CreateCategoriesViewController: UIViewController { } weak var delegate: CreateCategoriesViewControllerDelegate? - - private var viewModel: ViewModelProtocol? + private var viewModel: TrackerViewModelProtocol private lazy var categoriLabel: UILabel = { let categoriLabel = UILabel() @@ -103,18 +102,27 @@ final class CreateCategoriesViewController: UIViewController { return createCategoriButton }() + init(delegate: CreateCategoriesViewControllerDelegate, viewModel: TrackerViewModelProtocol) { + self.delegate = delegate + self.viewModel = viewModel + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + override func viewDidLoad() { super.viewDidLoad() - guard let viewModel = viewModel as? ViewModel else { return } + guard let viewModel = viewModel as? TrackerViewModel else { return } + setupUIElement() bind() view.backgroundColor = .whiteDay - setupUIElement() resultTypeHandler(viewModel.category) { [weak self] cat in guard let self else { return } self.showStabView(flag: !cat.isEmpty) } selectionCategoryTableView.rowHeight = ConstantsCreateCatVc.selectionCategoryTableViewRowHeight - let tapGesture = UITapGestureRecognizer(target: self, action: nil) tapGesture.cancelsTouchesInView = false view.addGestureRecognizer(tapGesture) @@ -122,10 +130,6 @@ final class CreateCategoriesViewController: UIViewController { } extension CreateCategoriesViewController { - func config(viewModel: ViewModel?) { - self.viewModel = viewModel - } - //MARK: - обработка событий @objc private func didTapСategoryButton() { @@ -141,7 +145,7 @@ extension CreateCategoriesViewController { } private func bind() { - guard let viewModel = viewModel as? ViewModel else { return } + guard let viewModel = viewModel as? TrackerViewModel else { return } viewModel.$category.bind { [weak self] _ in guard let self else { return } resultTypeHandler(viewModel.category) { cat in @@ -244,15 +248,13 @@ extension CreateCategoriesViewController { //MARK: - UITableViewDataSource extension CreateCategoriesViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - guard let viewModel, - let count = resultTypeHandlerGetValue(viewModel.getCategory())?.count + guard let count = resultTypeHandlerGetValue(viewModel.getCategory())?.count else { return .zero } return count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { guard let cell = tableView.dequeueReusableCell(withIdentifier: "\(CreateCategoriesTableViewCell.self)") as? CreateCategoriesTableViewCell, - let viewModel, let isSelected = resultTypeHandlerGetValue(viewModel.isCategorySelected(at: indexPath.row)), let text = resultTypeHandlerGetValue(viewModel.createNameCategory(at: indexPath.row)), let count = resultTypeHandlerGetValue(viewModel.getCategory())?.count @@ -283,8 +285,7 @@ extension CreateCategoriesViewController: UITableViewDataSource { extension CreateCategoriesViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { guard let cell = tableView.cellForRow(at: indexPath) as? CreateCategoriesTableViewCell, - let delegate, - let viewModel + let delegate else { return } cell.showSelectedImage(flag: false) resultTypeHandler(viewModel.selectСategory(at: indexPath.row)) {} @@ -298,13 +299,8 @@ extension CreateCategoriesViewController: UITableViewDelegate { //MARK: - NewCategoriViewControllerDelegate extension CreateCategoriesViewController: NewCategoriViewControllerDelegate { func didNewCategoriName(_ vc: UIViewController, nameCategori: String) { - guard let viewModel, - let category = resultTypeHandlerGetValue(viewModel.getCategory()) + guard let category = resultTypeHandlerGetValue(viewModel.getCategory()) else { return } -// if category.contains(where: { $0.nameCategory == ConstantsCreateCatVc.textFixed}) { -// } else { -// resultTypeHandler(viewModel.addCategory(nameCategory: ConstantsCreateCatVc.textFixed)) {} -// } let nameFirstUppercased = nameCategori.lowercased().firstUppercased if let _ = category.filter({ $0.nameCategory == nameFirstUppercased }).first { return diff --git a/Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift b/Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift index a9402ce..174d1a5 100644 --- a/Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift +++ b/Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift @@ -7,14 +7,20 @@ import UIKit +//MARK: - UpdateTrackerViewControllerDelegate +protocol UpdateTrackerViewControllerDelegate: AnyObject { + func trackerViewController(vc: UIViewController, nameCategory: String, tracker: Tracker) + func trackerViewControllerDidCancel(_ vc: CreateTrackerViewController) +} + //MARK: - CreateTrackerViewControllerDelegate protocol CreateTrackerViewControllerDelegate: AnyObject { - func createTrackerViewController(vc: UIViewController, nameCategories: String, tracker: Tracker) - func createTrackerViewControllerDidCancel(_ vc: CreateTrackerViewController) + func trackerViewController(vc: UIViewController, nameCategory: String, tracker: Tracker) + func trackerViewControllerDidCancel(_ vc: CreateTrackerViewController) } //MARK: - CreateTrackerViewController -final class CreateTrackerViewController: UIViewController{ +final class CreateTrackerViewController: UIViewController { private struct ConstantsCreateVc { static let newHabit = NSLocalizedString("newHabit", comment: "") static let newIrregular = NSLocalizedString("newIrregular", comment: "") @@ -40,53 +46,15 @@ final class CreateTrackerViewController: UIViewController{ static let borderWidthButton = CGFloat(1) } - weak var delegate: CreateTrackerViewControllerDelegate? - - private var isSchedul: Bool = true - private var listSettings: [ChoiceParametrs] { isSchedul ? [.category] : [.category, .schedule] } - private let dataSection: [Header] = [.emoji, .color] - private let emojies: [String] = ["🙂", "😻", "🌺", "🐶", "❤️", "😱", "😇", "😡", "🥶", - "🤔", "🙌", "🍔", "🥦", "🏓", "🥇", "🎸", "🏝️", "😪"] + weak var updateTrackerDelegate: UpdateTrackerViewControllerDelegate? + weak var createTrackerDelegate: CreateTrackerViewControllerDelegate? - private let colors: [UIColor] = [.colorSelection1, .colorSelection2, .colorSelection3,.colorSelection4, - .colorSelection5, .colorSelection6, .colorSelection7, .colorSelection8, - .colorSelection9, .colorSelection10, .colorSelection11, .colorSelection12, - .colorSelection13, .colorSelection14, .colorSelection15, .colorSelection16, - .colorSelection17, .colorSelection18] - - //данные для создания трекера - private var schedule: [WeekDay] = [] { - didSet { - let flag = checkingForEmptiness() - isActivCreateButton(flag: flag) - } - } - - private let regular: [WeekDay] = [.monday, .tuesday, .wednesday, .thursday, .friday, .saturday, .sunday] - - private var color: UIColor? { - didSet { - let flag = checkingForEmptiness() - isActivCreateButton(flag: flag) - } - } - private var nameNewCategory: String = "" { - didSet { - let flag = checkingForEmptiness() - isActivCreateButton(flag: flag) - } - } - private var nameTracker: String = "" - private var emoji: String = "" { - didSet { - let flag = checkingForEmptiness() - isActivCreateButton(flag: flag) - } - } + private let viewModel: CategoryViewModelProtocol + private let handlerResultType = HandlerResultType() private lazy var newHabitLabel: UILabel = { let newHabitLabel = UILabel() - newHabitLabel.text = isSchedul ? ConstantsCreateVc.newIrregular : ConstantsCreateVc.newHabit + newHabitLabel.text = viewModel.isSchedul ? ConstantsCreateVc.newIrregular : ConstantsCreateVc.newHabit newHabitLabel.textColor = .blackDay newHabitLabel.font = ConstantsCreateVc.textFont newHabitLabel.textAlignment = .center @@ -139,7 +107,7 @@ final class CreateTrackerViewController: UIViewController{ selectionTableView.delegate = self selectionTableView.backgroundColor = .clear selectionTableView.isScrollEnabled = false - selectionTableView.separatorStyle = isSchedul ? .none : .singleLine + selectionTableView.separatorStyle = viewModel.isSchedul ? .none : .singleLine selectionTableView.separatorInset = ConstantsCreateVc.insertSeparatorTableView selectionTableView.register(CreateTrackerTableViewCell.self, forCellReuseIdentifier: "\(CreateTrackerTableViewCell.self)") @@ -152,7 +120,7 @@ final class CreateTrackerViewController: UIViewController{ let emojiCollectionView = TrackerCollectionView(frame: .zero, collectionViewLayout: layout, params: params) emojiCollectionView.dataSource = self emojiCollectionView.delegate = self - emojiCollectionView.register(EmojiColorCollectionViewCell.self, forCellWithReuseIdentifier: "\(EmojiColorCollectionViewCell.self)") + emojiCollectionView.register(EmojiCollectionViewCell.self, forCellWithReuseIdentifier: "\(EmojiCollectionViewCell.self)") emojiCollectionView.register(HeaderReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "\(HeaderReusableView.self)") emojiCollectionView.allowsMultipleSelection = false emojiCollectionView.backgroundColor = .clear @@ -168,7 +136,7 @@ final class CreateTrackerViewController: UIViewController{ let colorCollectionView = TrackerCollectionView(frame: .zero, collectionViewLayout: layout, params: params) colorCollectionView.dataSource = self colorCollectionView.delegate = self - colorCollectionView.register(EmojiColorCollectionViewCell.self, forCellWithReuseIdentifier: "\(EmojiColorCollectionViewCell.self)") + colorCollectionView.register(ColorCollectionViewCell.self, forCellWithReuseIdentifier: "\(ColorCollectionViewCell.self)") colorCollectionView.register(HeaderReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "\(HeaderReusableView.self)") @@ -234,19 +202,68 @@ final class CreateTrackerViewController: UIViewController{ return scrollView }() + init(viewModel: CategoryViewModelProtocol, + updateTrackerDelegate: UpdateTrackerViewControllerDelegate? = nil, + createTrackerDelegate: CreateTrackerViewControllerDelegate? = nil) + { + self.viewModel = viewModel + self.updateTrackerDelegate = updateTrackerDelegate + self.createTrackerDelegate = createTrackerDelegate + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + override func viewDidLoad() { super.viewDidLoad() + bind() + setupUIElement() view.backgroundColor = .white - let tapGesture = UITapGestureRecognizer(target: self, action: #selector(hideKeyboard)) tapGesture.cancelsTouchesInView = false view.addGestureRecognizer(tapGesture) - - setupUIElement() } } extension CreateTrackerViewController { + func bind() { + guard let viewModel = viewModel as? CategoryViewModel else { return } + viewModel.$schedule.bind { [weak self] _ in + guard let self else { return } + self.chengeActivButton() + selectionTableView.reloadRows(at: [IndexPath(row: 1, section: 0)], with: .automatic) + } + + viewModel.$color.bind { [weak self] _ in + guard let self else { return } + self.chengeActivButton() + } + + viewModel.$emoji.bind { [weak self] _ in + guard let self else { return } + self.chengeActivButton() + } + + viewModel.$nameNewCategory.bind { [weak self] _ in + guard let self else { return } + self.chengeActivButton() + selectionTableView.reloadRows(at: [IndexPath(row: 0, section: 0)], with: .automatic) + } + + viewModel.$nameTracker.bind { [weak self] nameTracker in + guard let self else { return } + self.chengeActivButton() + createNameTextField.text = nameTracker + } + } + + func chengeActivButton() { + let flag = self.viewModel.checkingForEmptiness() + isActivCreateButton(flag: flag) + } + //MARK: - Обработка событий @objc private func didTapCancelButton() { @@ -255,27 +272,46 @@ extension CreateTrackerViewController { @objc private func didTapGreateButton() { - guard let color, - let delegate + guard let viewModel = viewModel as? CategoryViewModel, + let color = viewModel.color else { return } - let tracker = Tracker(name: nameTracker, - color: color, - emoji: emoji, - schedule: isSchedul ? regular : schedule) - delegate.createTrackerViewController(vc: self, nameCategories: nameNewCategory, tracker: tracker) - delegate.createTrackerViewControllerDidCancel(self) + if let updateTrackerDelegate = updateTrackerDelegate, let id = viewModel.id { + let tracker = Tracker( + id: id, + name: viewModel.nameTracker, + color: color, + emoji: viewModel.emoji, + schedule: viewModel.isSchedul ? viewModel.regular : viewModel.schedule) + updateTrackerDelegate.trackerViewController(vc: self, + nameCategory: viewModel.nameNewCategory, + tracker: tracker) + updateTrackerDelegate.trackerViewControllerDidCancel(self) + } + if let createTrackerDelegate { + let tracker = Tracker( + name: viewModel.nameTracker, + color: color, + emoji: viewModel.emoji, + schedule: viewModel.isSchedul ? viewModel.regular : viewModel.schedule) + createTrackerDelegate.trackerViewController(vc: self, + nameCategory: viewModel.nameNewCategory, + tracker: tracker) + createTrackerDelegate.trackerViewControllerDidCancel(self) + } } @objc private func textFieldDidChange(_ textField: UITextField) { - guard let text = textField.text else { return } + guard let text = textField.text, + let viewModel = viewModel as? CategoryViewModel + else { return } if text.count > ConstantsCreateVc.restrictionsTextField { characterRestrictionsView.isHidden = false - textField.text = nameTracker.firstUppercased + textField.text = viewModel.nameTracker.firstUppercased return } - nameTracker = text.firstUppercased - let flag = checkingForEmptiness() + viewModel.setNameTracker(self, nameTracker: text.firstUppercased) + let flag = viewModel.checkingForEmptiness() isActivCreateButton(flag: flag) } @@ -285,52 +321,13 @@ extension CreateTrackerViewController { } //MARK: - Логика - //метод активации/деактивации greateButton private func isActivCreateButton(flag: Bool) { createButton.isEnabled = flag createButton.backgroundColor = flag ? .blackDay : .grayDay } - //метод ссоздания текста для вторичной строки в ячейке "shedul" - private func jonedSchedule(schedule: [WeekDay]) -> String { - var stringListDay: String - if schedule.count == 7 { - stringListDay = ConstantsCreateVc.everyDay - return stringListDay - } - let listDay = schedule.map { $0.briefWordDay } - stringListDay = listDay.joined(separator: ",") - - return stringListDay - } - - //метод проверки свойств на пустоту - private func checkingForEmptiness() -> Bool { - var flag: Bool - if isSchedul { - flag = !nameTracker.isEmpty && - color != nil && - !emoji.isEmpty && - !nameNewCategory.isEmpty ? true : false - - return flag - } - flag = !schedule.isEmpty && - !nameTracker.isEmpty && - color != nil && - !emoji.isEmpty && - !nameNewCategory.isEmpty ? true : false - - return flag - } - - //метод изменения свойства isSchedul func reverseIsSchedul() { - isSchedul = isSchedul ? !isSchedul : isSchedul - } - - func flowEditing(traker: Tracker) { - + viewModel.reverseIsSchedul() } func presentViewController(vc: UIViewController, modalStyle: UIModalPresentationStyle) { @@ -398,7 +395,7 @@ extension CreateTrackerViewController { createNameTextField.heightAnchor.constraint(equalToConstant: 75), emojiCollectionView.heightAnchor.constraint(equalToConstant: 222), colorCollectionView.heightAnchor.constraint(equalToConstant: 222), - selectionTableView.heightAnchor.constraint(equalToConstant: isSchedul ? 75 : 150) + selectionTableView.heightAnchor.constraint(equalToConstant: viewModel.isSchedul ? 75 : 150) ]) } } @@ -406,32 +403,36 @@ extension CreateTrackerViewController { //MARK: - UITableViewDataSource extension CreateTrackerViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - listSettings.count + viewModel.listSettings.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - guard let cell = tableView.dequeueReusableCell(withIdentifier: "\(CreateTrackerTableViewCell.self)") as? CreateTrackerTableViewCell else { return UITableViewCell() } - let secondarySchedulText = jonedSchedule(schedule: schedule) - switch listSettings[indexPath.row] { + guard let cell = tableView.dequeueReusableCell(withIdentifier: "\(CreateTrackerTableViewCell.self)") as? CreateTrackerTableViewCell, + let viewModel = viewModel as? CategoryViewModel + else { return UITableViewCell() } + let secondarySchedulText = viewModel.jonedSchedule(schedule: viewModel.schedule, + stringArrayDay: ConstantsCreateVc.everyDay) + let listSettings = viewModel.listSettings[indexPath.row] + switch listSettings { case .category: - cell.configCell(choice: listSettings[indexPath.row]) - cell.configSecondaryLableShedule(secondaryText: nameNewCategory) + cell.configCell(choice: listSettings) + cell.configSecondaryLableShedule(secondaryText: viewModel.nameNewCategory) case .schedule: - cell.configCell(choice: listSettings[indexPath.row]) + cell.configCell(choice: listSettings) cell.configSecondaryLableShedule(secondaryText: secondarySchedulText) tableView.separatorInset = UIEdgeInsets(top: 0, left: view.bounds.width, bottom: 0, right: 0) } - if isSchedul { + if viewModel.isSchedul { cell.layer.cornerRadius = ConstantsCreateVc.cornerRadius } - if !isSchedul && indexPath.row == 1 { + if !viewModel.isSchedul && indexPath.row == 1 { cell.setupCornerRadius(cornerRadius: ConstantsCreateVc.cornerRadius, maskedCorners: [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]) } - if !isSchedul && indexPath.row == 0 { + if !viewModel.isSchedul && indexPath.row == 0 { cell.setupCornerRadius(cornerRadius: ConstantsCreateVc.cornerRadius, maskedCorners: [.layerMinXMinYCorner, .layerMaxXMinYCorner]) } @@ -447,15 +448,13 @@ extension CreateTrackerViewController: UITableViewDelegate { } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - switch listSettings[indexPath.row] { + switch viewModel.listSettings[indexPath.row] { case .category: - let greateCategoriViewController = CreateCategoriesViewController() - let viewModel = ViewModel() - greateCategoriViewController.config(viewModel: viewModel) - greateCategoriViewController.delegate = self + let greateCategoriViewController = CreateCategoriesViewController(delegate: self, viewModel: TrackerViewModel()) presentViewController(vc: greateCategoriViewController, modalStyle: .formSheet) case .schedule: - let scheduleViewController = ScheduleViewController() + let viewModel = SheduleViewModel() + let scheduleViewController = ScheduleViewController(viewModel: viewModel) scheduleViewController.delegate = self presentViewController(vc: scheduleViewController, modalStyle: .formSheet) } @@ -470,79 +469,70 @@ extension CreateTrackerViewController: UITextFieldDelegate { } } -//MARK: - ScheduleViewControllerDelegate -extension CreateTrackerViewController: ScheduleViewControllerDelegate { - func daysOfWeek(viewController: UIViewController, listDays: [WeekDay]) { - guard let _ = viewController as? ScheduleViewController else { return } - schedule = listDays - selectionTableView.reloadRows(at: [IndexPath(row: 1, section: 0)], with: .automatic) - } -} - -//MARK: - GreateCategoriesViewControllerDelegate -extension CreateTrackerViewController: CreateCategoriesViewControllerDelegate { - func createCategoriesViewController(vc: UIViewController, nameCategory: String) { - nameNewCategory = nameCategory - selectionTableView.reloadRows(at: [IndexPath(row: 0, section: 0)], with: .automatic) - vc.dismiss(animated: true) - } -} - //MARK: - UICollectionViewDataSource extension CreateTrackerViewController: UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { var count: Int - switch dataSection[section] { + switch viewModel.dataSection[section] { case .color: - count = colors.count + count = viewModel.colors.count case .emoji: - count = emojies.count + count = viewModel.emojies.count } return count } func numberOfSections(in collectionView: UICollectionView) -> Int { - dataSection.count + viewModel.dataSection.count } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { - guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "\(EmojiColorCollectionViewCell.self)", for: indexPath) as? EmojiColorCollectionViewCell - else { return UICollectionViewCell() } - if collectionView == colorCollectionView { - let color = colors[indexPath.row] + var returnCell = UICollectionViewCell() + if collectionView == colorCollectionView, + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "\(ColorCollectionViewCell.self)", + for: indexPath) as? ColorCollectionViewCell + { + let color = viewModel.colors[indexPath.row] cell.configColor(color: color) + returnCell = cell } - if collectionView == emojiCollectionView { - let emoji = emojies[indexPath.row] + if collectionView == emojiCollectionView, + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "\(EmojiCollectionViewCell.self)", + for: indexPath) as? EmojiCollectionViewCell + { + let emoji = viewModel.emojies[indexPath.row] cell.configEmoji(emoji: emoji) + returnCell = cell } - return cell + return returnCell } } //MARK: - UICollectionViewDelegate extension CreateTrackerViewController: UICollectionViewDelegate { func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { - guard let cell = collectionView.cellForItem(at: indexPath) as? EmojiColorCollectionViewCell else { return } if collectionView == colorCollectionView { - let color = colors[indexPath.row] + guard let cell = collectionView.cellForItem(at: indexPath) as? ColorCollectionViewCell else { return } + let color = viewModel.colors[indexPath.row] cell.colorSelection(color: color, flag: true) - self.color = color + viewModel.setColor(self, color: color) } if collectionView == emojiCollectionView { - let emoji = emojies[indexPath.row] + guard let cell = collectionView.cellForItem(at: indexPath) as? EmojiCollectionViewCell else { return } + let emoji = viewModel.emojies[indexPath.row] cell.emojiSelection(isBackground: true) - self.emoji = emoji + viewModel.setEmojiTracker(self, emoji: emoji) } } func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) { - guard let cell = collectionView.cellForItem(at: indexPath) as? EmojiColorCollectionViewCell else { return } if collectionView == colorCollectionView { + guard let cell = collectionView.cellForItem(at: indexPath) as? ColorCollectionViewCell else { return } let color: UIColor = .clear cell.colorSelection(color: color, flag: false) } if collectionView == emojiCollectionView { + guard let cell = collectionView.cellForItem(at: indexPath) as? EmojiCollectionViewCell else { return } cell.emojiSelection(isBackground: false) } } @@ -586,3 +576,37 @@ extension CreateTrackerViewController: UICollectionViewDelegateFlowLayout { return supplementary } } + +//MARK: - ScheduleViewControllerDelegate +extension CreateTrackerViewController: ScheduleViewControllerDelegate { + func daysOfWeek(viewController: UIViewController, listDays: [WeekDay]) { + viewModel.setListWeekDay(listWeekDay: listDays) + } +} + +//MARK: - GreateCategoriesViewControllerDelegate +extension CreateTrackerViewController: CreateCategoriesViewControllerDelegate { + func createCategoriesViewController(vc: UIViewController, nameCategory: String) { + viewModel.setNameNewCategory(self, nameCategory: nameCategory) + vc.dismiss(animated: true) + } +} + +//MARK: - TrackersViewControllerDelegate +extension CreateTrackerViewController: TrackersViewControllerDelegate { + func editTracker(vc: TrackersViewController, editTracker: Tracker, nameCategory: String, indexPath: IndexPath) { + let colorRow = viewModel.getColorRow(color: editTracker.color) + let emojiRow = viewModel.getEmojiRow(emoji: editTracker.emoji) + + guard let emojiCell = emojiCollectionView.cellForItem(at: IndexPath(row: emojiRow, section: 0)) as? EmojiCollectionViewCell, + let colorCell = colorCollectionView.cellForItem(at: IndexPath(row: colorRow, section: 0)) as? ColorCollectionViewCell + else { return } + viewModel.editTracker(vc: self, editTracker: editTracker, nameCategory: nameCategory) + emojiCell.emojiSelection(isBackground: true) + colorCell.colorSelection(color: editTracker.color, flag: true) + emojiCollectionView.selectItem(at: IndexPath(row: emojiRow, section: 0), + animated: true, scrollPosition: UICollectionView.ScrollPosition()) + colorCollectionView.selectItem(at: IndexPath(row: colorRow, section: 0), + animated: true, scrollPosition: UICollectionView.ScrollPosition()) + } +} diff --git a/Tracker/TabBarController/TrackersViews/Cell/EventSelectionViewController.swift b/Tracker/TabBarController/TrackersViews/ViewControllers/EventSelectionViewController.swift similarity index 89% rename from Tracker/TabBarController/TrackersViews/Cell/EventSelectionViewController.swift rename to Tracker/TabBarController/TrackersViews/ViewControllers/EventSelectionViewController.swift index 2996e68..59aa85b 100644 --- a/Tracker/TabBarController/TrackersViews/Cell/EventSelectionViewController.swift +++ b/Tracker/TabBarController/TrackersViews/ViewControllers/EventSelectionViewController.swift @@ -7,8 +7,6 @@ import UIKit - - //MARK: - EventSelectionViewControllerDelegate protocol EventSelectionViewControllerDelegate: AnyObject { func eventSelectionViewController(vc: UIViewController, nameCategories: String, tracker: Tracker) @@ -84,10 +82,12 @@ private extension EventSelectionViewController { } func createTrackerVC() -> CreateTrackerViewController { - let greateVC = CreateTrackerViewController() - greateVC.delegate = self - greateVC.modalPresentationStyle = .formSheet - return greateVC + let viewModel = CategoryViewModel() + let createVC = CreateTrackerViewController(viewModel: viewModel, + updateTrackerDelegate: nil, + createTrackerDelegate: self) + createVC.modalPresentationStyle = .formSheet + return createVC } //MARK: - SetupUI @@ -134,11 +134,11 @@ private extension EventSelectionViewController { //MARK: - CreateTrackerViewControllerDelegate extension EventSelectionViewController: CreateTrackerViewControllerDelegate { - func createTrackerViewController(vc: UIViewController, nameCategories: String, tracker: Tracker) { - delegate?.eventSelectionViewController(vc: self, nameCategories: nameCategories, tracker: tracker) + func trackerViewController(vc: UIViewController, nameCategory: String, tracker: Tracker) { + delegate?.eventSelectionViewController(vc: self, nameCategories: nameCategory, tracker: tracker) } - func createTrackerViewControllerDidCancel(_ vc: CreateTrackerViewController) { + func trackerViewControllerDidCancel(_ vc: CreateTrackerViewController) { vc.dismiss(animated: false) delegate?.eventSelectionViewControllerDidCancel(self) } diff --git a/Tracker/TabBarController/TrackersViews/ViewControllers/ScheduleViewController.swift b/Tracker/TabBarController/TrackersViews/ViewControllers/ScheduleViewController.swift index 87456b1..ecee5ed 100644 --- a/Tracker/TabBarController/TrackersViews/ViewControllers/ScheduleViewController.swift +++ b/Tracker/TabBarController/TrackersViews/ViewControllers/ScheduleViewController.swift @@ -24,9 +24,8 @@ final class ScheduleViewController: UIViewController { static let scheduleLableFont = UIFont.systemFont(ofSize: 16, weight: .medium) } - private let weekDay: [WeekDay] = [.monday, .tuesday, .wednesday, .thursday, .friday, .saturday, .sunday] weak var delegate: ScheduleViewControllerDelegate? - private var listWeekDay: [WeekDay] = [] + private let viewModel: SheduleViewModelProtocol private lazy var scheduleLable: UILabel = { let scheduleLable = UILabel() @@ -72,6 +71,15 @@ final class ScheduleViewController: UIViewController { return contentStackView }() + init(viewModel: SheduleViewModelProtocol) { + self.viewModel = viewModel + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .whiteDay @@ -87,7 +95,7 @@ private extension ScheduleViewController { guard let delegate else { return } //передаем список дней для поля "schedule" при создании трекера - let listDays = listWeekDay.sorted { $0.rawValue < $1.rawValue } + let listDays = viewModel.listWeekDay.sorted { $0.rawValue < $1.rawValue } delegate.daysOfWeek(viewController: self, listDays: listDays) dismiss(animated: true) } @@ -97,15 +105,15 @@ private extension ScheduleViewController { func updateListWeekDay(flag: Bool, day: WeekDay) { if flag { var listDay: [WeekDay] = [] - listDay = listWeekDay + listDay = viewModel.listWeekDay listDay.append(day) - listWeekDay = listDay + viewModel.setListWeekDay(listWeekDay: listDay) } else { var listDay: [WeekDay] = [] - listDay = listWeekDay + listDay = viewModel.listWeekDay guard let index = listDay.firstIndex(of: day) else { return } listDay.remove(at: index) - listWeekDay = listDay + viewModel.setListWeekDay(listWeekDay: listDay) } } @@ -132,20 +140,19 @@ private extension ScheduleViewController { //MARK: - UITableViewDataSource extension ScheduleViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - weekDay.count + viewModel.weekDay.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { guard let cell = tableView.dequeueReusableCell(withIdentifier: "\(WeekDayTableViewCell.self)") as? WeekDayTableViewCell else { return UITableViewCell() } cell.delegate = self - cell.config(nameDay: weekDay[indexPath.row]) - - if weekDay[indexPath.row] == weekDay.first { + cell.config(nameDay: viewModel.weekDay[indexPath.row]) + if viewModel.weekDay[indexPath.row] == viewModel.weekDay.first { cell.setupCornerRadius(cornerRadius: ConstantsShedulVc.cornerRadiusUIElement, maskedCorners: [.layerMinXMinYCorner, .layerMaxXMinYCorner]) } - if weekDay[indexPath.row] == weekDay.last { + if viewModel.weekDay[indexPath.row] == viewModel.weekDay.last { cell.setupCornerRadius(cornerRadius: ConstantsShedulVc.cornerRadiusUIElement, maskedCorners: [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]) cell.separatorInset = UIEdgeInsets(top: 0, @@ -171,7 +178,7 @@ extension ScheduleViewController: WeekDayTableViewCellDelegate { guard let cell = cell as? WeekDayTableViewCell, let indexPath = weekDayTableView.indexPath(for: cell) else { return } - let day = weekDay[indexPath.row] + let day = viewModel.weekDay[indexPath.row] updateListWeekDay(flag: flag, day: day) } } diff --git a/Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift b/Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift index 4553949..5bdf6f5 100644 --- a/Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift +++ b/Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift @@ -7,6 +7,10 @@ import UIKit +protocol TrackersViewControllerDelegate: AnyObject { + func editTracker(vc: TrackersViewController, editTracker: Tracker, nameCategory: String, indexPath: IndexPath) +} + //MARK: - TrackersViewController final class TrackersViewController: UIViewController { private struct ConstantsTrackerVc { @@ -36,7 +40,9 @@ final class TrackersViewController: UIViewController { static let firstWeekday = 2 } - private var viewModel: ViewModelProtocol? + weak var delegate: TrackersViewControllerDelegate? + private var viewModel: TrackerViewModelProtocol? + private let handler = HandlerResultType() private lazy var horisontalStack: UIStackView = { let horisontalStack = UIStackView() @@ -121,15 +127,24 @@ final class TrackersViewController: UIViewController { return contentView }() + init(viewModel: TrackerViewModelProtocol) { + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + assertionFailure("init(coder:) has not been implemented") + } + override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .white setupUIElement() - viewModel = ViewModel() + viewModel = TrackerViewModel() bind() - guard let viewModel = viewModel as? ViewModel else { return } + guard let viewModel = viewModel as? TrackerViewModel else { return } viewModel.getShowListTrackersForDay(date: datePicker.date) - guard let visibleCategories = resultTypeHandlerGetValue(viewModel.visibleCategory) + guard let visibleCategories = handler.resultTypeHandlerGetValue(viewModel.visibleCategory) else { return } self.showStabView(flag: !visibleCategories.isEmpty) } @@ -137,7 +152,7 @@ final class TrackersViewController: UIViewController { private extension TrackersViewController { func bind() { - guard let viewModel = viewModel as? ViewModel else { return } + guard let viewModel = viewModel as? TrackerViewModel else { return } viewModel.$category.bind { [weak self] _ in guard let self else { return } viewModel.getShowListTrackersForDay(date: self.datePicker.date) @@ -150,7 +165,7 @@ private extension TrackersViewController { viewModel.$visibleCategory.bind { [weak self] result in guard let self, - let cat = resultTypeHandlerGetValue(result) + let cat = handler.resultTypeHandlerGetValue(result) else { return } self.showStabView(flag: !cat.isEmpty) self.trackerCollectionView.reloadData() @@ -174,14 +189,6 @@ private extension TrackersViewController { present(greateVC, animated: true) } - func showMessageErrorAlert(message: String) { - let alert = UIAlertController(title: nil, message: message, preferredStyle: .alert) - let action = UIAlertAction(title: "Ok", style: .cancel) { _ in - alert.dismiss(animated: true) - } - alert.addAction(action) - } - //метод изменения заглушки func chengeStab(text: String, nameImage: String) { lableTextStab.text = text @@ -207,25 +214,6 @@ private extension TrackersViewController { func showStabView(flag: Bool) { [imageViewStab, lableTextStab].forEach { $0.isHidden = flag } } - - func resultTypeHandler(_ listCategory: Result, handler: (Value) -> Void) { - switch listCategory { - case .success(let newValue): - handler(newValue) - case .failure(let error): - showMessageErrorAlert(message: error.localizedDescription) - } - } - - func resultTypeHandlerGetValue(_ value: Result) -> Value? { - switch value { - case .success(let newValue): - return newValue - case .failure(let error): - showMessageErrorAlert(message: error.localizedDescription) - return nil - } - } //метод создания кастомной CollectionView func greateTrackerCollectionView() -> TrackerCollectionView { @@ -326,15 +314,15 @@ private extension TrackersViewController { //MARK: - UICollectionViewDataSource extension TrackersViewController: UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - guard let viewModel = viewModel as? ViewModel, - let itemsCount = resultTypeHandlerGetValue(viewModel.visibleCategory)?[section].arrayTrackers.count + guard let viewModel = viewModel as? TrackerViewModel, + let itemsCount = handler.resultTypeHandlerGetValue(viewModel.visibleCategory)?[section].arrayTrackers.count else { return .zero } return itemsCount } func numberOfSections(in collectionView: UICollectionView) -> Int { - guard let viewModel = viewModel as? ViewModel, - let sectionCount = resultTypeHandlerGetValue(viewModel.visibleCategory)?.count + guard let viewModel = viewModel as? TrackerViewModel, + let sectionCount = handler.resultTypeHandlerGetValue(viewModel.visibleCategory)?.count else { return .zero } return sectionCount } @@ -342,8 +330,8 @@ extension TrackersViewController: UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "\(TrackersCollectionViewCell.self)", for: indexPath) as? TrackersCollectionViewCell, - let viewModel = viewModel as? ViewModel, - let visibleCategories = resultTypeHandlerGetValue(viewModel.visibleCategory) + let viewModel = viewModel as? TrackerViewModel, + let visibleCategories = handler.resultTypeHandlerGetValue(viewModel.visibleCategory) else { return UICollectionViewCell() } cell.delegate = self let tracker = visibleCategories[indexPath.section].arrayTrackers[indexPath.row] @@ -360,10 +348,10 @@ extension TrackersViewController: UICollectionViewDataSource { cell.updateBackgraundAddButton(isHidden: true) } cell.setPinned(flag: visibleCategories[indexPath.section].isPinned) - resultTypeHandler(viewModel.loadTrackerRecord(id: tracker.id)) { [weak self] count in + handler.resultTypeHandler(viewModel.loadTrackerRecord(id: tracker.id)) { [weak self] count in guard let self, - let isComplited = self.resultTypeHandlerGetValue(viewModel.getIsComplited(tracker: tracker, - date: self.datePicker.date)) + let isComplited = self.handler.resultTypeHandlerGetValue(viewModel.getIsComplited(tracker: tracker, + date: self.datePicker.date)) else { return } let updateModel = UpdateTracker(count: count, flag: isComplited) cell.config(tracker: tracker) @@ -387,8 +375,8 @@ extension TrackersViewController: UICollectionViewDelegate { contextMenuConfigurationForItemsAt indexPaths: [IndexPath], point: CGPoint) -> UIContextMenuConfiguration? { guard let indexPath = indexPaths.first, - let viewModel = viewModel as? ViewModel, - let visibleCategories = resultTypeHandlerGetValue(viewModel.visibleCategory) + let viewModel = viewModel as? TrackerViewModel, + let visibleCategories = handler.resultTypeHandlerGetValue(viewModel.visibleCategory) else { return nil } let selectorCategory = visibleCategories[indexPath.section] let selectedTracker = selectorCategory.arrayTrackers[indexPath.row] @@ -399,39 +387,47 @@ extension TrackersViewController: UICollectionViewDelegate { return UIMenu(children: [ UIAction(title: firstContextText) { [weak self] _ in guard let self, - let category = self.resultTypeHandlerGetValue(viewModel.getCategory()) + let cat = self.handler.resultTypeHandlerGetValue(viewModel.getCategory()) else { return } - let tracker = Tracker( id: selectedTracker.id, - name: selectedTracker.name, - color: selectedTracker.color, - emoji: selectedTracker.emoji, - schedule: selectedTracker.schedule) if selectorCategory.isPinned { - resultTypeHandler(viewModel.addPinnedCategory(id: selectedTracker.id, nameCategory: selectorCategory.nameCategory)) {} - self.resultTypeHandler(viewModel.deleteTracker(tracker.id)) {} - let filterNameCategory = category.filter { $0.nameCategory == ConstantsTrackerVc.textFixed } + handler.resultTypeHandler(viewModel.addPinnedCategory(id: selectedTracker.id, nameCategory: selectorCategory.nameCategory)) {} + self.handler.resultTypeHandler(viewModel.deleteTracker(selectedTracker.id)) {} + let filterNameCategory = cat.filter { $0.nameCategory == ConstantsTrackerVc.textFixed } if let _ = filterNameCategory.first?.nameCategory { - self.resultTypeHandler(viewModel.addNewTracker(tracker, nameCategory: ConstantsTrackerVc.textFixed)) {} + self.handler.resultTypeHandler(viewModel.addNewTracker(selectedTracker, nameCategory: ConstantsTrackerVc.textFixed)) {} } else { - self.resultTypeHandler(viewModel.addCategory(nameCategory: ConstantsTrackerVc.textFixed)) {} - self.resultTypeHandler(viewModel.addNewTracker(tracker, nameCategory: ConstantsTrackerVc.textFixed)) {} + self.handler.resultTypeHandler(viewModel.addCategory(nameCategory: ConstantsTrackerVc.textFixed)) {} + self.handler.resultTypeHandler(viewModel.addNewTracker(selectedTracker, nameCategory: ConstantsTrackerVc.textFixed)) {} } } else { - if let nameCategory = resultTypeHandlerGetValue(viewModel.deleteAndGetPinnedCategory(id: selectedTracker.id)) { - if let category = resultTypeHandlerGetValue(viewModel.category)?.filter({ $0.nameCategory == nameCategory }).first { - self.resultTypeHandler(viewModel.addNewTracker(tracker, nameCategory: category.nameCategory)) {} + if let nameCategory = handler.resultTypeHandlerGetValue(viewModel.deleteAndGetPinnedCategory(id: selectedTracker.id)) { + self.handler.resultTypeHandler(viewModel.deleteTracker(selectedTracker.id)) {} + if let category = handler.resultTypeHandlerGetValue(viewModel.category)?.filter({ $0.nameCategory == nameCategory }).first { + self.handler.resultTypeHandler(viewModel.addNewTracker(selectedTracker, nameCategory: category.nameCategory)) {} } else { guard let nameCategory else { return } - self.resultTypeHandler(viewModel.addCategory(nameCategory: nameCategory)) {} - self.resultTypeHandler(viewModel.addNewTracker(tracker, nameCategory: nameCategory)) {} + self.handler.resultTypeHandler(viewModel.addCategory(nameCategory: nameCategory)) {} + self.handler.resultTypeHandler(viewModel.addNewTracker(selectedTracker, nameCategory: nameCategory)) {} } } - self.resultTypeHandler(viewModel.deleteTracker(selectedTracker.id)) {} } + guard handler.resultTypeHandlerGetValue(viewModel.category)?.filter ({ $0.nameCategory == ConstantsTrackerVc.textFixed }).first?.arrayTrackers.count == 0 else { return } + handler.resultTypeHandler(viewModel.deleteCategory(nameCategory: ConstantsTrackerVc.textFixed)) {} }, UIAction(title: secondContentText) { [weak self] _ in guard let self else { return } - self.resultTypeHandler(viewModel.updateTracker(tracker: selectedTracker)) {} + let viewModel = CategoryViewModel() + let createTrackerVC = CreateTrackerViewController(viewModel: viewModel, updateTrackerDelegate: self, createTrackerDelegate: nil) + delegate = createTrackerVC + if selectedTracker.schedule.count != viewModel.regular.count { + createTrackerVC.reverseIsSchedul() + } + createTrackerVC.modalPresentationStyle = .fullScreen + present(createTrackerVC, animated: true) { + self.delegate?.editTracker(vc: self, + editTracker: selectedTracker, + nameCategory: selectorCategory.nameCategory, indexPath: indexPath) + } }, UIAction(title: thirdContentText, attributes: .destructive) { _ in } ]) @@ -455,8 +451,8 @@ extension TrackersViewController: UICollectionViewDelegateFlowLayout { } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize { - guard let viewModel = viewModel as? ViewModel, - let visibleCategories = resultTypeHandlerGetValue(viewModel.visibleCategory) + guard let viewModel = viewModel as? TrackerViewModel, + let visibleCategories = handler.resultTypeHandlerGetValue(viewModel.visibleCategory) else { return CGSize() } let indexPath = IndexPath(row: visibleCategories.count, section: section) let headerView = self.collectionView(collectionView, viewForSupplementaryElementOfKind: UICollectionView.elementKindSectionHeader, at: indexPath) @@ -468,8 +464,8 @@ extension TrackersViewController: UICollectionViewDelegateFlowLayout { switch kind { case UICollectionView.elementKindSectionHeader: guard let supplementary = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "\(HeaderReusableView.self)", for: indexPath) as? HeaderReusableView, - let viewModel = viewModel as? ViewModel, - let visibleCategories = resultTypeHandlerGetValue(viewModel.visibleCategory) + let viewModel = viewModel as? TrackerViewModel, + let visibleCategories = handler.resultTypeHandlerGetValue(viewModel.visibleCategory) else { return UICollectionReusableView() } supplementary.label.text = visibleCategories[indexPath.section].nameCategory return supplementary @@ -479,20 +475,6 @@ extension TrackersViewController: UICollectionViewDelegateFlowLayout { } } -//MARK: - TrackersCollectionViewCellDelegate -extension TrackersViewController: TrackersCollectionViewCellDelegate { - func didTrackerCompleted(_ cell: UICollectionViewCell) { - guard let trackerCell = cell as? TrackersCollectionViewCell, - let indexPath = trackerCollectionView.indexPath(for: trackerCell), - let viewModel = viewModel as? ViewModel, - let visibleCategories = resultTypeHandlerGetValue(viewModel.visibleCategory) - else { return } - let tracker = visibleCategories[indexPath.section].arrayTrackers[indexPath.row] - viewModel.setIndexPath(indexPath) - resultTypeHandler(viewModel.updateCompletedTrackers(tracker: tracker, date: datePicker.date)) {} - } -} - //MARK: - UISearchResultsUpdating extension TrackersViewController: UISearchResultsUpdating { func updateSearchResults(for searchController: UISearchController) { @@ -500,7 +482,7 @@ extension TrackersViewController: UISearchResultsUpdating { let viewModel else { return } if !word.isEmpty { - guard let seachCategory = resultTypeHandlerGetValue(viewModel.filterListTrackersName(word: word)) else { return } + guard let seachCategory = handler.resultTypeHandlerGetValue(viewModel.filterListTrackersName(word: word)) else { return } viewModel.getShowListTrackerSearchForName(seachCategory) return } @@ -517,14 +499,39 @@ extension TrackersViewController: UISearchResultsUpdating { } } +//MARK: - TrackersCollectionViewCellDelegate +extension TrackersViewController: TrackersCollectionViewCellDelegate { + func didTrackerCompleted(_ cell: UICollectionViewCell) { + guard let trackerCell = cell as? TrackersCollectionViewCell, + let indexPath = trackerCollectionView.indexPath(for: trackerCell), + let viewModel = viewModel as? TrackerViewModel, + let visibleCategories = handler.resultTypeHandlerGetValue(viewModel.visibleCategory) + else { return } + let tracker = visibleCategories[indexPath.section].arrayTrackers[indexPath.row] + viewModel.setIndexPath(indexPath) + handler.resultTypeHandler(viewModel.updateCompletedTrackers(tracker: tracker, date: datePicker.date)) {} + } +} + //MARK: - EventSelectionViewControllerDelegate extension TrackersViewController: EventSelectionViewControllerDelegate { func eventSelectionViewController(vc: UIViewController, nameCategories: String, tracker: Tracker) { guard let viewModel else { return } - resultTypeHandler(viewModel.addNewTracker(tracker, nameCategory: nameCategories)) {} + handler.resultTypeHandler(viewModel.addNewTracker(tracker, nameCategory: nameCategories)) {} } func eventSelectionViewControllerDidCancel(_ vc: EventSelectionViewController) { vc.dismiss(animated: false) } } + +extension TrackersViewController: UpdateTrackerViewControllerDelegate { + func trackerViewController(vc: UIViewController, nameCategory: String, tracker: Tracker) { + guard let viewModel else { return } + handler.resultTypeHandler(viewModel.updateTracker(tracker: tracker, nameCategory: nameCategory)) {} + } + + func trackerViewControllerDidCancel(_ vc: CreateTrackerViewController) { + vc.dismiss(animated: true) + } +} diff --git a/Tracker/ViewModel/CategoryViewModel.swift b/Tracker/ViewModel/CategoryViewModel.swift new file mode 100644 index 0000000..3810c24 --- /dev/null +++ b/Tracker/ViewModel/CategoryViewModel.swift @@ -0,0 +1,157 @@ +// +// CategoryViewModel.swift +// Tracker +// +// Created by Григорий Машук on 13.12.23. +// + +import UIKit + +protocol CategoryViewModelProtocol { + var isSchedul: Bool { get } + var listSettings: [ChoiceParametrs] { get } + var dataSection: [Header] { get } + var emojies: [String] { get } + var colors: [UIColor] { get } + + func reverseIsSchedul() + func checkingForEmptiness() -> Bool + func jonedSchedule(schedule: [WeekDay], stringArrayDay: String) -> String + func setListWeekDay(listWeekDay: [WeekDay]) + func getColorRow(color: UIColor) -> Int + func getEmojiRow(emoji: String) -> Int + + func setSchedule(_ vc: CreateTrackerViewController, schedule: [WeekDay]) + func setColor(_ vc: CreateTrackerViewController, color: UIColor) + func setNameNewCategory(_ vc: CreateTrackerViewController, nameCategory: String) + func setNameTracker(_ vc: CreateTrackerViewController, nameTracker: String) + func setEmojiTracker(_ vc: CreateTrackerViewController, emoji: String) + func editTracker(vc: CreateTrackerViewController, editTracker: Tracker, nameCategory: String) +} + +//MARK: - CategoryViewModel +final class CategoryViewModel { + var isSchedul: Bool = true + var listSettings: [ChoiceParametrs] { isSchedul ? [.category] : [.category, .schedule] } + let dataSection: [Header] = [.emoji, .color] + let emojies: [String] = ["🙂", "😻", "🌺", "🐶", "❤️", "😱", "😇", "😡", "🥶", + "🤔", "🙌", "🍔", "🥦", "🏓", "🥇", "🎸", "🏝️", "😪"] + + let colors: [UIColor] = [.colorSelection1, .colorSelection2, .colorSelection3,.colorSelection4, + .colorSelection5, .colorSelection6, .colorSelection7, .colorSelection8, + .colorSelection9, .colorSelection10, .colorSelection11, .colorSelection12, + .colorSelection13, .colorSelection14, .colorSelection15, .colorSelection16, + .colorSelection17, .colorSelection18] + + let regular: [WeekDay] = [.monday, .tuesday, .wednesday, .thursday, .friday, .saturday, .sunday] + + @Observable<[WeekDay]> private(set) var schedule: [WeekDay] = [] + @Observable private(set) var color: UIColor? + @Observable private(set) var nameNewCategory: String = "" + @Observable private(set) var nameTracker: String = "" + @Observable private(set) var emoji: String = "" + private(set) var id: UUID? + + private var marshalling: UIColorMarshalling + + convenience init() { + let marshalling = UIColorMarshalling() + self.init(marshalling: marshalling) + } + + init(marshalling: UIColorMarshalling) { + self.marshalling = marshalling + } +} + +//MARK: - CategoryViewModelProtocol +extension CategoryViewModel: CategoryViewModelProtocol { + func checkingForEmptiness() -> Bool { + var flag: Bool + if isSchedul { + flag = !nameTracker.isEmpty && + color != nil && + !emoji.isEmpty && + !nameNewCategory.isEmpty ? true : false + + return flag + } + flag = !schedule.isEmpty && + !nameTracker.isEmpty && + color != nil && + !emoji.isEmpty && + !nameNewCategory.isEmpty ? true : false + return flag + } + + func reverseIsSchedul() { + isSchedul = isSchedul ? !isSchedul : isSchedul + } + + func jonedSchedule(schedule: [WeekDay], stringArrayDay: String) -> String { + var stringListDay: String + if schedule.count == 7 { + stringListDay = stringArrayDay + return stringListDay + } + let listDay = schedule.map { $0.briefWordDay } + stringListDay = listDay.joined(separator: ",") + + return stringListDay + } + + func setListWeekDay(listWeekDay: [WeekDay]) { + schedule = listWeekDay + } + + func getColorRow(color: UIColor) -> Int { + var colorRow: Int = 0 + for (index, value) in colors.enumerated() { + let colorMarshalling = marshalling.color(from: marshalling.hexString(from: value)) + if colorMarshalling == color { + colorRow = index + } + } + return colorRow + } + + func getEmojiRow(emoji: String) -> Int { + var emojiRow: Int = 0 + for (index, value) in emojies.enumerated() { + if value == emoji { + emojiRow = index + } + } + return emojiRow + } + + func setSchedule(_ vc: CreateTrackerViewController, schedule: [WeekDay]) { + self.schedule = schedule + } + + func setColor(_ vc: CreateTrackerViewController, color: UIColor) { + self.color = color + } + + func setNameNewCategory(_ vc: CreateTrackerViewController, nameCategory: String) { + self.nameNewCategory = nameCategory + } + + func setNameTracker(_ vc: CreateTrackerViewController, nameTracker: String) { + self.nameTracker = nameTracker + } + + func setEmojiTracker(_ vc: CreateTrackerViewController, emoji: String) { + self.emoji = emoji + } + + func editTracker(vc: CreateTrackerViewController, editTracker: Tracker, nameCategory: String) { + schedule = editTracker.schedule + color = editTracker.color + nameNewCategory = nameCategory + nameTracker = editTracker.name + emoji = editTracker.emoji + id = editTracker.id + } +} + diff --git a/Tracker/ViewModel/ShedulViewModel.swift b/Tracker/ViewModel/ShedulViewModel.swift new file mode 100644 index 0000000..590988b --- /dev/null +++ b/Tracker/ViewModel/ShedulViewModel.swift @@ -0,0 +1,26 @@ +// +// ShedulViewModel.swift +// Tracker +// +// Created by Григорий Машук on 14.12.23. +// + +import Foundation + +protocol SheduleViewModelProtocol { + var weekDay: [WeekDay] { get } + var listWeekDay: [WeekDay] { get } + func setListWeekDay(listWeekDay: [WeekDay]) +} + +final class SheduleViewModel { + let weekDay: [WeekDay] = [.monday, .tuesday, .wednesday, .thursday, .friday, .saturday, .sunday] + private(set) var listWeekDay: [WeekDay] = [] +} + +extension SheduleViewModel: SheduleViewModelProtocol { + func setListWeekDay(listWeekDay: [WeekDay]) { + self.listWeekDay = listWeekDay + } +} + diff --git a/Tracker/ViewModel/ViewModel.swift b/Tracker/ViewModel/TrackerViewModel.swift similarity index 93% rename from Tracker/ViewModel/ViewModel.swift rename to Tracker/ViewModel/TrackerViewModel.swift index 9f9983e..992bc2c 100644 --- a/Tracker/ViewModel/ViewModel.swift +++ b/Tracker/ViewModel/TrackerViewModel.swift @@ -7,7 +7,7 @@ import Foundation -protocol ViewModelProtocol { +protocol TrackerViewModelProtocol { func isCategorySelected(at index: Int) -> Result func getCategory() -> Result<[TrackerCategory], Error> func deleteTracker(_ id: UUID) -> Result @@ -21,7 +21,7 @@ protocol ViewModelProtocol { func updateCompletedTrackers(tracker: Tracker, date: Date) -> Result func addNewTracker(_ tracker: Tracker, nameCategory: String) -> Result - func updateTracker(tracker: Tracker) -> Result + func updateTracker(tracker: Tracker, nameCategory: String) -> Result func getShowListTrackersForDay(date: Date) func getShowListTrackerSearchForName(_ searchCategory: [TrackerCategory]) @@ -34,7 +34,7 @@ protocol ViewModelProtocol { } //MARK: - ViewModel -final class ViewModel { +final class TrackerViewModel { @Observable> private(set) var category: Result<[TrackerCategory], Error> @Observable private(set) var indexPath: IndexPath @Observable> private(set) var visibleCategory: Result<[TrackerCategory], Error> @@ -62,6 +62,7 @@ final class ViewModel { indexPath: IndexPath()) trackerCategoryStore.delegate = self trackerRecordStore.delegate = self + trackerStore.delegate = self category = getCategory() completedTrackers = treckersRecordsResult() } @@ -86,7 +87,7 @@ final class ViewModel { } } //MARK: - Extension -private extension ViewModel { +private extension TrackerViewModel { func category(from trackerCategoryCoreData: TrackerCategoryCoreData) throws -> TrackerCategory { guard let nameCategory = trackerCategoryCoreData.nameCategory else { throw StoreErrors.TrackrerCategoryStoreError.decodingErrorInvalidNameCategori @@ -140,7 +141,7 @@ private extension ViewModel { } //MARK: - CategoriViewModelProtocol -extension ViewModel: ViewModelProtocol { +extension TrackerViewModel: TrackerViewModelProtocol { func deleteCategory(nameCategory: String) -> Result { trackerCategoryStore.deleteCategory(nameCategory: nameCategory) } @@ -206,8 +207,8 @@ extension ViewModel: ViewModelProtocol { return trackerCoreData } - func updateTracker(tracker: Tracker) -> Result { - trackerStore.updateTracker(tracker: tracker) + func updateTracker(tracker: Tracker, nameCategory: String) -> Result { + trackerStore.updateTracker(tracker: tracker, nameCategory: nameCategory) } func getShowListTrackersForDay(date: Date) { @@ -284,16 +285,23 @@ extension ViewModel: ViewModelProtocol { } //MARK: - TrackerCategoryStoreDelegate -extension ViewModel: TrackerCategoryStoreDelegate { +extension TrackerViewModel: TrackerCategoryStoreDelegate { func storeCategory(trackerCategoryStore: TrackerCategoryStoreProtocol) { category = getCategory() } } //MARK: - TrackerRecordStoreDelegate -extension ViewModel: TrackerRecordStoreDelegate { +extension TrackerViewModel: TrackerRecordStoreDelegate { func trackerRecordStore(trackerRecordStore: TrackerRecordStoreProtocol) { completedTrackers = trackerRecordStore.treckersRecordsResult() self.indexPath = indexPath } } + +//MARK: - TrackerStoreDelegate +extension TrackerViewModel: TrackerStoreDelegate { + func updateTracker(_ trackerCoreData: TrackerStoreProtocol) { + category = getCategory() + } +} From 7b2b496d402c15c77a8e14e1c3c7e80ec1ea5bbf Mon Sep 17 00:00:00 2001 From: Grifon Date: Sat, 16 Dec 2023 18:54:50 +0300 Subject: [PATCH 04/24] added deleting tracker via menu --- Tracker.xcodeproj/project.pbxproj | 12 +- .../xcdebugger/Breakpoints_v2.xcbkptlist | 1092 ++--------------- Tracker/Helpers/HandlerResultType.swift | 2 +- .../Resources/en.lproj/Localizable.strings | 1 + .../Resources/ru.lproj/Localizable.strings | 1 + .../CreateCategoriesViewController.swift | 71 +- .../CreateTrackerViewController.swift | 14 +- .../EventSelectionViewController.swift | 2 +- .../NewCategoriViewController.swift | 4 +- .../TrackersViewController.swift | 137 ++- Tracker/ViewModel/CategoryViewModel.swift | 225 ++-- Tracker/ViewModel/EditTrackerViewModel.swift | 157 +++ Tracker/ViewModel/TrackerViewModel.swift | 48 +- 13 files changed, 542 insertions(+), 1224 deletions(-) create mode 100644 Tracker/ViewModel/EditTrackerViewModel.swift diff --git a/Tracker.xcodeproj/project.pbxproj b/Tracker.xcodeproj/project.pbxproj index cdbbb79..282787b 100644 --- a/Tracker.xcodeproj/project.pbxproj +++ b/Tracker.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 1A0077F82B2DD6DF000EAE69 /* CategoryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A0077F72B2DD6DF000EAE69 /* CategoryViewModel.swift */; }; 1A14106A2ADD5185001D6DE6 /* ColorCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A1410692ADD5185001D6DE6 /* ColorCollectionViewCell.swift */; }; 1A19A7462B1672A200DD415B /* UpdateTrackerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A19A7452B1672A200DD415B /* UpdateTrackerModel.swift */; }; 1A1E94B52AD952EF00FE9A0C /* CreateCategoriesTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A1E94B42AD952EF00FE9A0C /* CreateCategoriesTableViewCell.swift */; }; @@ -30,7 +31,7 @@ 1A598BB82AC6FAC5009B166D /* TrackersViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A598BB72AC6FAC5009B166D /* TrackersViewController.swift */; }; 1A66BB192B25D16500838F5F /* PinnTrackerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A66BB182B25D16500838F5F /* PinnTrackerModel.swift */; }; 1A704D3F2B2B76C60035C94D /* EmojiCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A704D3E2B2B76C60035C94D /* EmojiCollectionViewCell.swift */; }; - 1A760A9D2B29EDC900B0C1CC /* CategoryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A760A9C2B29EDC900B0C1CC /* CategoryViewModel.swift */; }; + 1A760A9D2B29EDC900B0C1CC /* EditTrackerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A760A9C2B29EDC900B0C1CC /* EditTrackerViewModel.swift */; }; 1A83156E2B2C786000097DE7 /* HandlerResultType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A83156D2B2C786000097DE7 /* HandlerResultType.swift */; }; 1A84F3792AC87F4E001DD42F /* GeometricParams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A84F3782AC87F4E001DD42F /* GeometricParams.swift */; }; 1A8A576C2ACD5F4D00C060FA /* CreateTrackerTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A8A576B2ACD5F4D00C060FA /* CreateTrackerTableViewCell.swift */; }; @@ -67,6 +68,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 1A0077F72B2DD6DF000EAE69 /* CategoryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CategoryViewModel.swift; sourceTree = ""; }; 1A1410692ADD5185001D6DE6 /* ColorCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorCollectionViewCell.swift; sourceTree = ""; }; 1A19A7452B1672A200DD415B /* UpdateTrackerModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateTrackerModel.swift; sourceTree = ""; }; 1A1E94B42AD952EF00FE9A0C /* CreateCategoriesTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateCategoriesTableViewCell.swift; sourceTree = ""; }; @@ -91,7 +93,7 @@ 1A598BB72AC6FAC5009B166D /* TrackersViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackersViewController.swift; sourceTree = ""; }; 1A66BB182B25D16500838F5F /* PinnTrackerModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinnTrackerModel.swift; sourceTree = ""; }; 1A704D3E2B2B76C60035C94D /* EmojiCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiCollectionViewCell.swift; sourceTree = ""; }; - 1A760A9C2B29EDC900B0C1CC /* CategoryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CategoryViewModel.swift; sourceTree = ""; }; + 1A760A9C2B29EDC900B0C1CC /* EditTrackerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditTrackerViewModel.swift; sourceTree = ""; }; 1A83156D2B2C786000097DE7 /* HandlerResultType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandlerResultType.swift; sourceTree = ""; }; 1A84F3782AC87F4E001DD42F /* GeometricParams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeometricParams.swift; sourceTree = ""; }; 1A8A576B2ACD5F4D00C060FA /* CreateTrackerTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateTrackerTableViewCell.swift; sourceTree = ""; }; @@ -318,8 +320,9 @@ isa = PBXGroup; children = ( 1A9AC5B32AF37AED00A9213D /* TrackerViewModel.swift */, - 1A760A9C2B29EDC900B0C1CC /* CategoryViewModel.swift */, + 1A760A9C2B29EDC900B0C1CC /* EditTrackerViewModel.swift */, 1AF05E052B2B011D0099D022 /* ShedulViewModel.swift */, + 1A0077F72B2DD6DF000EAE69 /* CategoryViewModel.swift */, ); path = ViewModel; sourceTree = ""; @@ -485,6 +488,7 @@ files = ( 1A9AC5AF2AF373F000A9213D /* Observable.swift in Sources */, 1A959AF42AE24C44001C0EAE /* UIColorMarshalling.swift in Sources */, + 1A0077F82B2DD6DF000EAE69 /* CategoryViewModel.swift in Sources */, 1A1E94B52AD952EF00FE9A0C /* CreateCategoriesTableViewCell.swift in Sources */, 1A8A576C2ACD5F4D00C060FA /* CreateTrackerTableViewCell.swift in Sources */, 1A598BB82AC6FAC5009B166D /* TrackersViewController.swift in Sources */, @@ -497,7 +501,7 @@ 1A45A67A2ADE8EDB0063F08C /* TrackerCategoryStore.swift in Sources */, 1ABABBF72AFC10AD007281E0 /* CreateCategoryCellModel.swift in Sources */, 1A32FF912ADC72880065E44C /* Header.swift in Sources */, - 1A760A9D2B29EDC900B0C1CC /* CategoryViewModel.swift in Sources */, + 1A760A9D2B29EDC900B0C1CC /* EditTrackerViewModel.swift in Sources */, 1A19A7462B1672A200DD415B /* UpdateTrackerModel.swift in Sources */, 1A8A7FE62AC55D970038CB42 /* AppDelegate.swift in Sources */, 1A3B83AA2AD56323003C3897 /* NameEvent.swift in Sources */, diff --git a/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index 58149d2..9ba1b62 100644 --- a/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -866,8 +866,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "466" - endingLineNumber = "466" + startingLineNumber = "508" + endingLineNumber = "508" landmarkName = "collectionView(_:viewForSupplementaryElementOfKind:at:)" landmarkType = "7"> @@ -1064,8 +1064,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "464" - endingLineNumber = "464" + startingLineNumber = "506" + endingLineNumber = "506" landmarkName = "collectionView(_:viewForSupplementaryElementOfKind:at:)" landmarkType = "7"> @@ -1172,8 +1172,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "449" - endingLineNumber = "449" + startingLineNumber = "491" + endingLineNumber = "491" landmarkName = "collectionView(_:layout:insetForSectionAt:)" landmarkType = "7"> @@ -1188,8 +1188,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "442" - endingLineNumber = "442" + startingLineNumber = "484" + endingLineNumber = "484" landmarkName = "collectionView(_:layout:sizeForItemAt:)" landmarkType = "7"> @@ -1204,8 +1204,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "463" - endingLineNumber = "463" + startingLineNumber = "505" + endingLineNumber = "505" landmarkName = "collectionView(_:viewForSupplementaryElementOfKind:at:)" landmarkType = "7"> @@ -1312,8 +1312,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "231" - endingLineNumber = "231" + startingLineNumber = "232" + endingLineNumber = "232" landmarkName = "registerCellAndHeader(collectionView:)" landmarkType = "7"> @@ -1328,8 +1328,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "458" - endingLineNumber = "458" + startingLineNumber = "500" + endingLineNumber = "500" landmarkName = "collectionView(_:layout:referenceSizeForHeaderInSection:)" landmarkType = "7"> @@ -1441,8 +1441,8 @@ endingColumnNumber = "9223372036854775807" startingLineNumber = "144" endingLineNumber = "144" - landmarkName = "EventSelectionViewController" - landmarkType = "21"> + landmarkName = "trackerViewControllerDidCancel(_:)" + landmarkType = "7"> @@ -1519,8 +1519,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "484" - endingLineNumber = "484" + startingLineNumber = "526" + endingLineNumber = "526" landmarkName = "updateSearchResults(for:)" landmarkType = "7"> @@ -1803,8 +1803,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "491" - endingLineNumber = "491" + startingLineNumber = "533" + endingLineNumber = "533" landmarkName = "updateSearchResults(for:)" landmarkType = "7"> @@ -1851,8 +1851,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "445" - endingLineNumber = "445" + startingLineNumber = "487" + endingLineNumber = "487" landmarkName = "collectionView(_:layout:sizeForItemAt:)" landmarkType = "7"> @@ -2876,8 +2876,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "492" - endingLineNumber = "492" + startingLineNumber = "534" + endingLineNumber = "534" landmarkName = "updateSearchResults(for:)" landmarkType = "7"> @@ -3052,8 +3052,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "287" - endingLineNumber = "287" + startingLineNumber = "259" + endingLineNumber = "259" landmarkName = "tableView(_:didSelectRowAt:)" landmarkType = "7"> @@ -3130,8 +3130,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "286" - endingLineNumber = "286" + startingLineNumber = "258" + endingLineNumber = "258" landmarkName = "tableView(_:didSelectRowAt:)" landmarkType = "7"> @@ -3146,8 +3146,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "264" - endingLineNumber = "264" + startingLineNumber = "236" + endingLineNumber = "236" landmarkName = "tableView(_:cellForRowAt:)" landmarkType = "7"> @@ -3162,8 +3162,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "273" - endingLineNumber = "273" + startingLineNumber = "245" + endingLineNumber = "245" landmarkName = "tableView(_:cellForRowAt:)" landmarkType = "7"> @@ -3178,8 +3178,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "267" - endingLineNumber = "267" + startingLineNumber = "239" + endingLineNumber = "239" landmarkName = "tableView(_:cellForRowAt:)" landmarkType = "7"> @@ -3735,9 +3735,9 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "305" - endingLineNumber = "305" - landmarkName = "didNewCategoriName(_:nameCategori:)" + startingLineNumber = "277" + endingLineNumber = "277" + landmarkName = "didNewCategoryName(_:nameCategory:)" landmarkType = "7"> @@ -3863,8 +3863,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "360" - endingLineNumber = "360" + startingLineNumber = "438" + endingLineNumber = "438" landmarkName = "collectionView(_:cellForItemAt:)" landmarkType = "7"> @@ -3879,8 +3879,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "385" - endingLineNumber = "385" + startingLineNumber = "463" + endingLineNumber = "463" landmarkName = "collectionView(_:contextMenuConfigurationForItemsAt:point:)" landmarkType = "7"> @@ -3895,8 +3895,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "374" - endingLineNumber = "374" + startingLineNumber = "452" + endingLineNumber = "452" landmarkName = "collectionView(_:contextMenuConfigurationForItemsAt:point:)" landmarkType = "7"> @@ -4382,8 +4382,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "149" - endingLineNumber = "149" + startingLineNumber = "150" + endingLineNumber = "150" landmarkName = "bind()" landmarkType = "7"> @@ -4460,8 +4460,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "152" - endingLineNumber = "152" + startingLineNumber = "153" + endingLineNumber = "153" landmarkName = "bind()" landmarkType = "7"> @@ -4476,8 +4476,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "520" - endingLineNumber = "520" + startingLineNumber = "562" + endingLineNumber = "562" landmarkName = "eventSelectionViewController(vc:nameCategories:tracker:)" landmarkType = "7"> @@ -4746,8 +4746,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "163" - endingLineNumber = "163" + startingLineNumber = "164" + endingLineNumber = "164" landmarkName = "bind()" landmarkType = "7"> @@ -4887,8 +4887,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "171" - endingLineNumber = "171" + startingLineNumber = "172" + endingLineNumber = "172" landmarkName = "bind()" landmarkType = "7"> @@ -4997,8 +4997,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "154" - endingLineNumber = "154" + startingLineNumber = "155" + endingLineNumber = "155" landmarkName = "bind()" landmarkType = "7"> @@ -5013,8 +5013,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "257" - endingLineNumber = "257" + startingLineNumber = "229" + endingLineNumber = "229" landmarkName = "tableView(_:cellForRowAt:)" landmarkType = "7"> @@ -5091,8 +5091,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "148" - endingLineNumber = "148" + startingLineNumber = "149" + endingLineNumber = "149" landmarkName = "bind()" landmarkType = "7"> @@ -5107,8 +5107,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "123" - endingLineNumber = "123" + startingLineNumber = "125" + endingLineNumber = "125" landmarkName = "viewDidLoad()" landmarkType = "7"> @@ -5281,289 +5281,120 @@ filePath = "Tracker/ViewModel/CategoryViewModel.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "73" - endingLineNumber = "73" - landmarkName = "checkingForEmptiness()" + startingLineNumber = "41" + endingLineNumber = "41" + landmarkName = "init(trackerCategoryStore:trackerStore:category:)" landmarkType = "7"> - - - - - - - - - - - - - - - - - - - - - - - - - - + startingLineNumber = "544" + endingLineNumber = "544" + offsetFromSymbolStart = "1048"> + startingLineNumber = "541" + endingLineNumber = "541" + offsetFromSymbolStart = "1048"> @@ -5571,62 +5402,47 @@ - - + startingLineNumber = "547" + endingLineNumber = "547" + offsetFromSymbolStart = "1252"> + startingLineNumber = "544" + endingLineNumber = "544" + offsetFromSymbolStart = "1252"> @@ -5634,547 +5450,16 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -6289,54 +5574,6 @@ - - - - - - - - - - @@ -6459,8 +5696,8 @@ filePath = "Tracker/ViewModel/TrackerViewModel.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "290" - endingLineNumber = "290" + startingLineNumber = "270" + endingLineNumber = "270" landmarkName = "storeCategory(trackerCategoryStore:)" landmarkType = "7"> @@ -6475,8 +5712,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "531" - endingLineNumber = "531" + startingLineNumber = "574" + endingLineNumber = "574" landmarkName = "trackerViewController(vc:nameCategory:tracker:)" landmarkType = "7"> @@ -6849,69 +6086,6 @@ - - - - - - - - - - - - diff --git a/Tracker/Helpers/HandlerResultType.swift b/Tracker/Helpers/HandlerResultType.swift index 7c06b51..c571e0b 100644 --- a/Tracker/Helpers/HandlerResultType.swift +++ b/Tracker/Helpers/HandlerResultType.swift @@ -27,7 +27,7 @@ final class HandlerResultType { } } - func showMessageErrorAlert(message: String) { + private func showMessageErrorAlert(message: String) { let alert = UIAlertController(title: nil, message: message, preferredStyle: .alert) let action = UIAlertAction(title: "Ok", style: .cancel) { _ in alert.dismiss(animated: true) diff --git a/Tracker/Resources/en.lproj/Localizable.strings b/Tracker/Resources/en.lproj/Localizable.strings index 6af2d41..b431e12 100644 --- a/Tracker/Resources/en.lproj/Localizable.strings +++ b/Tracker/Resources/en.lproj/Localizable.strings @@ -27,6 +27,7 @@ "textEdit" = "Edit"; "textDelete" = "Delete"; "textFixed" = "Fixed"; +"sureDeleteTracker" = "are you sure you want to delete the tracker"; //MARK: - CreateTrackerViewController "placeholderTrackerTextField" = "Enter the name of the tracker"; diff --git a/Tracker/Resources/ru.lproj/Localizable.strings b/Tracker/Resources/ru.lproj/Localizable.strings index 464b8d5..2e99009 100644 --- a/Tracker/Resources/ru.lproj/Localizable.strings +++ b/Tracker/Resources/ru.lproj/Localizable.strings @@ -26,6 +26,7 @@ "textEdit" = "Редактировать"; "textDelete" = "Удалить"; "textFixed" = "Закрепленные"; +"sureDeleteTracker" = "Уверены что хотите удалить трекер?"; //MARK: - CreateTrackerViewController "placeholderTrackerTextField" = "Введите название трекера"; diff --git a/Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift b/Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift index 349c8ca..15d0e75 100644 --- a/Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift +++ b/Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift @@ -35,7 +35,9 @@ final class CreateCategoriesViewController: UIViewController { } weak var delegate: CreateCategoriesViewControllerDelegate? - private var viewModel: TrackerViewModelProtocol + private var viewModel: CategoryViewModelProtocol + + private let handler = HandlerResultType() private lazy var categoriLabel: UILabel = { let categoriLabel = UILabel() @@ -102,7 +104,7 @@ final class CreateCategoriesViewController: UIViewController { return createCategoriButton }() - init(delegate: CreateCategoriesViewControllerDelegate, viewModel: TrackerViewModelProtocol) { + init(delegate: CreateCategoriesViewControllerDelegate, viewModel: CategoryViewModelProtocol) { self.delegate = delegate self.viewModel = viewModel super.init(nibName: nil, bundle: nil) @@ -114,11 +116,11 @@ final class CreateCategoriesViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - guard let viewModel = viewModel as? TrackerViewModel else { return } + guard let viewModel = viewModel as? CategoryViewModel else { return } setupUIElement() bind() view.backgroundColor = .whiteDay - resultTypeHandler(viewModel.category) { [weak self] cat in + handler.resultTypeHandler(viewModel.сategoryExcludingFixed()) { [weak self] cat in guard let self else { return } self.showStabView(flag: !cat.isEmpty) } @@ -139,49 +141,21 @@ extension CreateCategoriesViewController { present(newCategoryVC, animated: true) } - // метод показа/скрытия заглушки private func showStabView(flag: Bool) { conteinerStabView.isHidden = flag } private func bind() { - guard let viewModel = viewModel as? TrackerViewModel else { return } + guard let viewModel = viewModel as? CategoryViewModel else { return } viewModel.$category.bind { [weak self] _ in guard let self else { return } - resultTypeHandler(viewModel.category) { cat in + handler.resultTypeHandler(viewModel.сategoryExcludingFixed()) { cat in self.showStabView(flag: !cat.isEmpty) } selectionCategoryTableView.reloadData() } } - private func resultTypeHandler(_ value: Result, handler: (Value) -> Void) { - switch value { - case .success(let newValue): - handler(newValue) - case .failure(let error): - showMessageErrorAlert(message: error.localizedDescription) - } - } - - private func resultTypeHandlerGetValue(_ value: Result) -> Value? { - switch value { - case .success(let newValue): - return newValue - case .failure(let error): - showMessageErrorAlert(message: error.localizedDescription) - return nil - } - } - - private func showMessageErrorAlert(message: String) { - let alert = UIAlertController(title: nil, message: message, preferredStyle: .alert) - let action = UIAlertAction(title: ConstantsCreateCatVc.alertActionErrorTitle, style: .cancel) { _ in - alert.dismiss(animated: true) - } - alert.addAction(action) - } - //MARK: - SetupUI private func setupUIElement() { setupCategoriButton() @@ -248,26 +222,24 @@ extension CreateCategoriesViewController { //MARK: - UITableViewDataSource extension CreateCategoriesViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - guard let count = resultTypeHandlerGetValue(viewModel.getCategory())?.count - else { return .zero } - return count + handler.resultTypeHandlerGetValue(viewModel.сategoryExcludingFixed())?.count ?? 0 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { guard let cell = tableView.dequeueReusableCell(withIdentifier: "\(CreateCategoriesTableViewCell.self)") as? CreateCategoriesTableViewCell, - let isSelected = resultTypeHandlerGetValue(viewModel.isCategorySelected(at: indexPath.row)), - let text = resultTypeHandlerGetValue(viewModel.createNameCategory(at: indexPath.row)), - let count = resultTypeHandlerGetValue(viewModel.getCategory())?.count + let isSelected = handler.resultTypeHandlerGetValue(viewModel.isCategorySelected(at: indexPath.row)), + let text = handler.resultTypeHandlerGetValue(viewModel.createNameCategory(at: indexPath.row)), + let category = handler.resultTypeHandlerGetValue(viewModel.сategoryExcludingFixed()) else { return UITableViewCell() } cell.showSelectedImage(flag: isSelected) let model = CreateCategoryCellModel(text: text, color: ConstantsCreateCatVc.backgroundColorCell) cell.config(model: model) - if count > 1 { + if category.count > 1 { cell.separatorInset = ConstantsCreateCatVc.insertSeparatorTableView } - if indexPath.row == count - 1 { + if indexPath.row == category.count - 1 { cell.setupCornerRadius(cornerRadius: ConstantsCreateCatVc.cornerRadius, maskedCorners: [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]) cell.separatorInset = UIEdgeInsets(top: .zero, left: view.bounds.width, bottom: .zero, right: .zero) @@ -288,8 +260,8 @@ extension CreateCategoriesViewController: UITableViewDelegate { let delegate else { return } cell.showSelectedImage(flag: false) - resultTypeHandler(viewModel.selectСategory(at: indexPath.row)) {} - resultTypeHandler(viewModel.createNameCategory(at: indexPath.row)) { [weak self] cat in + handler.resultTypeHandler(viewModel.selectСategory(at: indexPath.row)) {} + handler.resultTypeHandler(viewModel.createNameCategory(at: indexPath.row)) { [weak self] cat in guard let self else { return } delegate.createCategoriesViewController(vc: self, nameCategory: cat) } @@ -298,13 +270,16 @@ extension CreateCategoriesViewController: UITableViewDelegate { //MARK: - NewCategoriViewControllerDelegate extension CreateCategoriesViewController: NewCategoriViewControllerDelegate { - func didNewCategoriName(_ vc: UIViewController, nameCategori: String) { - guard let category = resultTypeHandlerGetValue(viewModel.getCategory()) + func didNewCategoryName(_ vc: UIViewController, nameCategory: String) { + guard let category = handler.resultTypeHandlerGetValue(viewModel.getCategory()) else { return } - let nameFirstUppercased = nameCategori.lowercased().firstUppercased + let nameFirstUppercased = nameCategory.lowercased().firstUppercased if let _ = category.filter({ $0.nameCategory == nameFirstUppercased }).first { return } - resultTypeHandler(viewModel.addCategory(nameCategory: nameFirstUppercased)) {} + if nameFirstUppercased == ConstantsCreateCatVc.textFixed { + return + } + handler.resultTypeHandler(viewModel.addCategory(nameCategory: nameFirstUppercased)) {} } } diff --git a/Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift b/Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift index 174d1a5..b99d2da 100644 --- a/Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift +++ b/Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift @@ -49,7 +49,7 @@ final class CreateTrackerViewController: UIViewController { weak var updateTrackerDelegate: UpdateTrackerViewControllerDelegate? weak var createTrackerDelegate: CreateTrackerViewControllerDelegate? - private let viewModel: CategoryViewModelProtocol + private let viewModel: EditTrackerModelProtocol private let handlerResultType = HandlerResultType() private lazy var newHabitLabel: UILabel = { @@ -202,7 +202,7 @@ final class CreateTrackerViewController: UIViewController { return scrollView }() - init(viewModel: CategoryViewModelProtocol, + init(viewModel: EditTrackerModelProtocol, updateTrackerDelegate: UpdateTrackerViewControllerDelegate? = nil, createTrackerDelegate: CreateTrackerViewControllerDelegate? = nil) { @@ -229,7 +229,7 @@ final class CreateTrackerViewController: UIViewController { extension CreateTrackerViewController { func bind() { - guard let viewModel = viewModel as? CategoryViewModel else { return } + guard let viewModel = viewModel as? EditTrackerViewModel else { return } viewModel.$schedule.bind { [weak self] _ in guard let self else { return } self.chengeActivButton() @@ -272,7 +272,7 @@ extension CreateTrackerViewController { @objc private func didTapGreateButton() { - guard let viewModel = viewModel as? CategoryViewModel, + guard let viewModel = viewModel as? EditTrackerViewModel, let color = viewModel.color else { return } if let updateTrackerDelegate = updateTrackerDelegate, let id = viewModel.id { @@ -303,7 +303,7 @@ extension CreateTrackerViewController { @objc private func textFieldDidChange(_ textField: UITextField) { guard let text = textField.text, - let viewModel = viewModel as? CategoryViewModel + let viewModel = viewModel as? EditTrackerViewModel else { return } if text.count > ConstantsCreateVc.restrictionsTextField { characterRestrictionsView.isHidden = false @@ -408,7 +408,7 @@ extension CreateTrackerViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { guard let cell = tableView.dequeueReusableCell(withIdentifier: "\(CreateTrackerTableViewCell.self)") as? CreateTrackerTableViewCell, - let viewModel = viewModel as? CategoryViewModel + let viewModel = viewModel as? EditTrackerViewModel else { return UITableViewCell() } let secondarySchedulText = viewModel.jonedSchedule(schedule: viewModel.schedule, stringArrayDay: ConstantsCreateVc.everyDay) @@ -450,7 +450,7 @@ extension CreateTrackerViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { switch viewModel.listSettings[indexPath.row] { case .category: - let greateCategoriViewController = CreateCategoriesViewController(delegate: self, viewModel: TrackerViewModel()) + let greateCategoriViewController = CreateCategoriesViewController(delegate: self, viewModel: CategoryViewModel()) presentViewController(vc: greateCategoriViewController, modalStyle: .formSheet) case .schedule: let viewModel = SheduleViewModel() diff --git a/Tracker/TabBarController/TrackersViews/ViewControllers/EventSelectionViewController.swift b/Tracker/TabBarController/TrackersViews/ViewControllers/EventSelectionViewController.swift index 59aa85b..0075d36 100644 --- a/Tracker/TabBarController/TrackersViews/ViewControllers/EventSelectionViewController.swift +++ b/Tracker/TabBarController/TrackersViews/ViewControllers/EventSelectionViewController.swift @@ -82,7 +82,7 @@ private extension EventSelectionViewController { } func createTrackerVC() -> CreateTrackerViewController { - let viewModel = CategoryViewModel() + let viewModel = EditTrackerViewModel() let createVC = CreateTrackerViewController(viewModel: viewModel, updateTrackerDelegate: nil, createTrackerDelegate: self) diff --git a/Tracker/TabBarController/TrackersViews/ViewControllers/NewCategoriViewController.swift b/Tracker/TabBarController/TrackersViews/ViewControllers/NewCategoriViewController.swift index 2477336..7ba8435 100644 --- a/Tracker/TabBarController/TrackersViews/ViewControllers/NewCategoriViewController.swift +++ b/Tracker/TabBarController/TrackersViews/ViewControllers/NewCategoriViewController.swift @@ -8,7 +8,7 @@ import UIKit protocol NewCategoriViewControllerDelegate: AnyObject { - func didNewCategoriName(_ vc: UIViewController, nameCategori: String) + func didNewCategoryName(_ vc: UIViewController, nameCategory: String) } //MARK: - NewCategoriViewController @@ -86,7 +86,7 @@ private extension NewCategoriViewController { //MARK: - Обработка событий @objc func didTapNewСategoriButton() { - delegate?.didNewCategoriName(self, nameCategori: nameCategori) + delegate?.didNewCategoryName(self, nameCategory: nameCategori) dismiss(animated: true) } diff --git a/Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift b/Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift index 5bdf6f5..f72004d 100644 --- a/Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift +++ b/Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift @@ -30,6 +30,7 @@ final class TrackersViewController: UIViewController { static let textEdit = NSLocalizedString("textEdit", comment: "") static let textDelete = NSLocalizedString("textDelete", comment: "") static let textFixed = NSLocalizedString("textFixed", comment: "") + static let sureDeleteTracker = NSLocalizedString("sureDeleteTracker", comment: "") static let datePickerCornerRadius = CGFloat(8) static let heightCollectionView = CGFloat(148) @@ -233,6 +234,83 @@ private extension TrackersViewController { withReuseIdentifier: "\(HeaderReusableView.self)") } + //MARK: - Menu Action + + func pinnedAction(text: String, tracker: Tracker, category: TrackerCategory) -> UIAction { + UIAction(title: text) { [weak self] _ in + guard let self, + let viewModel, + let cat = self.handler.resultTypeHandlerGetValue(viewModel.getCategory()) + else { return } + if category.isPinned { + handler.resultTypeHandler(viewModel.addPinnedCategory(id: tracker.id, nameCategory: category.nameCategory)) {} + self.handler.resultTypeHandler(viewModel.deleteTracker(tracker.id)) {} + let filterNameCategory = cat.filter { $0.nameCategory == ConstantsTrackerVc.textFixed } + if let _ = filterNameCategory.first?.nameCategory { + self.handler.resultTypeHandler(viewModel.addNewTracker(tracker, nameCategory: ConstantsTrackerVc.textFixed)) {} + } else { + self.handler.resultTypeHandler(viewModel.addCategory(nameCategory: ConstantsTrackerVc.textFixed)) {} + self.handler.resultTypeHandler(viewModel.addNewTracker(tracker, nameCategory: ConstantsTrackerVc.textFixed)) {} + } + } else { + if let nameCategory = handler.resultTypeHandlerGetValue(viewModel.deleteAndGetPinnedCategory(id: tracker.id)) { + self.handler.resultTypeHandler(viewModel.deleteTracker(tracker.id)) {} + if let category = handler.resultTypeHandlerGetValue(viewModel.category)?.filter({ $0.nameCategory == nameCategory }).first { + self.handler.resultTypeHandler(viewModel.addNewTracker(tracker, nameCategory: category.nameCategory)) {} + } else { + guard let nameCategory else { return } + self.handler.resultTypeHandler(viewModel.addCategory(nameCategory: nameCategory)) {} + self.handler.resultTypeHandler(viewModel.addNewTracker(tracker, nameCategory: nameCategory)) {} + } + } + } + } + } + + func editTrackerAction(text: String, + tracker: Tracker, + category: TrackerCategory, + indexPath: IndexPath) -> UIAction + { + UIAction(title: text) { [weak self] _ in + guard let self else { return } + let viewModel = EditTrackerViewModel() + let createTrackerVC = CreateTrackerViewController(viewModel: viewModel, updateTrackerDelegate: self, createTrackerDelegate: nil) + delegate = createTrackerVC + if tracker.schedule.count != viewModel.regular.count { + createTrackerVC.reverseIsSchedul() + } + createTrackerVC.modalPresentationStyle = .formSheet + present(createTrackerVC, animated: true) { + self.delegate?.editTracker(vc: self, + editTracker: tracker, + nameCategory: category.nameCategory, + indexPath: indexPath) + } + } + } + + func deleteAction(text: String, tracker: Tracker) -> UIAction { + UIAction(title: text, attributes: .destructive) { [weak self] _ in + guard let self, let viewModel else { return } + let alert = UIAlertController(title: nil, + message: ConstantsTrackerVc.sureDeleteTracker, + preferredStyle: .actionSheet) + let deleteAction = UIAlertAction(title: ConstantsTrackerVc.textDelete, + style: .destructive) { _ in + self.handler.resultTypeHandler(viewModel.deleteTracker(tracker.id)) {} + alert.dismiss(animated: true) + } + let cancelAction = UIAlertAction(title: ConstantsTrackerVc.textCancel, + style: .cancel) { _ in + self.dismiss(animated: true) + } + alert.addAction(deleteAction) + alert.addAction(cancelAction) + present(alert, animated: true) + } + } + //MARK: - SetupUI func setupUIElement() { setupVerticalSteck() @@ -375,7 +453,7 @@ extension TrackersViewController: UICollectionViewDelegate { contextMenuConfigurationForItemsAt indexPaths: [IndexPath], point: CGPoint) -> UIContextMenuConfiguration? { guard let indexPath = indexPaths.first, - let viewModel = viewModel as? TrackerViewModel, + let viewModel, let visibleCategories = handler.resultTypeHandlerGetValue(viewModel.visibleCategory) else { return nil } let selectorCategory = visibleCategories[indexPath.section] @@ -383,53 +461,17 @@ extension TrackersViewController: UICollectionViewDelegate { let firstContextText = !selectorCategory.isPinned ? ConstantsTrackerVc.textUnpin : ConstantsTrackerVc.textFix let secondContentText = ConstantsTrackerVc.textEdit let thirdContentText = ConstantsTrackerVc.textDelete - return UIContextMenuConfiguration(actionProvider: { _ in + return UIContextMenuConfiguration(actionProvider: { [weak self] _ in + guard let self else { return UIMenu() } return UIMenu(children: [ - UIAction(title: firstContextText) { [weak self] _ in - guard let self, - let cat = self.handler.resultTypeHandlerGetValue(viewModel.getCategory()) - else { return } - if selectorCategory.isPinned { - handler.resultTypeHandler(viewModel.addPinnedCategory(id: selectedTracker.id, nameCategory: selectorCategory.nameCategory)) {} - self.handler.resultTypeHandler(viewModel.deleteTracker(selectedTracker.id)) {} - let filterNameCategory = cat.filter { $0.nameCategory == ConstantsTrackerVc.textFixed } - if let _ = filterNameCategory.first?.nameCategory { - self.handler.resultTypeHandler(viewModel.addNewTracker(selectedTracker, nameCategory: ConstantsTrackerVc.textFixed)) {} - } else { - self.handler.resultTypeHandler(viewModel.addCategory(nameCategory: ConstantsTrackerVc.textFixed)) {} - self.handler.resultTypeHandler(viewModel.addNewTracker(selectedTracker, nameCategory: ConstantsTrackerVc.textFixed)) {} - } - } else { - if let nameCategory = handler.resultTypeHandlerGetValue(viewModel.deleteAndGetPinnedCategory(id: selectedTracker.id)) { - self.handler.resultTypeHandler(viewModel.deleteTracker(selectedTracker.id)) {} - if let category = handler.resultTypeHandlerGetValue(viewModel.category)?.filter({ $0.nameCategory == nameCategory }).first { - self.handler.resultTypeHandler(viewModel.addNewTracker(selectedTracker, nameCategory: category.nameCategory)) {} - } else { - guard let nameCategory else { return } - self.handler.resultTypeHandler(viewModel.addCategory(nameCategory: nameCategory)) {} - self.handler.resultTypeHandler(viewModel.addNewTracker(selectedTracker, nameCategory: nameCategory)) {} - } - } - } - guard handler.resultTypeHandlerGetValue(viewModel.category)?.filter ({ $0.nameCategory == ConstantsTrackerVc.textFixed }).first?.arrayTrackers.count == 0 else { return } - handler.resultTypeHandler(viewModel.deleteCategory(nameCategory: ConstantsTrackerVc.textFixed)) {} - }, - UIAction(title: secondContentText) { [weak self] _ in - guard let self else { return } - let viewModel = CategoryViewModel() - let createTrackerVC = CreateTrackerViewController(viewModel: viewModel, updateTrackerDelegate: self, createTrackerDelegate: nil) - delegate = createTrackerVC - if selectedTracker.schedule.count != viewModel.regular.count { - createTrackerVC.reverseIsSchedul() - } - createTrackerVC.modalPresentationStyle = .fullScreen - present(createTrackerVC, animated: true) { - self.delegate?.editTracker(vc: self, - editTracker: selectedTracker, - nameCategory: selectorCategory.nameCategory, indexPath: indexPath) - } - }, - UIAction(title: thirdContentText, attributes: .destructive) { _ in } + self.pinnedAction(text: firstContextText, + tracker: selectedTracker, + category: selectorCategory), + self.editTrackerAction(text: secondContentText, + tracker: selectedTracker, + category: selectorCategory, + indexPath: indexPath), + self.deleteAction(text: thirdContentText, tracker: selectedTracker) ]) }) } @@ -525,6 +567,7 @@ extension TrackersViewController: EventSelectionViewControllerDelegate { } } +//MARK: - UpdateTrackerViewControllerDelegate extension TrackersViewController: UpdateTrackerViewControllerDelegate { func trackerViewController(vc: UIViewController, nameCategory: String, tracker: Tracker) { guard let viewModel else { return } diff --git a/Tracker/ViewModel/CategoryViewModel.swift b/Tracker/ViewModel/CategoryViewModel.swift index 3810c24..1c4eab8 100644 --- a/Tracker/ViewModel/CategoryViewModel.swift +++ b/Tracker/ViewModel/CategoryViewModel.swift @@ -1,157 +1,140 @@ // -// CategoryViewModel.swift +// CategoriViewModel.swift // Tracker // -// Created by Григорий Машук on 13.12.23. +// Created by Григорий Машук on 2.11.23. // -import UIKit +import Foundation protocol CategoryViewModelProtocol { - var isSchedul: Bool { get } - var listSettings: [ChoiceParametrs] { get } - var dataSection: [Header] { get } - var emojies: [String] { get } - var colors: [UIColor] { get } - - func reverseIsSchedul() - func checkingForEmptiness() -> Bool - func jonedSchedule(schedule: [WeekDay], stringArrayDay: String) -> String - func setListWeekDay(listWeekDay: [WeekDay]) - func getColorRow(color: UIColor) -> Int - func getEmojiRow(emoji: String) -> Int - - func setSchedule(_ vc: CreateTrackerViewController, schedule: [WeekDay]) - func setColor(_ vc: CreateTrackerViewController, color: UIColor) - func setNameNewCategory(_ vc: CreateTrackerViewController, nameCategory: String) - func setNameTracker(_ vc: CreateTrackerViewController, nameTracker: String) - func setEmojiTracker(_ vc: CreateTrackerViewController, emoji: String) - func editTracker(vc: CreateTrackerViewController, editTracker: Tracker, nameCategory: String) + func isCategorySelected(at index: Int) -> Result + func getCategory() -> Result<[TrackerCategory], Error> + func сategoryExcludingFixed() -> Result<[TrackerCategory], Error> + func createNameCategory(at index: Int) -> Result + func selectСategory(at index: Int) -> Result + func addCategory(nameCategory: String) -> Result } -//MARK: - CategoryViewModel +//MARK: - ViewModel final class CategoryViewModel { - var isSchedul: Bool = true - var listSettings: [ChoiceParametrs] { isSchedul ? [.category] : [.category, .schedule] } - let dataSection: [Header] = [.emoji, .color] - let emojies: [String] = ["🙂", "😻", "🌺", "🐶", "❤️", "😱", "😇", "😡", "🥶", - "🤔", "🙌", "🍔", "🥦", "🏓", "🥇", "🎸", "🏝️", "😪"] - - let colors: [UIColor] = [.colorSelection1, .colorSelection2, .colorSelection3,.colorSelection4, - .colorSelection5, .colorSelection6, .colorSelection7, .colorSelection8, - .colorSelection9, .colorSelection10, .colorSelection11, .colorSelection12, - .colorSelection13, .colorSelection14, .colorSelection15, .colorSelection16, - .colorSelection17, .colorSelection18] - - let regular: [WeekDay] = [.monday, .tuesday, .wednesday, .thursday, .friday, .saturday, .sunday] + private let textFixed = NSLocalizedString("textFixed", comment: "") - @Observable<[WeekDay]> private(set) var schedule: [WeekDay] = [] - @Observable private(set) var color: UIColor? - @Observable private(set) var nameNewCategory: String = "" - @Observable private(set) var nameTracker: String = "" - @Observable private(set) var emoji: String = "" - private(set) var id: UUID? + @Observable> private(set) var category: Result<[TrackerCategory], Error> + @UserDefaultsBacked(key: "select_name_category") private var selectNameCategory: String? - private var marshalling: UIColorMarshalling + private let trackerCategoryStore: TrackerCategoryStoreProtocol + private let trackerStore: TrackerStoreProtocol convenience init() { - let marshalling = UIColorMarshalling() - self.init(marshalling: marshalling) + let trackerCategoryStore = TrackerCategoryStore() + let trackerStore = TrackerStore() + self.init(trackerCategoryStore: trackerCategoryStore, + trackerStore: trackerStore, + category: .success([]) + ) + trackerCategoryStore.delegate = self + trackerStore.delegate = self + category = getCategory() } - init(marshalling: UIColorMarshalling) { - self.marshalling = marshalling + init(trackerCategoryStore: TrackerCategoryStoreProtocol, + trackerStore: TrackerStoreProtocol, + category: Result<[TrackerCategory], Error> +) + { + self.trackerCategoryStore = trackerCategoryStore + self.trackerStore = trackerStore + self.category = category } } - -//MARK: - CategoryViewModelProtocol -extension CategoryViewModel: CategoryViewModelProtocol { - func checkingForEmptiness() -> Bool { - var flag: Bool - if isSchedul { - flag = !nameTracker.isEmpty && - color != nil && - !emoji.isEmpty && - !nameNewCategory.isEmpty ? true : false - - return flag +//MARK: - Extension +private extension CategoryViewModel { + func category(from trackerCategoryCoreData: TrackerCategoryCoreData) throws -> TrackerCategory { + guard let nameCategory = trackerCategoryCoreData.nameCategory else { + throw StoreErrors.TrackrerCategoryStoreError.decodingErrorInvalidNameCategori } - flag = !schedule.isEmpty && - !nameTracker.isEmpty && - color != nil && - !emoji.isEmpty && - !nameNewCategory.isEmpty ? true : false - return flag - } - - func reverseIsSchedul() { - isSchedul = isSchedul ? !isSchedul : isSchedul - } - - func jonedSchedule(schedule: [WeekDay], stringArrayDay: String) -> String { - var stringListDay: String - if schedule.count == 7 { - stringListDay = stringArrayDay - return stringListDay + guard let arrayTrackersCoreData = trackerCategoryCoreData.trakers?.allObjects as? [TrackerCoreData] + else { throw StoreErrors.NSSetError.transformationErrorInvalid } + let result = trackerStore.getTrackers(arrayTrackersCoreData) + switch result { + case .success(let trackers): + return TrackerCategory(nameCategory: nameCategory, + arrayTrackers: trackers, + isPinned: trackerCategoryCoreData.isPinned) + case .failure(let error): + throw error } - let listDay = schedule.map { $0.briefWordDay } - stringListDay = listDay.joined(separator: ",") - - return stringListDay } - - func setListWeekDay(listWeekDay: [WeekDay]) { - schedule = listWeekDay +} + +//MARK: - TrackerViewModelProtocol +extension CategoryViewModel: CategoryViewModelProtocol { + func addCategory(nameCategory: String) -> Result { + trackerCategoryStore.addCategory(nameCategory) } - func getColorRow(color: UIColor) -> Int { - var colorRow: Int = 0 - for (index, value) in colors.enumerated() { - let colorMarshalling = marshalling.color(from: marshalling.hexString(from: value)) - if colorMarshalling == color { - colorRow = index - } + func isCategorySelected(at index: Int) -> Result { + switch category { + case .success(let category): + return .success(category[index].nameCategory != selectNameCategory) + case .failure(let error): + return .failure(error) } - return colorRow } - func getEmojiRow(emoji: String) -> Int { - var emojiRow: Int = 0 - for (index, value) in emojies.enumerated() { - if value == emoji { - emojiRow = index - } + func getCategory() -> Result<[TrackerCategory], Error> { + let trackerCategoryCoreData = trackerCategoryStore.getListTrackerCategoryCoreData() + guard let trackerCategoryCoreData else { return .failure(StoreErrors.TrackrerStoreError.getTrackerError) } + do { + let listCategory = try trackerCategoryCoreData.map ({ try category(from: $0) }) + return .success(listCategory) + } catch { + return .failure(error) } - return emojiRow } - - func setSchedule(_ vc: CreateTrackerViewController, schedule: [WeekDay]) { - self.schedule = schedule - } - - func setColor(_ vc: CreateTrackerViewController, color: UIColor) { - self.color = color - } - - func setNameNewCategory(_ vc: CreateTrackerViewController, nameCategory: String) { - self.nameNewCategory = nameCategory + + func сategoryExcludingFixed() -> Result<[TrackerCategory], Error> { + switch category { + case .success(let category): + let filterCat = category.filter { $0.nameCategory != textFixed } + return .success(filterCat) + case .failure(let error): + return .failure(error) + } } - func setNameTracker(_ vc: CreateTrackerViewController, nameTracker: String) { - self.nameTracker = nameTracker + func createNameCategory(at index: Int) -> Result { + switch сategoryExcludingFixed() { + case .success(let category): + let name = category[index].nameCategory + return .success(name) + case .failure(let error): + return .failure(error) + } } - func setEmojiTracker(_ vc: CreateTrackerViewController, emoji: String) { - self.emoji = emoji + func selectСategory(at index: Int) -> Result { + switch category { + case .success(let category): + selectNameCategory = category[index].nameCategory + return .success(()) + case .failure(let error): + return .failure(error) + } } - - func editTracker(vc: CreateTrackerViewController, editTracker: Tracker, nameCategory: String) { - schedule = editTracker.schedule - color = editTracker.color - nameNewCategory = nameCategory - nameTracker = editTracker.name - emoji = editTracker.emoji - id = editTracker.id +} + +//MARK: - TrackerCategoryStoreDelegate +extension CategoryViewModel: TrackerCategoryStoreDelegate { + func storeCategory(trackerCategoryStore: TrackerCategoryStoreProtocol) { + category = getCategory() } } +//MARK: - TrackerStoreDelegate +extension CategoryViewModel: TrackerStoreDelegate { + func updateTracker(_ trackerCoreData: TrackerStoreProtocol) { + category = getCategory() + } +} diff --git a/Tracker/ViewModel/EditTrackerViewModel.swift b/Tracker/ViewModel/EditTrackerViewModel.swift new file mode 100644 index 0000000..dd479ad --- /dev/null +++ b/Tracker/ViewModel/EditTrackerViewModel.swift @@ -0,0 +1,157 @@ +// +// CategoryViewModel.swift +// Tracker +// +// Created by Григорий Машук on 13.12.23. +// + +import UIKit + +protocol EditTrackerModelProtocol { + var isSchedul: Bool { get } + var listSettings: [ChoiceParametrs] { get } + var dataSection: [Header] { get } + var emojies: [String] { get } + var colors: [UIColor] { get } + + func reverseIsSchedul() + func checkingForEmptiness() -> Bool + func jonedSchedule(schedule: [WeekDay], stringArrayDay: String) -> String + func setListWeekDay(listWeekDay: [WeekDay]) + func getColorRow(color: UIColor) -> Int + func getEmojiRow(emoji: String) -> Int + + func setSchedule(_ vc: CreateTrackerViewController, schedule: [WeekDay]) + func setColor(_ vc: CreateTrackerViewController, color: UIColor) + func setNameNewCategory(_ vc: CreateTrackerViewController, nameCategory: String) + func setNameTracker(_ vc: CreateTrackerViewController, nameTracker: String) + func setEmojiTracker(_ vc: CreateTrackerViewController, emoji: String) + func editTracker(vc: CreateTrackerViewController, editTracker: Tracker, nameCategory: String) +} + +//MARK: - EditTrackerViewModel +final class EditTrackerViewModel { + var isSchedul: Bool = true + var listSettings: [ChoiceParametrs] { isSchedul ? [.category] : [.category, .schedule] } + let dataSection: [Header] = [.emoji, .color] + let emojies: [String] = ["🙂", "😻", "🌺", "🐶", "❤️", "😱", "😇", "😡", "🥶", + "🤔", "🙌", "🍔", "🥦", "🏓", "🥇", "🎸", "🏝️", "😪"] + + let colors: [UIColor] = [.colorSelection1, .colorSelection2, .colorSelection3,.colorSelection4, + .colorSelection5, .colorSelection6, .colorSelection7, .colorSelection8, + .colorSelection9, .colorSelection10, .colorSelection11, .colorSelection12, + .colorSelection13, .colorSelection14, .colorSelection15, .colorSelection16, + .colorSelection17, .colorSelection18] + + let regular: [WeekDay] = [.monday, .tuesday, .wednesday, .thursday, .friday, .saturday, .sunday] + + @Observable<[WeekDay]> private(set) var schedule: [WeekDay] = [] + @Observable private(set) var color: UIColor? + @Observable private(set) var nameNewCategory: String = "" + @Observable private(set) var nameTracker: String = "" + @Observable private(set) var emoji: String = "" + private(set) var id: UUID? + + private let marshalling: UIColorMarshalling + + convenience init() { + let marshalling = UIColorMarshalling() + self.init(marshalling: marshalling) + } + + init(marshalling: UIColorMarshalling) { + self.marshalling = marshalling + } +} + +//MARK: - EditTrackerViewModel +extension EditTrackerViewModel: EditTrackerModelProtocol { + func checkingForEmptiness() -> Bool { + var flag: Bool + if isSchedul { + flag = !nameTracker.isEmpty && + color != nil && + !emoji.isEmpty && + !nameNewCategory.isEmpty ? true : false + + return flag + } + flag = !schedule.isEmpty && + !nameTracker.isEmpty && + color != nil && + !emoji.isEmpty && + !nameNewCategory.isEmpty ? true : false + return flag + } + + func reverseIsSchedul() { + isSchedul = isSchedul ? !isSchedul : isSchedul + } + + func jonedSchedule(schedule: [WeekDay], stringArrayDay: String) -> String { + var stringListDay: String + if schedule.count == 7 { + stringListDay = stringArrayDay + return stringListDay + } + let listDay = schedule.map { $0.briefWordDay } + stringListDay = listDay.joined(separator: ",") + + return stringListDay + } + + func setListWeekDay(listWeekDay: [WeekDay]) { + schedule = listWeekDay + } + + func getColorRow(color: UIColor) -> Int { + var colorRow: Int = 0 + for (index, value) in colors.enumerated() { + let colorMarshalling = marshalling.color(from: marshalling.hexString(from: value)) + if colorMarshalling == color { + colorRow = index + } + } + return colorRow + } + + func getEmojiRow(emoji: String) -> Int { + var emojiRow: Int = 0 + for (index, value) in emojies.enumerated() { + if value == emoji { + emojiRow = index + } + } + return emojiRow + } + + func setSchedule(_ vc: CreateTrackerViewController, schedule: [WeekDay]) { + self.schedule = schedule + } + + func setColor(_ vc: CreateTrackerViewController, color: UIColor) { + self.color = color + } + + func setNameNewCategory(_ vc: CreateTrackerViewController, nameCategory: String) { + self.nameNewCategory = nameCategory + } + + func setNameTracker(_ vc: CreateTrackerViewController, nameTracker: String) { + self.nameTracker = nameTracker + } + + func setEmojiTracker(_ vc: CreateTrackerViewController, emoji: String) { + self.emoji = emoji + } + + func editTracker(vc: CreateTrackerViewController, editTracker: Tracker, nameCategory: String) { + schedule = editTracker.schedule + color = editTracker.color + nameNewCategory = nameCategory + nameTracker = editTracker.name + emoji = editTracker.emoji + id = editTracker.id + } +} + diff --git a/Tracker/ViewModel/TrackerViewModel.swift b/Tracker/ViewModel/TrackerViewModel.swift index 992bc2c..c33a508 100644 --- a/Tracker/ViewModel/TrackerViewModel.swift +++ b/Tracker/ViewModel/TrackerViewModel.swift @@ -8,11 +8,10 @@ import Foundation protocol TrackerViewModelProtocol { - func isCategorySelected(at index: Int) -> Result - func getCategory() -> Result<[TrackerCategory], Error> + var category: Result<[TrackerCategory], Error> { get } + var visibleCategory: Result<[TrackerCategory], Error> { get } + func getCategory() -> Result<[TrackerCategory], Error> func deleteTracker(_ id: UUID) -> Result - func createNameCategory(at index: Int) -> Result - func selectСategory(at index: Int) -> Result func addCategory(nameCategory: String) -> Result func deleteCategory(nameCategory: String) -> Result @@ -35,6 +34,8 @@ protocol TrackerViewModelProtocol { //MARK: - ViewModel final class TrackerViewModel { + private let textFixed = NSLocalizedString("textFixed", comment: "") + @Observable> private(set) var category: Result<[TrackerCategory], Error> @Observable private(set) var indexPath: IndexPath @Observable> private(set) var visibleCategory: Result<[TrackerCategory], Error> @@ -137,10 +138,9 @@ private extension TrackerViewModel { func updateTrackerRecord(_ trackerRecord: TrackerRecord) -> Result { trackerRecordStore.updateTrackerRecord(trackerRecord) } - } -//MARK: - CategoriViewModelProtocol +//MARK: - TrackerViewModelProtocol extension TrackerViewModel: TrackerViewModelProtocol { func deleteCategory(nameCategory: String) -> Result { trackerCategoryStore.deleteCategory(nameCategory: nameCategory) @@ -154,14 +154,14 @@ extension TrackerViewModel: TrackerViewModelProtocol { trackerCategoryStore.addCategory(nameCategory) } - func isCategorySelected(at index: Int) -> Result { - switch category { - case .success(let category): - return .success(category[index].nameCategory != selectNameCategory) - case .failure(let error): - return .failure(error) - } - } +// func isCategorySelected(at index: Int) -> Result { +// switch category { +// case .success(let category): +// return .success(category[index].nameCategory != selectNameCategory) +// case .failure(let error): +// return .failure(error) +// } +// } func getCategory() -> Result<[TrackerCategory], Error> { let trackerCategoryCoreData = trackerCategoryStore.getListTrackerCategoryCoreData() @@ -174,26 +174,6 @@ extension TrackerViewModel: TrackerViewModelProtocol { } } - func createNameCategory(at index: Int) -> Result { - switch category { - case .success(let category): - let name = category[index].nameCategory - return .success(name) - case .failure(let error): - return .failure(error) - } - } - - func selectСategory(at index: Int) -> Result { - switch category { - case .success(let category): - selectNameCategory = category[index].nameCategory - return .success(()) - case .failure(let error): - return .failure(error) - } - } - func treckersRecordsResult() -> Result, Error> { trackerRecordStore.treckersRecordsResult() } From 284fd4ecbe5e446c45f66c17bcf91a162420e51c Mon Sep 17 00:00:00 2001 From: Grifon Date: Thu, 21 Dec 2023 10:18:37 +0300 Subject: [PATCH 05/24] added tracker filtering --- Tracker.xcodeproj/project.pbxproj | 151 ++- .../xcshareddata/xcschemes/Tracker.xcscheme | 13 + .../xcdebugger/Breakpoints_v2.xcbkptlist | 946 +++++++++++++++--- .../blueFilterButton.colorset/Contents.json | 38 + Tracker/Domain/Store/TrackerRecordStore.swift | 49 +- Tracker/Domain/Store/TrackerStore.swift | 15 + Tracker/Extension +/UIColor.swift | 1 + ...yCellModel.swift => CustomCellModel.swift} | 2 +- Tracker/Models/Enum/FiltersState.swift | 30 + .../Resources/en.lproj/Localizable.strings | 7 + .../Resources/ru.lproj/Localizable.strings | 7 + ...ewCell.swift => CustomTableViewCell.swift} | 9 +- .../CreateCategoriesViewController.swift | 114 ++- .../FiltersViewController.swift | 127 +++ .../TrackersViewController.swift | 127 ++- Tracker/ViewModel/FiltersViewModel.swift | 36 + Tracker/ViewModel/TrackerViewModel.swift | 133 ++- TrackerTests/TrackerTests.swift | 35 + 18 files changed, 1592 insertions(+), 248 deletions(-) create mode 100644 Tracker/Assets.xcassets/Colors/blueFilterButton.colorset/Contents.json rename Tracker/Models/{CreateCategoryCellModel.swift => CustomCellModel.swift} (83%) create mode 100644 Tracker/Models/Enum/FiltersState.swift rename Tracker/TabBarController/TrackersViews/Cell/{CreateCategoriesTableViewCell.swift => CustomTableViewCell.swift} (93%) create mode 100644 Tracker/TabBarController/TrackersViews/ViewControllers/FiltersViewController.swift create mode 100644 Tracker/ViewModel/FiltersViewModel.swift create mode 100644 TrackerTests/TrackerTests.swift diff --git a/Tracker.xcodeproj/project.pbxproj b/Tracker.xcodeproj/project.pbxproj index 282787b..e3db88a 100644 --- a/Tracker.xcodeproj/project.pbxproj +++ b/Tracker.xcodeproj/project.pbxproj @@ -10,7 +10,7 @@ 1A0077F82B2DD6DF000EAE69 /* CategoryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A0077F72B2DD6DF000EAE69 /* CategoryViewModel.swift */; }; 1A14106A2ADD5185001D6DE6 /* ColorCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A1410692ADD5185001D6DE6 /* ColorCollectionViewCell.swift */; }; 1A19A7462B1672A200DD415B /* UpdateTrackerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A19A7452B1672A200DD415B /* UpdateTrackerModel.swift */; }; - 1A1E94B52AD952EF00FE9A0C /* CreateCategoriesTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A1E94B42AD952EF00FE9A0C /* CreateCategoriesTableViewCell.swift */; }; + 1A1E94B52AD952EF00FE9A0C /* CustomTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A1E94B42AD952EF00FE9A0C /* CustomTableViewCell.swift */; }; 1A21DD952ADE5A6A00583A01 /* DaysValueTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A21DD942ADE5A6A00583A01 /* DaysValueTransformer.swift */; }; 1A272CE42B27922A00D39A44 /* PinnedCategoryStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A272CE32B27922A00D39A44 /* PinnedCategoryStore.swift */; }; 1A27E6B92AD9871400ED3DF3 /* StringProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A27E6B82AD9871400ED3DF3 /* StringProtocol.swift */; }; @@ -30,6 +30,8 @@ 1A598BB62AC6F352009B166D /* TabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A598BB52AC6F352009B166D /* TabBarController.swift */; }; 1A598BB82AC6FAC5009B166D /* TrackersViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A598BB72AC6FAC5009B166D /* TrackersViewController.swift */; }; 1A66BB192B25D16500838F5F /* PinnTrackerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A66BB182B25D16500838F5F /* PinnTrackerModel.swift */; }; + 1A6724112B2EBE3700719A9D /* FiltersViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A6724102B2EBE3700719A9D /* FiltersViewController.swift */; }; + 1A6724132B2F108000719A9D /* FiltersState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A6724122B2F108000719A9D /* FiltersState.swift */; }; 1A704D3F2B2B76C60035C94D /* EmojiCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A704D3E2B2B76C60035C94D /* EmojiCollectionViewCell.swift */; }; 1A760A9D2B29EDC900B0C1CC /* EditTrackerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A760A9C2B29EDC900B0C1CC /* EditTrackerViewModel.swift */; }; 1A83156E2B2C786000097DE7 /* HandlerResultType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A83156D2B2C786000097DE7 /* HandlerResultType.swift */; }; @@ -55,11 +57,13 @@ 1AAD15652AF14E220080EB72 /* UserDefaultsBacked.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AAD15642AF14E220080EB72 /* UserDefaultsBacked.swift */; }; 1AB0E14F2B036CE8007183D8 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1AB0E1512B036CE8007183D8 /* InfoPlist.strings */; }; 1AB78AA32AC750A900904AB2 /* TrackersCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AB78AA22AC750A900904AB2 /* TrackersCollectionViewCell.swift */; }; - 1ABABBF72AFC10AD007281E0 /* CreateCategoryCellModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ABABBF62AFC10AD007281E0 /* CreateCategoryCellModel.swift */; }; + 1ABABBF72AFC10AD007281E0 /* CustomCellModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ABABBF62AFC10AD007281E0 /* CustomCellModel.swift */; }; 1ABB3D2C2AC9AAB40037B601 /* HeaderReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ABB3D2B2AC9AAB40037B601 /* HeaderReusableView.swift */; }; 1AD277712ACD4B1800F92219 /* UITextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AD277702ACD4B1800F92219 /* UITextField.swift */; }; 1ADD3D742ACBDBB1005F639F /* TrackerCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ADD3D732ACBDBB1005F639F /* TrackerCollectionView.swift */; }; + 1AE08AB82B2F5C250042CE59 /* FiltersViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AE08AB72B2F5C250042CE59 /* FiltersViewModel.swift */; }; 1AE27C2D2B0604A800C4A925 /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AE27C2C2B0604A800C4A925 /* Errors.swift */; }; + 1AE96E0E2B341E000061F10D /* TrackerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AE96E0D2B341E000061F10D /* TrackerTests.swift */; }; 1AE989292ACAE13800392315 /* TrackerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AE989282ACAE13800392315 /* TrackerModel.swift */; }; 1AE9892D2ACAE76E00392315 /* WeekDay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AE9892C2ACAE76E00392315 /* WeekDay.swift */; }; 1AE989302ACAF14B00392315 /* TrackerCategoryModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AE9892F2ACAF14B00392315 /* TrackerCategoryModel.swift */; }; @@ -67,11 +71,21 @@ 1AF05E062B2B011D0099D022 /* ShedulViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AF05E052B2B011D0099D022 /* ShedulViewModel.swift */; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + 1AE96E0F2B341E000061F10D /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 1A8A7FDA2AC55D960038CB42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 1A8A7FE12AC55D960038CB42; + remoteInfo = Tracker; + }; +/* End PBXContainerItemProxy section */ + /* Begin PBXFileReference section */ 1A0077F72B2DD6DF000EAE69 /* CategoryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CategoryViewModel.swift; sourceTree = ""; }; 1A1410692ADD5185001D6DE6 /* ColorCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorCollectionViewCell.swift; sourceTree = ""; }; 1A19A7452B1672A200DD415B /* UpdateTrackerModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateTrackerModel.swift; sourceTree = ""; }; - 1A1E94B42AD952EF00FE9A0C /* CreateCategoriesTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateCategoriesTableViewCell.swift; sourceTree = ""; }; + 1A1E94B42AD952EF00FE9A0C /* CustomTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTableViewCell.swift; sourceTree = ""; }; 1A21DD942ADE5A6A00583A01 /* DaysValueTransformer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DaysValueTransformer.swift; sourceTree = ""; }; 1A272CE32B27922A00D39A44 /* PinnedCategoryStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinnedCategoryStore.swift; sourceTree = ""; }; 1A27E6B82AD9871400ED3DF3 /* StringProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringProtocol.swift; sourceTree = ""; }; @@ -92,6 +106,8 @@ 1A598BB52AC6F352009B166D /* TabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarController.swift; sourceTree = ""; }; 1A598BB72AC6FAC5009B166D /* TrackersViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackersViewController.swift; sourceTree = ""; }; 1A66BB182B25D16500838F5F /* PinnTrackerModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinnTrackerModel.swift; sourceTree = ""; }; + 1A6724102B2EBE3700719A9D /* FiltersViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FiltersViewController.swift; sourceTree = ""; }; + 1A6724122B2F108000719A9D /* FiltersState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FiltersState.swift; sourceTree = ""; }; 1A704D3E2B2B76C60035C94D /* EmojiCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiCollectionViewCell.swift; sourceTree = ""; }; 1A760A9C2B29EDC900B0C1CC /* EditTrackerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditTrackerViewModel.swift; sourceTree = ""; }; 1A83156D2B2C786000097DE7 /* HandlerResultType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandlerResultType.swift; sourceTree = ""; }; @@ -122,11 +138,14 @@ 1AB0E1502B036CE8007183D8 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 1AB0E1522B036CEF007183D8 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/InfoPlist.strings; sourceTree = ""; }; 1AB78AA22AC750A900904AB2 /* TrackersCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackersCollectionViewCell.swift; sourceTree = ""; }; - 1ABABBF62AFC10AD007281E0 /* CreateCategoryCellModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateCategoryCellModel.swift; sourceTree = ""; }; + 1ABABBF62AFC10AD007281E0 /* CustomCellModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomCellModel.swift; sourceTree = ""; }; 1ABB3D2B2AC9AAB40037B601 /* HeaderReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderReusableView.swift; sourceTree = ""; }; 1AD277702ACD4B1800F92219 /* UITextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITextField.swift; sourceTree = ""; }; 1ADD3D732ACBDBB1005F639F /* TrackerCollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerCollectionView.swift; sourceTree = ""; }; + 1AE08AB72B2F5C250042CE59 /* FiltersViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FiltersViewModel.swift; sourceTree = ""; }; 1AE27C2C2B0604A800C4A925 /* Errors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = ""; }; + 1AE96E0B2B341E000061F10D /* TrackerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TrackerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 1AE96E0D2B341E000061F10D /* TrackerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerTests.swift; sourceTree = ""; }; 1AE989282ACAE13800392315 /* TrackerModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerModel.swift; sourceTree = ""; }; 1AE9892C2ACAE76E00392315 /* WeekDay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeekDay.swift; sourceTree = ""; }; 1AE9892F2ACAF14B00392315 /* TrackerCategoryModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerCategoryModel.swift; sourceTree = ""; }; @@ -142,6 +161,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 1AE96E082B341E000061F10D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -209,6 +235,7 @@ 1AA5B2362AD91D1800CB38A2 /* CreateCategoriesViewController.swift */, 1A2F9B642AC82FBF00914B60 /* EventSelectionViewController.swift */, 1AA5B2382AD9424C00CB38A2 /* NewCategoriViewController.swift */, + 1A6724102B2EBE3700719A9D /* FiltersViewController.swift */, ); path = ViewControllers; sourceTree = ""; @@ -254,7 +281,7 @@ 1AE9892F2ACAF14B00392315 /* TrackerCategoryModel.swift */, 1AE989312ACAF20500392315 /* TrackerRecordModel.swift */, 1A8ED5112AEFFADF00BA4EE4 /* OnboardingModel.swift */, - 1ABABBF62AFC10AD007281E0 /* CreateCategoryCellModel.swift */, + 1ABABBF62AFC10AD007281E0 /* CustomCellModel.swift */, 1A19A7452B1672A200DD415B /* UpdateTrackerModel.swift */, 1A66BB182B25D16500838F5F /* PinnTrackerModel.swift */, ); @@ -273,6 +300,7 @@ isa = PBXGroup; children = ( 1A8A7FE42AC55D970038CB42 /* Tracker */, + 1AE96E0C2B341E000061F10D /* TrackerTests */, 1A8A7FE32AC55D970038CB42 /* Products */, ); sourceTree = ""; @@ -282,6 +310,7 @@ isa = PBXGroup; children = ( 1A8A7FE22AC55D970038CB42 /* Tracker.app */, + 1AE96E0B2B341E000061F10D /* TrackerTests.xctest */, ); name = Products; sourceTree = ""; @@ -323,6 +352,7 @@ 1A760A9C2B29EDC900B0C1CC /* EditTrackerViewModel.swift */, 1AF05E052B2B011D0099D022 /* ShedulViewModel.swift */, 1A0077F72B2DD6DF000EAE69 /* CategoryViewModel.swift */, + 1AE08AB72B2F5C250042CE59 /* FiltersViewModel.swift */, ); path = ViewModel; sourceTree = ""; @@ -343,7 +373,7 @@ 1A8A576B2ACD5F4D00C060FA /* CreateTrackerTableViewCell.swift */, 1AB78AA22AC750A900904AB2 /* TrackersCollectionViewCell.swift */, 1A3ED4512ACDEA3E0006EBBB /* WeekDayTableViewCell.swift */, - 1A1E94B42AD952EF00FE9A0C /* CreateCategoriesTableViewCell.swift */, + 1A1E94B42AD952EF00FE9A0C /* CustomTableViewCell.swift */, 1A1410692ADD5185001D6DE6 /* ColorCollectionViewCell.swift */, 1A704D3E2B2B76C60035C94D /* EmojiCollectionViewCell.swift */, ); @@ -367,6 +397,14 @@ path = PropertyWrappers; sourceTree = ""; }; + 1AE96E0C2B341E000061F10D /* TrackerTests */ = { + isa = PBXGroup; + children = ( + 1AE96E0D2B341E000061F10D /* TrackerTests.swift */, + ); + path = TrackerTests; + sourceTree = ""; + }; 1AE9892E2ACAE8D900392315 /* Enum */ = { isa = PBXGroup; children = ( @@ -375,6 +413,7 @@ 1A3B83A92AD56323003C3897 /* NameEvent.swift */, 1A32FF902ADC72880065E44C /* Header.swift */, 1AE27C2C2B0604A800C4A925 /* Errors.swift */, + 1A6724122B2F108000719A9D /* FiltersState.swift */, ); path = Enum; sourceTree = ""; @@ -409,6 +448,24 @@ productReference = 1A8A7FE22AC55D970038CB42 /* Tracker.app */; productType = "com.apple.product-type.application"; }; + 1AE96E0A2B341E000061F10D /* TrackerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1AE96E132B341E000061F10D /* Build configuration list for PBXNativeTarget "TrackerTests" */; + buildPhases = ( + 1AE96E072B341E000061F10D /* Sources */, + 1AE96E082B341E000061F10D /* Frameworks */, + 1AE96E092B341E000061F10D /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 1AE96E102B341E000061F10D /* PBXTargetDependency */, + ); + name = TrackerTests; + productName = TrackerTests; + productReference = 1AE96E0B2B341E000061F10D /* TrackerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -422,6 +479,10 @@ 1A8A7FE12AC55D960038CB42 = { CreatedOnToolsVersion = 14.3.1; }; + 1AE96E0A2B341E000061F10D = { + CreatedOnToolsVersion = 14.3.1; + TestTargetID = 1A8A7FE12AC55D960038CB42; + }; }; }; buildConfigurationList = 1A8A7FDD2AC55D960038CB42 /* Build configuration list for PBXProject "Tracker" */; @@ -441,6 +502,7 @@ projectRoot = ""; targets = ( 1A8A7FE12AC55D960038CB42 /* Tracker */, + 1AE96E0A2B341E000061F10D /* TrackerTests */, ); }; /* End PBXProject section */ @@ -458,6 +520,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 1AE96E092B341E000061F10D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ @@ -489,7 +558,7 @@ 1A9AC5AF2AF373F000A9213D /* Observable.swift in Sources */, 1A959AF42AE24C44001C0EAE /* UIColorMarshalling.swift in Sources */, 1A0077F82B2DD6DF000EAE69 /* CategoryViewModel.swift in Sources */, - 1A1E94B52AD952EF00FE9A0C /* CreateCategoriesTableViewCell.swift in Sources */, + 1A1E94B52AD952EF00FE9A0C /* CustomTableViewCell.swift in Sources */, 1A8A576C2ACD5F4D00C060FA /* CreateTrackerTableViewCell.swift in Sources */, 1A598BB82AC6FAC5009B166D /* TrackersViewController.swift in Sources */, 1A8A7FEA2AC55D970038CB42 /* StatisticViewController.swift in Sources */, @@ -499,7 +568,8 @@ 1A45A67C2ADE8F0F0063F08C /* TrackerRecordStore.swift in Sources */, 1AE989322ACAF20500392315 /* TrackerRecordModel.swift in Sources */, 1A45A67A2ADE8EDB0063F08C /* TrackerCategoryStore.swift in Sources */, - 1ABABBF72AFC10AD007281E0 /* CreateCategoryCellModel.swift in Sources */, + 1A6724132B2F108000719A9D /* FiltersState.swift in Sources */, + 1ABABBF72AFC10AD007281E0 /* CustomCellModel.swift in Sources */, 1A32FF912ADC72880065E44C /* Header.swift in Sources */, 1A760A9D2B29EDC900B0C1CC /* EditTrackerViewModel.swift in Sources */, 1A19A7462B1672A200DD415B /* UpdateTrackerModel.swift in Sources */, @@ -515,10 +585,12 @@ 1A83156E2B2C786000097DE7 /* HandlerResultType.swift in Sources */, 1A14106A2ADD5185001D6DE6 /* ColorCollectionViewCell.swift in Sources */, 1A8A576F2ACD65C400C060FA /* ChoiceParametrs.swift in Sources */, + 1AE08AB82B2F5C250042CE59 /* FiltersViewModel.swift in Sources */, 1ADD3D742ACBDBB1005F639F /* TrackerCollectionView.swift in Sources */, 1A9E5C962AD2FF6100010C30 /* String.swift in Sources */, 1A45A6752ADE8BCC0063F08C /* Model.xcdatamodeld in Sources */, 1A9AC5B42AF37AED00A9213D /* TrackerViewModel.swift in Sources */, + 1A6724112B2EBE3700719A9D /* FiltersViewController.swift in Sources */, 1A8ED50F2AEFEB5A00BA4EE4 /* OnboardingViewController.swift in Sources */, 1A272CE42B27922A00D39A44 /* PinnedCategoryStore.swift in Sources */, 1AD277712ACD4B1800F92219 /* UITextField.swift in Sources */, @@ -542,8 +614,24 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 1AE96E072B341E000061F10D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1AE96E0E2B341E000061F10D /* TrackerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXTargetDependency section */ + 1AE96E102B341E000061F10D /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 1A8A7FE12AC55D960038CB42 /* Tracker */; + targetProxy = 1AE96E0F2B341E000061F10D /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + /* Begin PBXVariantGroup section */ 1A323DA32B02B0EF00624B54 /* LocalizableDict.stringsdict */ = { isa = PBXVariantGroup; @@ -756,6 +844,44 @@ }; name = Release; }; + 1AE96E112B341E000061F10D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 4HXMY46TMH; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.Grifon.TrackerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Tracker.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Tracker"; + }; + name = Debug; + }; + 1AE96E122B341E000061F10D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 4HXMY46TMH; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.Grifon.TrackerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Tracker.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Tracker"; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -777,6 +903,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 1AE96E132B341E000061F10D /* Build configuration list for PBXNativeTarget "TrackerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1AE96E112B341E000061F10D /* Debug */, + 1AE96E122B341E000061F10D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ /* Begin XCVersionGroup section */ diff --git a/Tracker.xcodeproj/xcshareddata/xcschemes/Tracker.xcscheme b/Tracker.xcodeproj/xcshareddata/xcschemes/Tracker.xcscheme index 9c67c1b..95d4f14 100644 --- a/Tracker.xcodeproj/xcshareddata/xcschemes/Tracker.xcscheme +++ b/Tracker.xcodeproj/xcshareddata/xcschemes/Tracker.xcscheme @@ -28,6 +28,19 @@ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES" shouldAutocreateTestPlan = "YES"> + + + + + + @@ -1064,8 +1064,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "506" - endingLineNumber = "506" + startingLineNumber = "571" + endingLineNumber = "571" landmarkName = "collectionView(_:viewForSupplementaryElementOfKind:at:)" landmarkType = "7"> @@ -1172,8 +1172,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "491" - endingLineNumber = "491" + startingLineNumber = "556" + endingLineNumber = "556" landmarkName = "collectionView(_:layout:insetForSectionAt:)" landmarkType = "7"> @@ -1188,8 +1188,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "484" - endingLineNumber = "484" + startingLineNumber = "549" + endingLineNumber = "549" landmarkName = "collectionView(_:layout:sizeForItemAt:)" landmarkType = "7"> @@ -1204,8 +1204,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "505" - endingLineNumber = "505" + startingLineNumber = "570" + endingLineNumber = "570" landmarkName = "collectionView(_:viewForSupplementaryElementOfKind:at:)" landmarkType = "7"> @@ -1312,8 +1312,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "232" - endingLineNumber = "232" + startingLineNumber = "284" + endingLineNumber = "284" landmarkName = "registerCellAndHeader(collectionView:)" landmarkType = "7"> @@ -1328,8 +1328,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "500" - endingLineNumber = "500" + startingLineNumber = "565" + endingLineNumber = "565" landmarkName = "collectionView(_:layout:referenceSizeForHeaderInSection:)" landmarkType = "7"> @@ -1455,8 +1455,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "566" - endingLineNumber = "566" + startingLineNumber = "631" + endingLineNumber = "631" landmarkName = "eventSelectionViewControllerDidCancel(_:)" landmarkType = "7"> @@ -1519,8 +1519,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "526" - endingLineNumber = "526" + startingLineNumber = "591" + endingLineNumber = "591" landmarkName = "updateSearchResults(for:)" landmarkType = "7"> @@ -1803,8 +1803,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "533" - endingLineNumber = "533" + startingLineNumber = "598" + endingLineNumber = "598" landmarkName = "updateSearchResults(for:)" landmarkType = "7"> @@ -1851,8 +1851,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "487" - endingLineNumber = "487" + startingLineNumber = "552" + endingLineNumber = "552" landmarkName = "collectionView(_:layout:sizeForItemAt:)" landmarkType = "7"> @@ -2876,8 +2876,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "534" - endingLineNumber = "534" + startingLineNumber = "599" + endingLineNumber = "599" landmarkName = "updateSearchResults(for:)" landmarkType = "7"> @@ -3052,8 +3052,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "259" - endingLineNumber = "259" + startingLineNumber = "263" + endingLineNumber = "263" landmarkName = "tableView(_:didSelectRowAt:)" landmarkType = "7"> @@ -3130,8 +3130,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "258" - endingLineNumber = "258" + startingLineNumber = "262" + endingLineNumber = "262" landmarkName = "tableView(_:didSelectRowAt:)" landmarkType = "7"> @@ -3146,24 +3146,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "236" - endingLineNumber = "236" - landmarkName = "tableView(_:cellForRowAt:)" - landmarkType = "7"> - - - - @@ -3178,8 +3162,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "239" - endingLineNumber = "239" + startingLineNumber = "236" + endingLineNumber = "236" landmarkName = "tableView(_:cellForRowAt:)" landmarkType = "7"> @@ -3735,8 +3719,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "277" - endingLineNumber = "277" + startingLineNumber = "281" + endingLineNumber = "281" landmarkName = "didNewCategoryName(_:nameCategory:)" landmarkType = "7"> @@ -3863,8 +3847,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "438" - endingLineNumber = "438" + startingLineNumber = "502" + endingLineNumber = "502" landmarkName = "collectionView(_:cellForItemAt:)" landmarkType = "7"> @@ -3879,8 +3863,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "463" - endingLineNumber = "463" + startingLineNumber = "528" + endingLineNumber = "528" landmarkName = "collectionView(_:contextMenuConfigurationForItemsAt:point:)" landmarkType = "7"> @@ -3895,8 +3879,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "452" - endingLineNumber = "452" + startingLineNumber = "517" + endingLineNumber = "517" landmarkName = "collectionView(_:contextMenuConfigurationForItemsAt:point:)" landmarkType = "7"> @@ -4037,8 +4021,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "169" - endingLineNumber = "169" + startingLineNumber = "171" + endingLineNumber = "171" landmarkName = "deleteTracker(_:)" landmarkType = "7"> @@ -4146,8 +4130,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "174" - endingLineNumber = "174" + startingLineNumber = "176" + endingLineNumber = "176" landmarkName = "deleteTracker(_:)" landmarkType = "7"> @@ -4382,8 +4366,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "150" - endingLineNumber = "150" + startingLineNumber = "147" + endingLineNumber = "147" landmarkName = "bind()" landmarkType = "7"> @@ -4460,8 +4444,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "153" - endingLineNumber = "153" + startingLineNumber = "150" + endingLineNumber = "150" landmarkName = "bind()" landmarkType = "7"> @@ -4476,8 +4460,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "562" - endingLineNumber = "562" + startingLineNumber = "627" + endingLineNumber = "627" landmarkName = "eventSelectionViewController(vc:nameCategories:tracker:)" landmarkType = "7"> @@ -4650,8 +4634,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "125" - endingLineNumber = "125" + startingLineNumber = "127" + endingLineNumber = "127" landmarkName = "addNewTracker(_:nameCategory:)" landmarkType = "7"> @@ -4682,8 +4666,8 @@ filePath = "Tracker/Domain/Store/TrackerRecordStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "113" - endingLineNumber = "113" + startingLineNumber = "125" + endingLineNumber = "125" landmarkName = "updateTrackerRecord(_:)" landmarkType = "7"> @@ -4746,8 +4730,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "164" - endingLineNumber = "164" + startingLineNumber = "187" + endingLineNumber = "187" landmarkName = "bind()" landmarkType = "7"> @@ -4826,6 +4810,21 @@ endingLineNumber = "164" offsetFromSymbolStart = "148"> + + @@ -4887,8 +4886,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "172" - endingLineNumber = "172" + startingLineNumber = "196" + endingLineNumber = "196" landmarkName = "bind()" landmarkType = "7"> @@ -4919,8 +4918,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "189" - endingLineNumber = "189" + startingLineNumber = "195" + endingLineNumber = "195" landmarkName = "addPinnedCategory(_:pinnedCategory:)" landmarkType = "7"> @@ -4997,8 +4996,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "155" - endingLineNumber = "155" + startingLineNumber = "152" + endingLineNumber = "152" landmarkName = "bind()" landmarkType = "7"> @@ -5013,8 +5012,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "229" - endingLineNumber = "229" + startingLineNumber = "226" + endingLineNumber = "226" landmarkName = "tableView(_:cellForRowAt:)" landmarkType = "7"> @@ -5091,8 +5090,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "149" - endingLineNumber = "149" + startingLineNumber = "146" + endingLineNumber = "146" landmarkName = "bind()" landmarkType = "7"> @@ -5107,8 +5106,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "125" - endingLineNumber = "125" + startingLineNumber = "122" + endingLineNumber = "122" landmarkName = "viewDidLoad()" landmarkType = "7"> @@ -5616,8 +5615,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "102" - endingLineNumber = "102" + startingLineNumber = "103" + endingLineNumber = "103" landmarkName = "searchTracker(id:)" landmarkType = "7"> @@ -5664,28 +5663,12 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "100" - endingLineNumber = "100" + startingLineNumber = "101" + endingLineNumber = "101" landmarkName = "searchTracker(id:)" landmarkType = "7"> - - - - @@ -5712,8 +5695,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "574" - endingLineNumber = "574" + startingLineNumber = "639" + endingLineNumber = "639" landmarkName = "trackerViewController(vc:nameCategory:tracker:)" landmarkType = "7"> @@ -5790,8 +5773,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "204" - endingLineNumber = "204" + startingLineNumber = "219" + endingLineNumber = "219" landmarkName = "controllerDidChangeContent(_:)" landmarkType = "7"> @@ -6096,8 +6079,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "152" - endingLineNumber = "152" + startingLineNumber = "154" + endingLineNumber = "154" landmarkName = "updateTracker(tracker:nameCategory:)" landmarkType = "7"> @@ -6174,8 +6157,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "140" - endingLineNumber = "140" + startingLineNumber = "142" + endingLineNumber = "142" landmarkName = "updateTracker(tracker:nameCategory:)" landmarkType = "7"> @@ -6285,8 +6268,8 @@ filePath = "Tracker/ViewModel/TrackerViewModel.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "191" - endingLineNumber = "191" + startingLineNumber = "256" + endingLineNumber = "256" landmarkName = "updateTracker(tracker:nameCategory:)" landmarkType = "7"> @@ -6301,8 +6284,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "136" - endingLineNumber = "136" + startingLineNumber = "138" + endingLineNumber = "138" landmarkName = "updateTracker(tracker:nameCategory:)" landmarkType = "7"> @@ -6339,5 +6322,732 @@ landmarkType = "7"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tracker/Assets.xcassets/Colors/blueFilterButton.colorset/Contents.json b/Tracker/Assets.xcassets/Colors/blueFilterButton.colorset/Contents.json new file mode 100644 index 0000000..6090dcf --- /dev/null +++ b/Tracker/Assets.xcassets/Colors/blueFilterButton.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "1.000", + "green" : "0.518", + "red" : "0.039" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "1.000", + "green" : "0.518", + "red" : "0.039" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tracker/Domain/Store/TrackerRecordStore.swift b/Tracker/Domain/Store/TrackerRecordStore.swift index 0ad46a4..6c9d956 100644 --- a/Tracker/Domain/Store/TrackerRecordStore.swift +++ b/Tracker/Domain/Store/TrackerRecordStore.swift @@ -48,7 +48,7 @@ final class TrackerRecordStore: NSObject { } private extension TrackerRecordStore { - func save(context: NSManagedObjectContext) -> Result { + func save() -> Result { do { try context.save() return .success(()) @@ -77,12 +77,32 @@ private extension TrackerRecordStore { func addNewTrackerRecord(_ trackerRecord: TrackerRecord) -> Result { let trackerRecordCoreData = TrackerRecordCoreData(context: context) updateExistingTrackerRecord(trackerRecordCoreData, trackerRecord: trackerRecord) - return save(context: context) + return save() } func deleteTrackerRecord(_ trackerRecordCoreData: TrackerRecordCoreData) -> Result { context.delete(trackerRecordCoreData) - return save(context: context) + return save() + } + + func searchTrackerRecordForDate(trackerRecord: TrackerRecord) throws -> TrackerRecordCoreData? { + let request = NSFetchRequest(entityName: "\(TrackerRecordCoreData.self)") + request.returnsObjectsAsFaults = false + request.predicate = NSPredicate(format: "%K == %@ AND %K == %@", + #keyPath(TrackerRecordCoreData.date), + trackerRecord.date as CVarArg, + #keyPath(TrackerRecordCoreData.trackerId), + trackerRecord.id as CVarArg) + return try context.fetch(request).first + } + + func getCountTrackersRecord(id: UUID) throws -> Int? { + let request = NSFetchRequest(entityName: "\(TrackerRecordCoreData.self)") + request.returnsObjectsAsFaults = false + request.predicate = NSPredicate(format: "%K == %@", + #keyPath(TrackerRecordCoreData.trackerId), + id as CVarArg) + return try context.fetch(request).count } } @@ -100,17 +120,9 @@ extension TrackerRecordStore: TrackerRecordStoreProtocol { } func updateTrackerRecord(_ trackerRecord: TrackerRecord) -> Result { - let request = NSFetchRequest(entityName: "\(TrackerRecordCoreData.self)") - request.returnsObjectsAsFaults = false - request.predicate = NSPredicate(format: "%K == %@ AND %K == %@", - #keyPath(TrackerRecordCoreData.date), - trackerRecord.date as CVarArg, - #keyPath(TrackerRecordCoreData.trackerId), - trackerRecord.id as CVarArg) do { - let trackers = try context.fetch(request) - if let tracker = trackers.first { - return deleteTrackerRecord(tracker) + if let trackerRecord = try searchTrackerRecordForDate(trackerRecord: trackerRecord) { + return deleteTrackerRecord(trackerRecord) } else { return addNewTrackerRecord(trackerRecord) } @@ -121,17 +133,14 @@ extension TrackerRecordStore: TrackerRecordStoreProtocol { } func loadTrackerRecord(id: UUID) -> Result { - let request = NSFetchRequest(entityName: "\(TrackerRecordCoreData.self)") - request.returnsObjectsAsFaults = false - request.predicate = NSPredicate(format: "%K == %@", - #keyPath(TrackerRecordCoreData.trackerId), - id as CVarArg) do { - let countTrackers = try context.fetch(request).count - return .success(countTrackers) + if let countTrackers = try getCountTrackersRecord(id: id) { + return .success(countTrackers) + } } catch { return .failure(error) } + return .failure(StoreErrors.TrackrerRecordStoreError.loadTrackerRecord) } } diff --git a/Tracker/Domain/Store/TrackerStore.swift b/Tracker/Domain/Store/TrackerStore.swift index da4f1bd..136611f 100644 --- a/Tracker/Domain/Store/TrackerStore.swift +++ b/Tracker/Domain/Store/TrackerStore.swift @@ -10,6 +10,7 @@ import CoreData protocol TrackerStoreProtocol { func addNewTracker(_ tracker: Tracker, nameCategory: String) -> Result + func getTracker(indexPath: IndexPath) -> TrackerCoreData func getTrackers(_ objects: [TrackerCoreData]) -> Result<[Tracker], Error> func updateTracker(tracker: Tracker, nameCategory: String) -> Result func deleteTracker(_ id: UUID) -> Result @@ -120,6 +121,7 @@ extension TrackerStore: TrackerStoreProtocol { do { if let category = try searchCategory(name: nameCategory) { let trackerCoreData = TrackerCoreData(context: context) +// trackerCoreData.addToRecord(<#T##value: TrackerRecordCoreData##TrackerRecordCoreData#>) updateExistingTrackerRecord(trackerCoreData: trackerCoreData, tracker: tracker) category.addToTrakers(trackerCoreData) return save() @@ -183,6 +185,10 @@ extension TrackerStore: TrackerStoreProtocol { } } + func getTracker(indexPath: IndexPath) -> TrackerCoreData { + fetchedTrackerResultController.object(at: indexPath) + } + func addPinnedCategory(_ id: UUID, pinnedCategory: PinnCategoryCoreData) -> Result { do { if let trackerCD = try searchTracker(id: id) { @@ -196,6 +202,15 @@ extension TrackerStore: TrackerStoreProtocol { } return save() } + +// func getCompletedTrackers(id: UUID, date: Date) -> TrackerCoreData? { +// let request = NSFetchRequest(entityName: "\(TrackerCoreData.self)") +// request.returnsObjectsAsFaults = false +// guard let keyPath = (\TrackerRecordCoreData.trackerId)._kvcKeyPathString +// else { return nil } +// request.predicate = NSPredicate(format: "%K == %@ AND %K == %@", keyPath, id as CVarArg, #keyPath(TrackerRecordCoreData.date), date as CVarArg) +// return try? context.fetch(request).first +// } } extension TrackerStore: NSFetchedResultsControllerDelegate { diff --git a/Tracker/Extension +/UIColor.swift b/Tracker/Extension +/UIColor.swift index 5c6e43d..1dd9a63 100644 --- a/Tracker/Extension +/UIColor.swift +++ b/Tracker/Extension +/UIColor.swift @@ -11,6 +11,7 @@ extension UIColor { static var blackDay: UIColor {UIColor(named: "blackDay") ?? .black} static var grayDay: UIColor {UIColor(named: "grayDay") ?? .gray} static var blueDay: UIColor {UIColor(named: "blueDay") ?? .blue} + static var blueFilterButton: UIColor {UIColor(named: "blueFilterButton") ?? .blue} static var whiteDay: UIColor {UIColor(named: "whiteDay") ?? .white} static var redDay: UIColor {UIColor(named: "redDay") ?? .red} static var backgroundDay: UIColor {UIColor(named: "backgroundDay") ?? .gray} diff --git a/Tracker/Models/CreateCategoryCellModel.swift b/Tracker/Models/CustomCellModel.swift similarity index 83% rename from Tracker/Models/CreateCategoryCellModel.swift rename to Tracker/Models/CustomCellModel.swift index e207620..7d1fe92 100644 --- a/Tracker/Models/CreateCategoryCellModel.swift +++ b/Tracker/Models/CustomCellModel.swift @@ -7,7 +7,7 @@ import UIKit -struct CreateCategoryCellModel { +struct CustomCellModel { let text: String let color: UIColor } diff --git a/Tracker/Models/Enum/FiltersState.swift b/Tracker/Models/Enum/FiltersState.swift new file mode 100644 index 0000000..5ff6d5e --- /dev/null +++ b/Tracker/Models/Enum/FiltersState.swift @@ -0,0 +1,30 @@ +// +// FiltersTracker.swift +// Tracker +// +// Created by Григорий Машук on 17.12.23. +// + +import Foundation + +enum FiltersState: String { + case allTrackers + case toDayTrackers + case completed + case notCompleted + + var name: String { + var name = "" + switch self { + case .allTrackers: + name = NSLocalizedString(self.rawValue, comment: "") + case .toDayTrackers: + name = NSLocalizedString(self.rawValue, comment: "") + case .completed: + name = NSLocalizedString(self.rawValue, comment: "") + case .notCompleted: + name = NSLocalizedString(self.rawValue, comment: "") + } + return name + } +} diff --git a/Tracker/Resources/en.lproj/Localizable.strings b/Tracker/Resources/en.lproj/Localizable.strings index b431e12..a949f73 100644 --- a/Tracker/Resources/en.lproj/Localizable.strings +++ b/Tracker/Resources/en.lproj/Localizable.strings @@ -15,6 +15,12 @@ //MARK: - TabBarController "trackers" = "Trackers"; "statistics" = "Statistics"; + +//MARK: - Trackers +"allTrackers" = "All Trackers"; +"toDayTrackers" = "Trackers for today"; +"completed" = "Completed"; +"notCompleted" = "Not Completed"; //MARK: - TrackersViewController "headerText" = "Trackers"; @@ -28,6 +34,7 @@ "textDelete" = "Delete"; "textFixed" = "Fixed"; "sureDeleteTracker" = "are you sure you want to delete the tracker"; +"filters" = "filters"; //MARK: - CreateTrackerViewController "placeholderTrackerTextField" = "Enter the name of the tracker"; diff --git a/Tracker/Resources/ru.lproj/Localizable.strings b/Tracker/Resources/ru.lproj/Localizable.strings index 2e99009..3a6bac1 100644 --- a/Tracker/Resources/ru.lproj/Localizable.strings +++ b/Tracker/Resources/ru.lproj/Localizable.strings @@ -15,6 +15,12 @@ "trackers" = "Трекеры"; "statistics" = "Статистика"; +//MARK: - Trackers +"allTrackers" = "Все трекеры"; +"toDayTrackers" = "Трекеры на сегодня"; +"completed" = "Завершенные"; +"notCompleted" = "Не завершенные"; + //MARK: - TrackersViewController "headerText" = "Трекеры"; "labelStabText" = "Что будем отслеживать?"; @@ -27,6 +33,7 @@ "textDelete" = "Удалить"; "textFixed" = "Закрепленные"; "sureDeleteTracker" = "Уверены что хотите удалить трекер?"; +"filters" = "Фильтры"; //MARK: - CreateTrackerViewController "placeholderTrackerTextField" = "Введите название трекера"; diff --git a/Tracker/TabBarController/TrackersViews/Cell/CreateCategoriesTableViewCell.swift b/Tracker/TabBarController/TrackersViews/Cell/CustomTableViewCell.swift similarity index 93% rename from Tracker/TabBarController/TrackersViews/Cell/CreateCategoriesTableViewCell.swift rename to Tracker/TabBarController/TrackersViews/Cell/CustomTableViewCell.swift index 2d21b40..305894f 100644 --- a/Tracker/TabBarController/TrackersViews/Cell/CreateCategoriesTableViewCell.swift +++ b/Tracker/TabBarController/TrackersViews/Cell/CustomTableViewCell.swift @@ -8,7 +8,7 @@ import UIKit //MARK: - CreateCategoriesTableViewCell -final class CreateCategoriesTableViewCell: UITableViewCell { +final class CustomTableViewCell: UITableViewCell { private struct ConstantsGreateCell { static let iconButton = "IconButtonCell" static let nameImageSelected = "selected" @@ -51,9 +51,9 @@ final class CreateCategoriesTableViewCell: UITableViewCell { } } -extension CreateCategoriesTableViewCell { +extension CustomTableViewCell { //MARK: - Config - func config(model: CreateCategoryCellModel) { + func config(model: CustomCellModel) { nameCategoriLableView.text = model.text backgroundColor = model.color } @@ -62,8 +62,9 @@ extension CreateCategoriesTableViewCell { selectedImage.isHidden = flag } - func setupCornerRadius(cornerRadius: CGFloat, maskedCorners: CACornerMask) { + func setupCornerRadius(cornerRadius: CGFloat, maskedCorners: CACornerMask?) { layer.cornerRadius = cornerRadius + guard let maskedCorners else { return } layer.maskedCorners = maskedCorners } diff --git a/Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift b/Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift index 15d0e75..6d86b29 100644 --- a/Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift +++ b/Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift @@ -18,20 +18,18 @@ final class CreateCategoriesViewController: UIViewController { static let imageViewImageStab = "Star" static let alertActionErrorTitle = "Ok" - static let categoriLabelText = NSLocalizedString("categoriLabelText", comment: "") - static let categoriAddButtonText = NSLocalizedString("categoriAddButtonText", comment: "") + static let categoryLabelText = NSLocalizedString("categoriLabelText", comment: "") + static let categoryAddButtonText = NSLocalizedString("categoriAddButtonText", comment: "") static let labelStabText = NSLocalizedString("labelStabText", comment: "") static let textFixed = NSLocalizedString("textFixed", comment: "") static let lableTextStabNumberOfLines = 2 static let selectionCategoryTableViewRowHeight = CGFloat(75) static let cornerRadius = CGFloat(16) - static let categoriLabelFont = UIFont.systemFont(ofSize: 16, weight: .medium) + static let categoryLabelFont = UIFont.systemFont(ofSize: 16, weight: .medium) static let labelStabTextFont = UIFont.systemFont(ofSize: 12, weight: .medium) static let lableFont = UIFont.systemFont(ofSize: 17, weight: .regular) static let insertSeparatorTableView = UIEdgeInsets(top: .zero, left: 12, bottom: .zero, right: 12) - - static let backgroundColorCell: UIColor = .backgroundNight } weak var delegate: CreateCategoriesViewControllerDelegate? @@ -39,15 +37,15 @@ final class CreateCategoriesViewController: UIViewController { private let handler = HandlerResultType() - private lazy var categoriLabel: UILabel = { - let categoriLabel = UILabel() - categoriLabel.text = ConstantsCreateCatVc.categoriLabelText - categoriLabel.textColor = .blackDay - categoriLabel.font = ConstantsCreateCatVc.categoriLabelFont - categoriLabel.textAlignment = .center - categoriLabel.backgroundColor = .clear + private lazy var categoryLabel: UILabel = { + let categoryLabel = UILabel() + categoryLabel.text = ConstantsCreateCatVc.categoryLabelText + categoryLabel.textColor = .blackDay + categoryLabel.font = ConstantsCreateCatVc.categoryLabelFont + categoryLabel.textAlignment = .center + categoryLabel.backgroundColor = .clear - return categoriLabel + return categoryLabel }() private lazy var imageViewStab: UIImageView = { @@ -78,30 +76,29 @@ final class CreateCategoriesViewController: UIViewController { }() private lazy var selectionCategoryTableView: UITableView = { - let selectionCategoriTableView = UITableView() - selectionCategoriTableView.dataSource = self - selectionCategoriTableView.delegate = self - selectionCategoriTableView.backgroundColor = .clear - selectionCategoriTableView.separatorStyle = .singleLine - selectionCategoriTableView.register(CreateCategoriesTableViewCell.self, forCellReuseIdentifier: "\(CreateCategoriesTableViewCell.self)") - selectionCategoriTableView.separatorInset = ConstantsCreateCatVc.insertSeparatorTableView + let selectionCategoryTableView = UITableView() + selectionCategoryTableView.dataSource = self + selectionCategoryTableView.delegate = self + selectionCategoryTableView.backgroundColor = .clear + selectionCategoryTableView.separatorStyle = .singleLine + selectionCategoryTableView.register(CustomTableViewCell.self, forCellReuseIdentifier: "\(CustomTableViewCell.self)") + selectionCategoryTableView.separatorInset = ConstantsCreateCatVc.insertSeparatorTableView - return selectionCategoriTableView + return selectionCategoryTableView }() - private lazy var createCategoriButton: UIButton = { - let createCategoriButton = UIButton() - createCategoriButton.setTitle(ConstantsCreateCatVc.categoriAddButtonText, for: .normal) - createCategoriButton.titleLabel?.textColor = .whiteDay - createCategoriButton.backgroundColor = .blackDay - createCategoriButton.titleLabel?.font = ConstantsCreateCatVc.lableFont - createCategoriButton.layer.cornerRadius = ConstantsCreateCatVc.cornerRadius - createCategoriButton.layer.masksToBounds = true - createCategoriButton.translatesAutoresizingMaskIntoConstraints = false - - createCategoriButton.addTarget(self, action: #selector(didTapСategoryButton), for: .touchUpInside) + private lazy var createCategoryButton: UIButton = { + let createCategoryButton = UIButton() + createCategoryButton.setTitle(ConstantsCreateCatVc.categoryAddButtonText, for: .normal) + createCategoryButton.titleLabel?.textColor = .whiteDay + createCategoryButton.backgroundColor = .blackDay + createCategoryButton.titleLabel?.font = ConstantsCreateCatVc.lableFont + createCategoryButton.layer.cornerRadius = ConstantsCreateCatVc.cornerRadius + createCategoryButton.layer.masksToBounds = true + createCategoryButton.translatesAutoresizingMaskIntoConstraints = false + createCategoryButton.addTarget(self, action: #selector(didTapСategoryButton), for: .touchUpInside) - return createCategoriButton + return createCategoryButton }() init(delegate: CreateCategoriesViewControllerDelegate, viewModel: CategoryViewModelProtocol) { @@ -158,30 +155,30 @@ extension CreateCategoriesViewController { //MARK: - SetupUI private func setupUIElement() { - setupCategoriButton() - setupCategoriLabel() + setupCategoryButton() + setupCategoryLabel() setupTableView() setupStabView() } - private func setupCategoriButton() { - view.addSubview(createCategoriButton) + private func setupCategoryButton() { + view.addSubview(createCategoryButton) NSLayoutConstraint.activate([ - createCategoriButton.heightAnchor.constraint(equalToConstant: 60), - createCategoriButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -10), - createCategoriButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20), - createCategoriButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20) + createCategoryButton.heightAnchor.constraint(equalToConstant: 60), + createCategoryButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -10), + createCategoryButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20), + createCategoryButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20) ]) } - private func setupCategoriLabel() { - view.addSubview(categoriLabel) - categoriLabel.translatesAutoresizingMaskIntoConstraints = false + private func setupCategoryLabel() { + view.addSubview(categoryLabel) + categoryLabel.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ - categoriLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 24), - categoriLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor) + categoryLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 24), + categoryLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor) ]) } @@ -190,10 +187,10 @@ extension CreateCategoriesViewController { selectionCategoryTableView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ - selectionCategoryTableView.topAnchor.constraint(equalTo: categoriLabel.bottomAnchor, constant: 24), + selectionCategoryTableView.topAnchor.constraint(equalTo: categoryLabel.bottomAnchor, constant: 24), selectionCategoryTableView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16), selectionCategoryTableView.trailingAnchor.constraint(equalTo: view.trailingAnchor,constant: -16), - selectionCategoryTableView.bottomAnchor.constraint(equalTo: createCategoriButton.topAnchor) + selectionCategoryTableView.bottomAnchor.constraint(equalTo: createCategoryButton.topAnchor) ]) } @@ -226,29 +223,36 @@ extension CreateCategoriesViewController: UITableViewDataSource { } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - guard let cell = tableView.dequeueReusableCell(withIdentifier: "\(CreateCategoriesTableViewCell.self)") as? CreateCategoriesTableViewCell, + guard let cell = tableView.dequeueReusableCell(withIdentifier: "\(CustomTableViewCell.self)") as? CustomTableViewCell, let isSelected = handler.resultTypeHandlerGetValue(viewModel.isCategorySelected(at: indexPath.row)), let text = handler.resultTypeHandlerGetValue(viewModel.createNameCategory(at: indexPath.row)), let category = handler.resultTypeHandlerGetValue(viewModel.сategoryExcludingFixed()) else { return UITableViewCell() } cell.showSelectedImage(flag: isSelected) - let model = CreateCategoryCellModel(text: text, color: ConstantsCreateCatVc.backgroundColorCell) + let model = CustomCellModel(text: text, color: .backgroundNight) cell.config(model: model) if category.count > 1 { cell.separatorInset = ConstantsCreateCatVc.insertSeparatorTableView } - if indexPath.row == category.count - 1 { + if indexPath.row == .zero && category.count == 1 { cell.setupCornerRadius(cornerRadius: ConstantsCreateCatVc.cornerRadius, - maskedCorners: [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]) + maskedCorners: nil) cell.separatorInset = UIEdgeInsets(top: .zero, left: view.bounds.width, bottom: .zero, right: .zero) } - if indexPath.row == .zero { + if indexPath.row == .zero && category.count > 1 { cell.setupCornerRadius(cornerRadius: ConstantsCreateCatVc.cornerRadius, - maskedCorners: [.layerMinXMinYCorner, .layerMaxXMinYCorner]) + maskedCorners: [.layerMaxXMinYCorner, .layerMinXMinYCorner]) } + + if indexPath.row == category.count - 1 && category.count > 1 { + cell.setupCornerRadius(cornerRadius: ConstantsCreateCatVc.cornerRadius, + maskedCorners: [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]) + cell.separatorInset = UIEdgeInsets(top: .zero, left: view.bounds.width, bottom: .zero, right: .zero) + } + return cell } } @@ -256,7 +260,7 @@ extension CreateCategoriesViewController: UITableViewDataSource { //MARK: - UITableViewDelegate extension CreateCategoriesViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - guard let cell = tableView.cellForRow(at: indexPath) as? CreateCategoriesTableViewCell, + guard let cell = tableView.cellForRow(at: indexPath) as? CustomTableViewCell, let delegate else { return } cell.showSelectedImage(flag: false) diff --git a/Tracker/TabBarController/TrackersViews/ViewControllers/FiltersViewController.swift b/Tracker/TabBarController/TrackersViews/ViewControllers/FiltersViewController.swift new file mode 100644 index 0000000..4271496 --- /dev/null +++ b/Tracker/TabBarController/TrackersViews/ViewControllers/FiltersViewController.swift @@ -0,0 +1,127 @@ +// +// Filters.swift +// Tracker +// +// Created by Григорий Машук on 17.12.23. +// + +import UIKit + +protocol FiltersViewControllerDelegate: AnyObject { + func filter(vc: FiltersViewController, filterState: FiltersState) +} + +final class FiltersViewController: UIViewController { + private struct ConstantsFilters { + static let filtersLableText = NSLocalizedString("filters", comment: "") + static let filterLabelFont = UIFont.systemFont(ofSize: 16, weight: .medium) + static let cornerRadiusCell = CGFloat(16) + static let heightCell = CGFloat(75) + static let insertSeparatorTableView = UIEdgeInsets(top: .zero, left: 12, bottom: .zero, right: 12) + } + + weak var delegate: FiltersViewControllerDelegate? + private let viewModel: FiltersViewModelProtocol + + private lazy var filtersLable: UILabel = { + let filtersLable = UILabel() + filtersLable.text = ConstantsFilters.filtersLableText + filtersLable.textColor = .blackDay + filtersLable.font = ConstantsFilters.filterLabelFont + filtersLable.textAlignment = .center + filtersLable.backgroundColor = .clear + + return filtersLable + }() + + private lazy var filterTableView: UITableView = { + let filterTableView = UITableView() + filterTableView.dataSource = self + filterTableView.delegate = self + filterTableView.backgroundColor = .clear + filterTableView.separatorStyle = .singleLine + filterTableView.register(CustomTableViewCell.self, forCellReuseIdentifier: "\(CustomTableViewCell.self)") + filterTableView.separatorInset = ConstantsFilters.insertSeparatorTableView + + return filterTableView + }() + + override func viewDidLoad() { + super.viewDidLoad() + view.backgroundColor = .white + setupCategoryLabel() + setupTableView() + } + + init(viewModel: FiltersViewModelProtocol, delegate: FiltersViewControllerDelegate) { + self.viewModel = viewModel + self.delegate = delegate + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +private extension FiltersViewController { + private func setupCategoryLabel() { + view.addSubview(filtersLable) + filtersLable.translatesAutoresizingMaskIntoConstraints = false + + NSLayoutConstraint.activate([ + filtersLable.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 24), + filtersLable.centerXAnchor.constraint(equalTo: view.centerXAnchor) + ]) + } + + private func setupTableView() { + view.addSubview(filterTableView) + filterTableView.translatesAutoresizingMaskIntoConstraints = false + + NSLayoutConstraint.activate([ + filterTableView.topAnchor.constraint(equalTo: filtersLable.bottomAnchor, constant: 24), + filterTableView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16), + filterTableView.trailingAnchor.constraint(equalTo: view.trailingAnchor,constant: -16), + filterTableView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor) + ]) + } +} + +extension FiltersViewController: UITableViewDataSource { + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + viewModel.filtersState.count + } + + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + ConstantsFilters.heightCell + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + guard let cell = filterTableView.dequeueReusableCell(withIdentifier: "\(CustomTableViewCell.self)", for: indexPath) as? CustomTableViewCell + else { return UITableViewCell() } + cell.showSelectedImage(flag: viewModel.comparison(filter: viewModel.filtersState[indexPath.row].rawValue)) + let model = CustomCellModel(text: viewModel.filtersState[indexPath.row].name, color: .backgroundNight) + cell.config(model: model) + if indexPath.row == viewModel.filtersState.count - 1 { + cell.setupCornerRadius(cornerRadius: ConstantsFilters.cornerRadiusCell, + maskedCorners: [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]) + cell.separatorInset = UIEdgeInsets(top: .zero, left: view.bounds.width, bottom: .zero, right: .zero) + } + if indexPath.row == .zero { + cell.setupCornerRadius(cornerRadius: ConstantsFilters.cornerRadiusCell, + maskedCorners: [.layerMaxXMinYCorner, .layerMinXMinYCorner]) + } + + return cell + } +} + +extension FiltersViewController: UITableViewDelegate { + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + guard let delegate else { return } + let selectFilter = viewModel.filtersState[indexPath.row] + viewModel.setSelectFilter(selectFilter: selectFilter.rawValue) + delegate.filter(vc: self, filterState: selectFilter) + } +} diff --git a/Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift b/Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift index f72004d..73b7aa3 100644 --- a/Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift +++ b/Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift @@ -30,13 +30,16 @@ final class TrackersViewController: UIViewController { static let textEdit = NSLocalizedString("textEdit", comment: "") static let textDelete = NSLocalizedString("textDelete", comment: "") static let textFixed = NSLocalizedString("textFixed", comment: "") - static let sureDeleteTracker = NSLocalizedString("sureDeleteTracker", comment: "") + static let sureDeleteTracker = NSLocalizedString("sureDeleteTracker", comment: "") + static let filters = NSLocalizedString("filters", comment: "") static let datePickerCornerRadius = CGFloat(8) + static let buttonFilterCornerRadius = CGFloat(16) static let heightCollectionView = CGFloat(148) static let fontLableTextStab = UIFont.systemFont(ofSize: 12, weight: .medium) static let fontLabelHeader = UIFont.boldSystemFont(ofSize: 34) + static let fontButtonFilter = UIFont.systemFont(ofSize: 17, weight: .medium) static let firstWeekday = 2 } @@ -128,6 +131,19 @@ final class TrackersViewController: UIViewController { return contentView }() + private lazy var buttonFilter: UIButton = { + let buttonFilter = UIButton() + buttonFilter.backgroundColor = .blueFilterButton + buttonFilter.translatesAutoresizingMaskIntoConstraints = false + buttonFilter.setTitle(ConstantsTrackerVc.filters, for: .normal) + buttonFilter.addTarget(self, action: #selector(actionButtonFilter), for: .touchUpInside) + buttonFilter.titleLabel?.font = ConstantsTrackerVc.fontButtonFilter + buttonFilter.layer.cornerRadius = ConstantsTrackerVc.buttonFilterCornerRadius + buttonFilter.layer.masksToBounds = true + + return buttonFilter + }() + init(viewModel: TrackerViewModelProtocol) { super.init(nibName: nil, bundle: nil) } @@ -144,10 +160,17 @@ final class TrackersViewController: UIViewController { viewModel = TrackerViewModel() bind() guard let viewModel = viewModel as? TrackerViewModel else { return } - viewModel.getShowListTrackersForDay(date: datePicker.date) - guard let visibleCategories = handler.resultTypeHandlerGetValue(viewModel.visibleCategory) - else { return } - self.showStabView(flag: !visibleCategories.isEmpty) +// guard let visibleCategories = handler.resultTypeHandlerGetValue(viewModel.visibleCategory) +// else { return } +// self.showStabView(flag: !visibleCategories.isEmpty) + setColorButtonFilter(state: viewModel.getFilterState(), button: buttonFilter) + if let state = viewModel.getSelectFilter() { + if let state = FiltersState(rawValue: state) { + filterForState(state: state) + } + } else { + viewModel.allTrackers(date: datePicker.date) + } } } @@ -156,7 +179,7 @@ private extension TrackersViewController { guard let viewModel = viewModel as? TrackerViewModel else { return } viewModel.$category.bind { [weak self] _ in guard let self else { return } - viewModel.getShowListTrackersForDay(date: self.datePicker.date) + filterForState(state: viewModel.getFilterState()) } viewModel.$indexPath.bind { [weak self] indexPath in @@ -168,18 +191,43 @@ private extension TrackersViewController { guard let self, let cat = handler.resultTypeHandlerGetValue(result) else { return } + self.buttonFilter.isHidden = cat.isEmpty self.showStabView(flag: !cat.isEmpty) self.trackerCollectionView.reloadData() } + + viewModel.$filterState.bind { [weak self] state in + guard let self else { return } + switch state { + case .allTrackers: + viewModel.allTrackers(date: datePicker.date) + setColorButtonFilter(state: viewModel.getFilterState(), button: buttonFilter) + case .toDayTrackers: + datePicker.date = Date() + viewModel.allTrackers(date: datePicker.date) + setColorButtonFilter(state: viewModel.getFilterState(), button: buttonFilter) + case .completed: + viewModel.getCompleted(date: datePicker.date) + setColorButtonFilter(state: viewModel.getFilterState(), button: buttonFilter) + case .notCompleted: + viewModel.getNotCompleted(date: datePicker.date) + setColorButtonFilter(state: viewModel.getFilterState(), button: buttonFilter) + } + } } //MARK: - обработка событий + @objc + func actionButtonFilter() { + let filtersVC = FiltersViewController(viewModel: FiltersViewModel(), delegate: self) + filtersVC.modalPresentationStyle = .formSheet + present(filtersVC, animated: true) + } + @objc func actionForTapDatePicker() { guard let viewModel else { return } - chengeStab(text: ConstantsTrackerVc.labelStabText, nameImage: ConstantsTrackerVc.imageStar) - viewModel.getShowListTrackersForDay(date: datePicker.date) - + filterForState(state: viewModel.getFilterState()) } @objc @@ -190,33 +238,37 @@ private extension TrackersViewController { present(greateVC, animated: true) } - //метод изменения заглушки func chengeStab(text: String, nameImage: String) { lableTextStab.text = text imageViewStab.image = UIImage(named: nameImage) } - //функция для сравнения двух дат, вернет true, если дата больше или равна текущей - func dateComparison(date: Date, currentDate: Date) -> Bool { - let result = Calendar.current.compare(date, to: currentDate, toGranularity: .day) - var flag: Bool - switch result { - case .orderedAscending: - flag = false - case .orderedSame: - flag = true - case .orderedDescending: - flag = true + func showStabView(flag: Bool) { + [imageViewStab, lableTextStab].forEach { $0.isHidden = flag } + } + + func filterForState(state: FiltersState) { + guard let viewModel else { return } + switch state { + case .allTrackers: + viewModel.allTrackers(date: datePicker.date) + setColorButtonFilter(state: viewModel.getFilterState(), button: buttonFilter) + case .toDayTrackers: + viewModel.allTrackers(date: datePicker.date) + setColorButtonFilter(state: viewModel.getFilterState(), button: buttonFilter) + case .completed: + viewModel.getCompleted(date: datePicker.date) + setColorButtonFilter(state: viewModel.getFilterState(), button: buttonFilter) + case .notCompleted: + viewModel.getNotCompleted(date: datePicker.date) + setColorButtonFilter(state: viewModel.getFilterState(), button: buttonFilter) } - return flag } - //метод показа/скрытия заглушки - func showStabView(flag: Bool) { - [imageViewStab, lableTextStab].forEach { $0.isHidden = flag } + func setColorButtonFilter(state: FiltersState, button: UIButton) { + button.setTitleColor(state == .allTrackers ? .colorSelection9 : .colorSelection1, for: .normal) } - //метод создания кастомной CollectionView func greateTrackerCollectionView() -> TrackerCollectionView { let params = GeometricParams(cellCount: 2, leftInset: .zero, rightInset: .zero, cellSpacing: 12) let layout = UICollectionViewFlowLayout() @@ -235,7 +287,6 @@ private extension TrackersViewController { } //MARK: - Menu Action - func pinnedAction(text: String, tracker: Tracker, category: TrackerCategory) -> UIAction { UIAction(title: text) { [weak self] _ in guard let self, @@ -317,6 +368,8 @@ private extension TrackersViewController { setupContentView() setupStabView() setupNavBarItem() + setupButtonFilter() + } func setupNavBarItem() { @@ -387,6 +440,17 @@ private extension TrackersViewController { lableTextStab.topAnchor.constraint(equalTo: imageViewStab.bottomAnchor, constant: 10) ]) } + + func setupButtonFilter() { + view.addSubview(buttonFilter) + + NSLayoutConstraint.activate([ + buttonFilter.centerXAnchor.constraint(equalTo: view.centerXAnchor), + buttonFilter.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -16), + buttonFilter.heightAnchor.constraint(equalToConstant: 50), + buttonFilter.widthAnchor.constraint(equalToConstant: 114) + ]) + } } //MARK: - UICollectionViewDataSource @@ -439,6 +503,7 @@ extension TrackersViewController: UICollectionViewDataSource { } } +//MARK: - UICollectionViewDelegate extension TrackersViewController: UICollectionViewDelegate { func collectionView(_ collectionView: UICollectionView, contextMenuConfiguration configuration: UIContextMenuConfiguration, @@ -578,3 +643,11 @@ extension TrackersViewController: UpdateTrackerViewControllerDelegate { vc.dismiss(animated: true) } } + +extension TrackersViewController: FiltersViewControllerDelegate { + func filter(vc: FiltersViewController, filterState: FiltersState) { + guard let viewModel else { return } + viewModel.setFilterState(state: filterState) + vc.dismiss(animated: true) + } +} diff --git a/Tracker/ViewModel/FiltersViewModel.swift b/Tracker/ViewModel/FiltersViewModel.swift new file mode 100644 index 0000000..512c4cc --- /dev/null +++ b/Tracker/ViewModel/FiltersViewModel.swift @@ -0,0 +1,36 @@ +// +// FiltersViewModel.swift +// Tracker +// +// Created by Григорий Машук on 17.12.23. +// + +import Foundation + +protocol FiltersViewModelProtocol { + var filtersState: [FiltersState] { get } + func getSelectFilter() -> String? + func setSelectFilter(selectFilter: String) + func comparison(filter: String) -> Bool +} + +final class FiltersViewModel { + private static let keySelectFilter = "select_filter" + let filtersState: [FiltersState] = [.allTrackers, .toDayTrackers, .completed, .notCompleted] + @UserDefaultsBacked(key: FiltersViewModel.keySelectFilter) private var selectFilter: String? +} + +extension FiltersViewModel: FiltersViewModelProtocol { + func getSelectFilter() -> String? { + selectFilter + } + + func setSelectFilter(selectFilter: String) { + self.selectFilter = selectFilter + } + + func comparison(filter: String) -> Bool { + guard let selectFilter else { return true } + return filter != selectFilter + } +} diff --git a/Tracker/ViewModel/TrackerViewModel.swift b/Tracker/ViewModel/TrackerViewModel.swift index c33a508..d90d1eb 100644 --- a/Tracker/ViewModel/TrackerViewModel.swift +++ b/Tracker/ViewModel/TrackerViewModel.swift @@ -17,7 +17,7 @@ protocol TrackerViewModelProtocol { func treckersRecordsResult() -> Result, Error> func loadTrackerRecord(id: UUID) -> Result - func updateCompletedTrackers(tracker: Tracker, date: Date) -> Result + func updateCompletedTrackers(tracker: Tracker, date: Date) -> Result func addNewTracker(_ tracker: Tracker, nameCategory: String) -> Result func updateTracker(tracker: Tracker, nameCategory: String) -> Result @@ -30,18 +30,30 @@ protocol TrackerViewModelProtocol { func addPinnedCategory(id: UUID, nameCategory: String) -> Result func deleteAndGetPinnedCategory(id: UUID) -> Result + + func allTrackers(date: Date) + func getToDayTrackers(date: Date) + func getNotCompleted(date: Date) + func getCompleted(date: Date) + + func setFilterState(state: FiltersState) + func getFilterState() -> FiltersState } //MARK: - ViewModel final class TrackerViewModel { private let textFixed = NSLocalizedString("textFixed", comment: "") + private static let keySelectFilter = "select_filter" + private static let keySelectNameCategory = "select_name_category" @Observable> private(set) var category: Result<[TrackerCategory], Error> @Observable private(set) var indexPath: IndexPath @Observable> private(set) var visibleCategory: Result<[TrackerCategory], Error> - @UserDefaultsBacked(key: "select_name_category") private var selectNameCategory: String? + @Observableprivate(set) var filterState: FiltersState + @UserDefaultsBacked(key: TrackerViewModel.keySelectNameCategory) private var selectNameCategory: String? + @UserDefaultsBacked(key: TrackerViewModel.keySelectFilter) private var selectFilter: String? - private var completedTrackers: Result, Error> + private(set) var completedTrackers: Result, Error> private let trackerCategoryStore: TrackerCategoryStoreProtocol private let trackerRecordStore: TrackerRecordStoreProtocol @@ -60,12 +72,18 @@ final class TrackerViewModel { category: .success([]), completedTrackers: .success(Set()), visibleCategory: .success([]), - indexPath: IndexPath()) + indexPath: IndexPath(), + filterState: .allTrackers) trackerCategoryStore.delegate = self trackerRecordStore.delegate = self trackerStore.delegate = self category = getCategory() completedTrackers = treckersRecordsResult() + guard let state = selectFilter else { + filterState = .allTrackers + return + } + filterState = FiltersState(rawValue: state) ?? .allTrackers } init(trackerCategoryStore: TrackerCategoryStoreProtocol, @@ -75,7 +93,7 @@ final class TrackerViewModel { category: Result<[TrackerCategory], Error>, completedTrackers: Result, Error>, visibleCategory: Result<[TrackerCategory], Error>, - indexPath: IndexPath) + indexPath: IndexPath, filterState: FiltersState) { self.trackerCategoryStore = trackerCategoryStore self.trackerRecordStore = trackerRecordStore @@ -85,6 +103,7 @@ final class TrackerViewModel { self.completedTrackers = completedTrackers self.visibleCategory = visibleCategory self.indexPath = indexPath + self.filterState = filterState } } //MARK: - Extension @@ -135,8 +154,63 @@ private extension TrackerViewModel { return listCategories } + func filterListTrackersCompleted(trackerCategory: [TrackerCategory], date: Date) { + var listCategories: [TrackerCategory] = [] + for cat in .zero.. Result { - trackerRecordStore.updateTrackerRecord(trackerRecord) + return trackerRecordStore.updateTrackerRecord(trackerRecord) } } @@ -154,15 +228,6 @@ extension TrackerViewModel: TrackerViewModelProtocol { trackerCategoryStore.addCategory(nameCategory) } -// func isCategorySelected(at index: Int) -> Result { -// switch category { -// case .success(let category): -// return .success(category[index].nameCategory != selectNameCategory) -// case .failure(let error): -// return .failure(error) -// } -// } - func getCategory() -> Result<[TrackerCategory], Error> { let trackerCategoryCoreData = trackerCategoryStore.getListTrackerCategoryCoreData() guard let trackerCategoryCoreData else { return .failure(StoreErrors.TrackrerStoreError.getTrackerError) } @@ -262,6 +327,44 @@ extension TrackerViewModel: TrackerViewModelProtocol { func deleteAndGetPinnedCategory(id: UUID) -> Result { pinnedCategoryStore.deleteAndGetPinnedCategory(id) } + + func allTrackers(date: Date) { + getShowListTrackersForDay(date: date) + } + + func getToDayTrackers(date: Date) { + getShowListTrackersForDay(date: date) + } + + func getNotCompleted(date: Date) { + switch category { + case .success(let cat): + filterListTrackersNotCompleted(trackerCategory: cat, date: date) + case .failure(let error): + visibleCategory = .failure(error) + } + } + + func getCompleted(date: Date) { + switch category { + case .success(let cat): + filterListTrackersCompleted(trackerCategory: cat, date: date) + case .failure(let error): + visibleCategory = .failure(error) + } + } + + func setFilterState(state: FiltersState) { + filterState = state + } + + func getFilterState() -> FiltersState { + filterState + } + + func getSelectFilter() -> String? { + selectFilter + } } //MARK: - TrackerCategoryStoreDelegate diff --git a/TrackerTests/TrackerTests.swift b/TrackerTests/TrackerTests.swift new file mode 100644 index 0000000..1a4fbd4 --- /dev/null +++ b/TrackerTests/TrackerTests.swift @@ -0,0 +1,35 @@ +// +// TrackerTests.swift +// TrackerTests +// +// Created by Марина Машук on 21.12.23. +// + +import XCTest + +final class TrackerTests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + // Any test you write for XCTest can be annotated as throws and async. + // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error. + // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards. + } + + func testPerformanceExample() throws { + // This is an example of a performance test case. + measure { + // Put the code you want to measure the time of here. + } + } + +} From de236ca8ac99617be61ba35f57e8fb1d5cf63f24 Mon Sep 17 00:00:00 2001 From: Grifon Date: Thu, 21 Dec 2023 12:58:49 +0300 Subject: [PATCH 06/24] add Snapshots test main screen --- Tracker.xcodeproj/project.pbxproj | 91 +++++++---- .../xcshareddata/xcschemes/Tracker.xcscheme | 2 +- .../xcdebugger/Breakpoints_v2.xcbkptlist | 141 +++++++++++++++--- Tracker/Domain/Store/TrackerStore.swift | 1 - TrackerTests/TrackerTests.swift | 32 +--- 5 files changed, 190 insertions(+), 77 deletions(-) diff --git a/Tracker.xcodeproj/project.pbxproj b/Tracker.xcodeproj/project.pbxproj index e3db88a..d595738 100644 --- a/Tracker.xcodeproj/project.pbxproj +++ b/Tracker.xcodeproj/project.pbxproj @@ -63,7 +63,9 @@ 1ADD3D742ACBDBB1005F639F /* TrackerCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ADD3D732ACBDBB1005F639F /* TrackerCollectionView.swift */; }; 1AE08AB82B2F5C250042CE59 /* FiltersViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AE08AB72B2F5C250042CE59 /* FiltersViewModel.swift */; }; 1AE27C2D2B0604A800C4A925 /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AE27C2C2B0604A800C4A925 /* Errors.swift */; }; - 1AE96E0E2B341E000061F10D /* TrackerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AE96E0D2B341E000061F10D /* TrackerTests.swift */; }; + 1AE96E232B3424E40061F10D /* TrackerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AE96E222B3424E40061F10D /* TrackerTests.swift */; }; + 1AE96E2B2B34278E0061F10D /* InlineSnapshotTesting in Frameworks */ = {isa = PBXBuildFile; productRef = 1AE96E2A2B34278E0061F10D /* InlineSnapshotTesting */; }; + 1AE96E2D2B34278E0061F10D /* SnapshotTesting in Frameworks */ = {isa = PBXBuildFile; productRef = 1AE96E2C2B34278E0061F10D /* SnapshotTesting */; }; 1AE989292ACAE13800392315 /* TrackerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AE989282ACAE13800392315 /* TrackerModel.swift */; }; 1AE9892D2ACAE76E00392315 /* WeekDay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AE9892C2ACAE76E00392315 /* WeekDay.swift */; }; 1AE989302ACAF14B00392315 /* TrackerCategoryModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AE9892F2ACAF14B00392315 /* TrackerCategoryModel.swift */; }; @@ -72,7 +74,7 @@ /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - 1AE96E0F2B341E000061F10D /* PBXContainerItemProxy */ = { + 1AE96E242B3424E40061F10D /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 1A8A7FDA2AC55D960038CB42 /* Project object */; proxyType = 1; @@ -144,8 +146,8 @@ 1ADD3D732ACBDBB1005F639F /* TrackerCollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerCollectionView.swift; sourceTree = ""; }; 1AE08AB72B2F5C250042CE59 /* FiltersViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FiltersViewModel.swift; sourceTree = ""; }; 1AE27C2C2B0604A800C4A925 /* Errors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = ""; }; - 1AE96E0B2B341E000061F10D /* TrackerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TrackerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 1AE96E0D2B341E000061F10D /* TrackerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerTests.swift; sourceTree = ""; }; + 1AE96E202B3424E40061F10D /* TrackerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TrackerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 1AE96E222B3424E40061F10D /* TrackerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerTests.swift; sourceTree = ""; }; 1AE989282ACAE13800392315 /* TrackerModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerModel.swift; sourceTree = ""; }; 1AE9892C2ACAE76E00392315 /* WeekDay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeekDay.swift; sourceTree = ""; }; 1AE9892F2ACAF14B00392315 /* TrackerCategoryModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerCategoryModel.swift; sourceTree = ""; }; @@ -161,10 +163,12 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 1AE96E082B341E000061F10D /* Frameworks */ = { + 1AE96E1D2B3424E40061F10D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 1AE96E2B2B34278E0061F10D /* InlineSnapshotTesting in Frameworks */, + 1AE96E2D2B34278E0061F10D /* SnapshotTesting in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -300,7 +304,7 @@ isa = PBXGroup; children = ( 1A8A7FE42AC55D970038CB42 /* Tracker */, - 1AE96E0C2B341E000061F10D /* TrackerTests */, + 1AE96E212B3424E40061F10D /* TrackerTests */, 1A8A7FE32AC55D970038CB42 /* Products */, ); sourceTree = ""; @@ -310,7 +314,7 @@ isa = PBXGroup; children = ( 1A8A7FE22AC55D970038CB42 /* Tracker.app */, - 1AE96E0B2B341E000061F10D /* TrackerTests.xctest */, + 1AE96E202B3424E40061F10D /* TrackerTests.xctest */, ); name = Products; sourceTree = ""; @@ -397,10 +401,10 @@ path = PropertyWrappers; sourceTree = ""; }; - 1AE96E0C2B341E000061F10D /* TrackerTests */ = { + 1AE96E212B3424E40061F10D /* TrackerTests */ = { isa = PBXGroup; children = ( - 1AE96E0D2B341E000061F10D /* TrackerTests.swift */, + 1AE96E222B3424E40061F10D /* TrackerTests.swift */, ); path = TrackerTests; sourceTree = ""; @@ -444,26 +448,32 @@ dependencies = ( ); name = Tracker; + packageProductDependencies = ( + ); productName = Tracker; productReference = 1A8A7FE22AC55D970038CB42 /* Tracker.app */; productType = "com.apple.product-type.application"; }; - 1AE96E0A2B341E000061F10D /* TrackerTests */ = { + 1AE96E1F2B3424E40061F10D /* TrackerTests */ = { isa = PBXNativeTarget; - buildConfigurationList = 1AE96E132B341E000061F10D /* Build configuration list for PBXNativeTarget "TrackerTests" */; + buildConfigurationList = 1AE96E262B3424E40061F10D /* Build configuration list for PBXNativeTarget "TrackerTests" */; buildPhases = ( - 1AE96E072B341E000061F10D /* Sources */, - 1AE96E082B341E000061F10D /* Frameworks */, - 1AE96E092B341E000061F10D /* Resources */, + 1AE96E1C2B3424E40061F10D /* Sources */, + 1AE96E1D2B3424E40061F10D /* Frameworks */, + 1AE96E1E2B3424E40061F10D /* Resources */, ); buildRules = ( ); dependencies = ( - 1AE96E102B341E000061F10D /* PBXTargetDependency */, + 1AE96E252B3424E40061F10D /* PBXTargetDependency */, ); name = TrackerTests; + packageProductDependencies = ( + 1AE96E2A2B34278E0061F10D /* InlineSnapshotTesting */, + 1AE96E2C2B34278E0061F10D /* SnapshotTesting */, + ); productName = TrackerTests; - productReference = 1AE96E0B2B341E000061F10D /* TrackerTests.xctest */; + productReference = 1AE96E202B3424E40061F10D /* TrackerTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; /* End PBXNativeTarget section */ @@ -479,7 +489,7 @@ 1A8A7FE12AC55D960038CB42 = { CreatedOnToolsVersion = 14.3.1; }; - 1AE96E0A2B341E000061F10D = { + 1AE96E1F2B3424E40061F10D = { CreatedOnToolsVersion = 14.3.1; TestTargetID = 1A8A7FE12AC55D960038CB42; }; @@ -496,13 +506,14 @@ ); mainGroup = 1A8A7FD92AC55D960038CB42; packageReferences = ( + 1AE96E292B34278E0061F10D /* XCRemoteSwiftPackageReference "swift-snapshot-testing" */, ); productRefGroup = 1A8A7FE32AC55D970038CB42 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 1A8A7FE12AC55D960038CB42 /* Tracker */, - 1AE96E0A2B341E000061F10D /* TrackerTests */, + 1AE96E1F2B3424E40061F10D /* TrackerTests */, ); }; /* End PBXProject section */ @@ -520,7 +531,7 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 1AE96E092B341E000061F10D /* Resources */ = { + 1AE96E1E2B3424E40061F10D /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( @@ -614,21 +625,21 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 1AE96E072B341E000061F10D /* Sources */ = { + 1AE96E1C2B3424E40061F10D /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 1AE96E0E2B341E000061F10D /* TrackerTests.swift in Sources */, + 1AE96E232B3424E40061F10D /* TrackerTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ - 1AE96E102B341E000061F10D /* PBXTargetDependency */ = { + 1AE96E252B3424E40061F10D /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 1A8A7FE12AC55D960038CB42 /* Tracker */; - targetProxy = 1AE96E0F2B341E000061F10D /* PBXContainerItemProxy */; + targetProxy = 1AE96E242B3424E40061F10D /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ @@ -844,7 +855,7 @@ }; name = Release; }; - 1AE96E112B341E000061F10D /* Debug */ = { + 1AE96E272B3424E40061F10D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; @@ -863,7 +874,7 @@ }; name = Debug; }; - 1AE96E122B341E000061F10D /* Release */ = { + 1AE96E282B3424E40061F10D /* Release */ = { isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; @@ -903,17 +914,41 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 1AE96E132B341E000061F10D /* Build configuration list for PBXNativeTarget "TrackerTests" */ = { + 1AE96E262B3424E40061F10D /* Build configuration list for PBXNativeTarget "TrackerTests" */ = { isa = XCConfigurationList; buildConfigurations = ( - 1AE96E112B341E000061F10D /* Debug */, - 1AE96E122B341E000061F10D /* Release */, + 1AE96E272B3424E40061F10D /* Debug */, + 1AE96E282B3424E40061F10D /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ +/* Begin XCRemoteSwiftPackageReference section */ + 1AE96E292B34278E0061F10D /* XCRemoteSwiftPackageReference "swift-snapshot-testing" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/pointfreeco/swift-snapshot-testing"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.0.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 1AE96E2A2B34278E0061F10D /* InlineSnapshotTesting */ = { + isa = XCSwiftPackageProductDependency; + package = 1AE96E292B34278E0061F10D /* XCRemoteSwiftPackageReference "swift-snapshot-testing" */; + productName = InlineSnapshotTesting; + }; + 1AE96E2C2B34278E0061F10D /* SnapshotTesting */ = { + isa = XCSwiftPackageProductDependency; + package = 1AE96E292B34278E0061F10D /* XCRemoteSwiftPackageReference "swift-snapshot-testing" */; + productName = SnapshotTesting; + }; +/* End XCSwiftPackageProductDependency section */ + /* Begin XCVersionGroup section */ 1A45A6732ADE8BCC0063F08C /* Model.xcdatamodeld */ = { isa = XCVersionGroup; diff --git a/Tracker.xcodeproj/xcshareddata/xcschemes/Tracker.xcscheme b/Tracker.xcodeproj/xcshareddata/xcschemes/Tracker.xcscheme index 95d4f14..f095f57 100644 --- a/Tracker.xcodeproj/xcshareddata/xcschemes/Tracker.xcscheme +++ b/Tracker.xcodeproj/xcshareddata/xcschemes/Tracker.xcscheme @@ -34,7 +34,7 @@ parallelizable = "YES"> diff --git a/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index 22572e1..60614fa 100644 --- a/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -4021,8 +4021,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "171" - endingLineNumber = "171" + startingLineNumber = "170" + endingLineNumber = "170" landmarkName = "deleteTracker(_:)" landmarkType = "7"> @@ -4130,8 +4130,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "176" - endingLineNumber = "176" + startingLineNumber = "175" + endingLineNumber = "175" landmarkName = "deleteTracker(_:)" landmarkType = "7"> @@ -4634,8 +4634,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "127" - endingLineNumber = "127" + startingLineNumber = "126" + endingLineNumber = "126" landmarkName = "addNewTracker(_:nameCategory:)" landmarkType = "7"> @@ -4918,8 +4918,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "195" - endingLineNumber = "195" + startingLineNumber = "194" + endingLineNumber = "194" landmarkName = "addPinnedCategory(_:pinnedCategory:)" landmarkType = "7"> @@ -5773,8 +5773,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "219" - endingLineNumber = "219" + startingLineNumber = "218" + endingLineNumber = "218" landmarkName = "controllerDidChangeContent(_:)" landmarkType = "7"> @@ -6079,8 +6079,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "154" - endingLineNumber = "154" + startingLineNumber = "153" + endingLineNumber = "153" landmarkName = "updateTracker(tracker:nameCategory:)" landmarkType = "7"> @@ -6157,8 +6157,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "142" - endingLineNumber = "142" + startingLineNumber = "141" + endingLineNumber = "141" landmarkName = "updateTracker(tracker:nameCategory:)" landmarkType = "7"> @@ -6284,8 +6284,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "138" - endingLineNumber = "138" + startingLineNumber = "137" + endingLineNumber = "137" landmarkName = "updateTracker(tracker:nameCategory:)" landmarkType = "7"> @@ -6537,8 +6537,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "211" - endingLineNumber = "211" + startingLineNumber = "210" + endingLineNumber = "210" landmarkName = "TrackerStore" landmarkType = "21"> @@ -6630,8 +6630,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "209" - endingLineNumber = "209" + startingLineNumber = "208" + endingLineNumber = "208" landmarkName = "TrackerStore" landmarkType = "21"> @@ -6989,7 +6989,7 @@ BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint"> + + + + + + + + + + + + + + + + + + + + diff --git a/Tracker/Domain/Store/TrackerStore.swift b/Tracker/Domain/Store/TrackerStore.swift index 136611f..51946cf 100644 --- a/Tracker/Domain/Store/TrackerStore.swift +++ b/Tracker/Domain/Store/TrackerStore.swift @@ -121,7 +121,6 @@ extension TrackerStore: TrackerStoreProtocol { do { if let category = try searchCategory(name: nameCategory) { let trackerCoreData = TrackerCoreData(context: context) -// trackerCoreData.addToRecord(<#T##value: TrackerRecordCoreData##TrackerRecordCoreData#>) updateExistingTrackerRecord(trackerCoreData: trackerCoreData, tracker: tracker) category.addToTrakers(trackerCoreData) return save() diff --git a/TrackerTests/TrackerTests.swift b/TrackerTests/TrackerTests.swift index 1a4fbd4..8d7f59e 100644 --- a/TrackerTests/TrackerTests.swift +++ b/TrackerTests/TrackerTests.swift @@ -2,34 +2,18 @@ // TrackerTests.swift // TrackerTests // -// Created by Марина Машук on 21.12.23. +// Created by Григорий Машук on 21.12.23. // import XCTest +import SnapshotTesting +@testable import Tracker final class TrackerTests: XCTestCase { - - override func setUpWithError() throws { - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - - func testExample() throws { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. - // Any test you write for XCTest can be annotated as throws and async. - // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error. - // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards. - } - - func testPerformanceExample() throws { - // This is an example of a performance test case. - measure { - // Put the code you want to measure the time of here. - } + + func testViewController() { + let vc = TabBarController() + + assertSnapshot(matching: vc, as: .image) } - } From 9d6e52d92f721ede32864d5c3cd7c695069d3740 Mon Sep 17 00:00:00 2001 From: Grifon Date: Sat, 23 Dec 2023 16:19:46 +0300 Subject: [PATCH 07/24] implemented dark theme for the main screen --- Tracker.xcodeproj/project.pbxproj | 4 + .../xcshareddata/swiftpm/Package.resolved | 23 ++ .../xcdebugger/Breakpoints_v2.xcbkptlist | 353 +++++++----------- .../Domain/Store/TrackerCategoryStore.swift | 2 +- Tracker/Domain/Store/TrackerStore.swift | 15 +- Tracker/Helpers/Colors.swift | 28 ++ .../TabBarController/TabBarController.swift | 16 +- .../Cell/TrackersCollectionViewCell.swift | 4 +- .../CreateTrackerViewController.swift | 1 + .../TrackersViewController.swift | 28 +- TrackerTests/TrackerTests.swift | 1 - .../TrackerTests/testViewController.1.png | Bin 0 -> 197401 bytes 12 files changed, 243 insertions(+), 232 deletions(-) create mode 100644 Tracker.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved create mode 100644 Tracker/Helpers/Colors.swift create mode 100644 TrackerTests/__Snapshots__/TrackerTests/testViewController.1.png diff --git a/Tracker.xcodeproj/project.pbxproj b/Tracker.xcodeproj/project.pbxproj index d595738..28de951 100644 --- a/Tracker.xcodeproj/project.pbxproj +++ b/Tracker.xcodeproj/project.pbxproj @@ -26,6 +26,7 @@ 1A45A67A2ADE8EDB0063F08C /* TrackerCategoryStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A45A6792ADE8EDB0063F08C /* TrackerCategoryStore.swift */; }; 1A45A67C2ADE8F0F0063F08C /* TrackerRecordStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A45A67B2ADE8F0F0063F08C /* TrackerRecordStore.swift */; }; 1A46558A2AD3D2B8006DE343 /* FormatDate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A4655892AD3D2B8006DE343 /* FormatDate.swift */; }; + 1A4AA6D52B34BD8B00411C75 /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A4AA6D42B34BD8B00411C75 /* Colors.swift */; }; 1A52EFAF2ADC194C0002BD4B /* Date.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A52EFAE2ADC194C0002BD4B /* Date.swift */; }; 1A598BB62AC6F352009B166D /* TabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A598BB52AC6F352009B166D /* TabBarController.swift */; }; 1A598BB82AC6FAC5009B166D /* TrackersViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A598BB72AC6FAC5009B166D /* TrackersViewController.swift */; }; @@ -104,6 +105,7 @@ 1A45A6792ADE8EDB0063F08C /* TrackerCategoryStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerCategoryStore.swift; sourceTree = ""; }; 1A45A67B2ADE8F0F0063F08C /* TrackerRecordStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerRecordStore.swift; sourceTree = ""; }; 1A4655892AD3D2B8006DE343 /* FormatDate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormatDate.swift; sourceTree = ""; }; + 1A4AA6D42B34BD8B00411C75 /* Colors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Colors.swift; sourceTree = ""; }; 1A52EFAE2ADC194C0002BD4B /* Date.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Date.swift; sourceTree = ""; }; 1A598BB52AC6F352009B166D /* TabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarController.swift; sourceTree = ""; }; 1A598BB72AC6FAC5009B166D /* TrackersViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackersViewController.swift; sourceTree = ""; }; @@ -218,6 +220,7 @@ children = ( 1A4655892AD3D2B8006DE343 /* FormatDate.swift */, 1A83156D2B2C786000097DE7 /* HandlerResultType.swift */, + 1A4AA6D42B34BD8B00411C75 /* Colors.swift */, ); path = Helpers; sourceTree = ""; @@ -597,6 +600,7 @@ 1A14106A2ADD5185001D6DE6 /* ColorCollectionViewCell.swift in Sources */, 1A8A576F2ACD65C400C060FA /* ChoiceParametrs.swift in Sources */, 1AE08AB82B2F5C250042CE59 /* FiltersViewModel.swift in Sources */, + 1A4AA6D52B34BD8B00411C75 /* Colors.swift in Sources */, 1ADD3D742ACBDBB1005F639F /* TrackerCollectionView.swift in Sources */, 1A9E5C962AD2FF6100010C30 /* String.swift in Sources */, 1A45A6752ADE8BCC0063F08C /* Model.xcdatamodeld in Sources */, diff --git a/Tracker.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Tracker.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..ddf0806 --- /dev/null +++ b/Tracker.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,23 @@ +{ + "pins" : [ + { + "identity" : "swift-snapshot-testing", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-snapshot-testing", + "state" : { + "revision" : "59b663f68e69f27a87b45de48cb63264b8194605", + "version" : "1.15.1" + } + }, + { + "identity" : "swift-syntax", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-syntax.git", + "state" : { + "revision" : "6ad4ea24b01559dde0773e3d091f1b9e36175036", + "version" : "509.0.2" + } + } + ], + "version" : 2 +} diff --git a/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index 60614fa..317cd05 100644 --- a/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -866,8 +866,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "573" - endingLineNumber = "573" + startingLineNumber = "579" + endingLineNumber = "579" landmarkName = "collectionView(_:viewForSupplementaryElementOfKind:at:)" landmarkType = "7"> @@ -1064,8 +1064,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "571" - endingLineNumber = "571" + startingLineNumber = "577" + endingLineNumber = "577" landmarkName = "collectionView(_:viewForSupplementaryElementOfKind:at:)" landmarkType = "7"> @@ -1172,8 +1172,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "556" - endingLineNumber = "556" + startingLineNumber = "562" + endingLineNumber = "562" landmarkName = "collectionView(_:layout:insetForSectionAt:)" landmarkType = "7"> @@ -1188,8 +1188,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "549" - endingLineNumber = "549" + startingLineNumber = "555" + endingLineNumber = "555" landmarkName = "collectionView(_:layout:sizeForItemAt:)" landmarkType = "7"> @@ -1204,8 +1204,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "570" - endingLineNumber = "570" + startingLineNumber = "576" + endingLineNumber = "576" landmarkName = "collectionView(_:viewForSupplementaryElementOfKind:at:)" landmarkType = "7"> @@ -1312,8 +1312,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "284" - endingLineNumber = "284" + startingLineNumber = "290" + endingLineNumber = "290" landmarkName = "registerCellAndHeader(collectionView:)" landmarkType = "7"> @@ -1328,8 +1328,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "565" - endingLineNumber = "565" + startingLineNumber = "571" + endingLineNumber = "571" landmarkName = "collectionView(_:layout:referenceSizeForHeaderInSection:)" landmarkType = "7"> @@ -1455,8 +1455,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "631" - endingLineNumber = "631" + startingLineNumber = "637" + endingLineNumber = "637" landmarkName = "eventSelectionViewControllerDidCancel(_:)" landmarkType = "7"> @@ -1519,8 +1519,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "591" - endingLineNumber = "591" + startingLineNumber = "597" + endingLineNumber = "597" landmarkName = "updateSearchResults(for:)" landmarkType = "7"> @@ -1803,8 +1803,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "598" - endingLineNumber = "598" + startingLineNumber = "604" + endingLineNumber = "604" landmarkName = "updateSearchResults(for:)" landmarkType = "7"> @@ -1851,8 +1851,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "552" - endingLineNumber = "552" + startingLineNumber = "558" + endingLineNumber = "558" landmarkName = "collectionView(_:layout:sizeForItemAt:)" landmarkType = "7"> @@ -2169,8 +2169,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "566" - endingLineNumber = "566" + startingLineNumber = "567" + endingLineNumber = "567" landmarkName = "collectionView(_:viewForSupplementaryElementOfKind:at:)" landmarkType = "7"> @@ -2876,8 +2876,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "599" - endingLineNumber = "599" + startingLineNumber = "605" + endingLineNumber = "605" landmarkName = "updateSearchResults(for:)" landmarkType = "7"> @@ -2924,8 +2924,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "423" - endingLineNumber = "423" + startingLineNumber = "424" + endingLineNumber = "424" landmarkName = "tableView(_:cellForRowAt:)" landmarkType = "7"> @@ -3847,8 +3847,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "502" - endingLineNumber = "502" + startingLineNumber = "508" + endingLineNumber = "508" landmarkName = "collectionView(_:cellForItemAt:)" landmarkType = "7"> @@ -3863,8 +3863,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "528" - endingLineNumber = "528" + startingLineNumber = "534" + endingLineNumber = "534" landmarkName = "collectionView(_:contextMenuConfigurationForItemsAt:point:)" landmarkType = "7"> @@ -3879,8 +3879,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "517" - endingLineNumber = "517" + startingLineNumber = "523" + endingLineNumber = "523" landmarkName = "collectionView(_:contextMenuConfigurationForItemsAt:point:)" landmarkType = "7"> @@ -4021,8 +4021,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "170" - endingLineNumber = "170" + startingLineNumber = "173" + endingLineNumber = "173" landmarkName = "deleteTracker(_:)" landmarkType = "7"> @@ -4130,8 +4130,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "175" - endingLineNumber = "175" + startingLineNumber = "178" + endingLineNumber = "178" landmarkName = "deleteTracker(_:)" landmarkType = "7"> @@ -4460,8 +4460,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "627" - endingLineNumber = "627" + startingLineNumber = "633" + endingLineNumber = "633" landmarkName = "eventSelectionViewController(vc:nameCategories:tracker:)" landmarkType = "7"> @@ -4634,8 +4634,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "126" - endingLineNumber = "126" + startingLineNumber = "130" + endingLineNumber = "130" landmarkName = "addNewTracker(_:nameCategory:)" landmarkType = "7"> @@ -4650,8 +4650,8 @@ filePath = "Tracker/TabBarController/TrackersViews/Cell/TrackersCollectionViewCell.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "140" - endingLineNumber = "140" + startingLineNumber = "141" + endingLineNumber = "141" landmarkName = "didTapAddButton()" landmarkType = "7"> @@ -4730,8 +4730,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "187" - endingLineNumber = "187" + startingLineNumber = "190" + endingLineNumber = "190" landmarkName = "bind()" landmarkType = "7"> @@ -4886,8 +4886,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "196" - endingLineNumber = "196" + startingLineNumber = "199" + endingLineNumber = "199" landmarkName = "bind()" landmarkType = "7"> @@ -4918,8 +4918,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "194" - endingLineNumber = "194" + startingLineNumber = "193" + endingLineNumber = "193" landmarkName = "addPinnedCategory(_:pinnedCategory:)" landmarkType = "7"> @@ -5169,8 +5169,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "235" - endingLineNumber = "235" + startingLineNumber = "236" + endingLineNumber = "236" landmarkName = "bind()" landmarkType = "7"> @@ -5185,8 +5185,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "246" - endingLineNumber = "246" + startingLineNumber = "247" + endingLineNumber = "247" landmarkName = "bind()" landmarkType = "7"> @@ -5248,8 +5248,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "251" - endingLineNumber = "251" + startingLineNumber = "252" + endingLineNumber = "252" landmarkName = "bind()" landmarkType = "7"> @@ -5264,8 +5264,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "232" - endingLineNumber = "232" + startingLineNumber = "233" + endingLineNumber = "233" landmarkName = "bind()" landmarkType = "7"> @@ -5312,8 +5312,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "601" - endingLineNumber = "601" + startingLineNumber = "602" + endingLineNumber = "602" landmarkName = "editTracker(vc:editTracker:nameCategory:indexPath:)" landmarkType = "7"> @@ -5328,8 +5328,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "544" - endingLineNumber = "544" + startingLineNumber = "545" + endingLineNumber = "545" landmarkName = "collectionView(_:layout:sizeForItemAt:)" landmarkType = "7"> @@ -5344,8 +5344,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "557" - endingLineNumber = "557" + startingLineNumber = "558" + endingLineNumber = "558" landmarkName = "collectionView(_:layout:referenceSizeForHeaderInSection:)" landmarkType = "7"> @@ -5360,8 +5360,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "568" - endingLineNumber = "568" + startingLineNumber = "569" + endingLineNumber = "569" landmarkName = "collectionView(_:viewForSupplementaryElementOfKind:at:)" landmarkType = "7"> @@ -5408,8 +5408,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "571" - endingLineNumber = "571" + startingLineNumber = "572" + endingLineNumber = "572" landmarkName = "collectionView(_:viewForSupplementaryElementOfKind:at:)" landmarkType = "7"> @@ -5456,8 +5456,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "516" - endingLineNumber = "516" + startingLineNumber = "517" + endingLineNumber = "517" landmarkName = "collectionView(_:didSelectItemAt:)" landmarkType = "7"> @@ -5472,8 +5472,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "523" - endingLineNumber = "523" + startingLineNumber = "524" + endingLineNumber = "524" landmarkName = "collectionView(_:didSelectItemAt:)" landmarkType = "7"> @@ -5488,8 +5488,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "491" - endingLineNumber = "491" + startingLineNumber = "492" + endingLineNumber = "492" landmarkName = "collectionView(_:cellForItemAt:)" landmarkType = "7"> @@ -5520,8 +5520,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "605" - endingLineNumber = "605" + startingLineNumber = "606" + endingLineNumber = "606" landmarkName = "editTracker(vc:editTracker:nameCategory:indexPath:)" landmarkType = "7"> @@ -5583,8 +5583,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "531" - endingLineNumber = "531" + startingLineNumber = "532" + endingLineNumber = "532" landmarkName = "collectionView(_:didDeselectItemAt:)" landmarkType = "7"> @@ -5599,8 +5599,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "536" - endingLineNumber = "536" + startingLineNumber = "537" + endingLineNumber = "537" landmarkName = "collectionView(_:didDeselectItemAt:)" landmarkType = "7"> @@ -5615,8 +5615,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "103" - endingLineNumber = "103" + startingLineNumber = "107" + endingLineNumber = "107" landmarkName = "searchTracker(id:)" landmarkType = "7"> @@ -5663,8 +5663,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "101" - endingLineNumber = "101" + startingLineNumber = "105" + endingLineNumber = "105" landmarkName = "searchTracker(id:)" landmarkType = "7"> @@ -5695,8 +5695,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "639" - endingLineNumber = "639" + startingLineNumber = "645" + endingLineNumber = "645" landmarkName = "trackerViewController(vc:nameCategory:tracker:)" landmarkType = "7"> @@ -5773,8 +5773,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "218" - endingLineNumber = "218" + startingLineNumber = "217" + endingLineNumber = "217" landmarkName = "controllerDidChangeContent(_:)" landmarkType = "7"> @@ -5821,8 +5821,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "275" - endingLineNumber = "275" + startingLineNumber = "276" + endingLineNumber = "276" landmarkName = "didTapGreateButton()" landmarkType = "7"> @@ -6079,8 +6079,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "153" - endingLineNumber = "153" + startingLineNumber = "156" + endingLineNumber = "156" landmarkName = "updateTracker(tracker:nameCategory:)" landmarkType = "7"> @@ -6157,8 +6157,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "141" - endingLineNumber = "141" + startingLineNumber = "144" + endingLineNumber = "144" landmarkName = "updateTracker(tracker:nameCategory:)" landmarkType = "7"> @@ -6220,8 +6220,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "290" - endingLineNumber = "290" + startingLineNumber = "291" + endingLineNumber = "291" landmarkName = "didTapGreateButton()" landmarkType = "7"> @@ -6284,8 +6284,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "137" - endingLineNumber = "137" + startingLineNumber = "140" + endingLineNumber = "140" landmarkName = "updateTracker(tracker:nameCategory:)" landmarkType = "7"> @@ -6300,8 +6300,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "282" - endingLineNumber = "282" + startingLineNumber = "283" + endingLineNumber = "283" landmarkName = "didTapGreateButton()" landmarkType = "7"> @@ -6316,8 +6316,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "294" - endingLineNumber = "294" + startingLineNumber = "295" + endingLineNumber = "295" landmarkName = "didTapGreateButton()" landmarkType = "7"> @@ -6505,8 +6505,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "209" - endingLineNumber = "209" + startingLineNumber = "212" + endingLineNumber = "212" landmarkName = "bind()" landmarkType = "7"> @@ -6521,8 +6521,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "207" - endingLineNumber = "207" + startingLineNumber = "210" + endingLineNumber = "210" landmarkName = "bind()" landmarkType = "7"> @@ -6537,8 +6537,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "210" - endingLineNumber = "210" + startingLineNumber = "209" + endingLineNumber = "209" landmarkName = "TrackerStore" landmarkType = "21"> @@ -6630,8 +6630,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "208" - endingLineNumber = "208" + startingLineNumber = "207" + endingLineNumber = "207" landmarkName = "TrackerStore" landmarkType = "21"> @@ -6708,8 +6708,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "212" - endingLineNumber = "212" + startingLineNumber = "215" + endingLineNumber = "215" landmarkName = "bind()" landmarkType = "7"> @@ -6724,8 +6724,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "210" - endingLineNumber = "210" + startingLineNumber = "213" + endingLineNumber = "213" landmarkName = "bind()" landmarkType = "7"> @@ -6788,8 +6788,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "619" - endingLineNumber = "619" + startingLineNumber = "625" + endingLineNumber = "625" landmarkName = "didTrackerCompleted(_:)" landmarkType = "7"> @@ -6851,8 +6851,8 @@ filePath = "Tracker/TabBarController/TrackersViews/Cell/TrackersCollectionViewCell.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "153" - endingLineNumber = "153" + startingLineNumber = "154" + endingLineNumber = "154" landmarkName = "updateLableCountAndImageAddButton(_:)" landmarkType = "7"> @@ -6867,8 +6867,8 @@ filePath = "Tracker/TabBarController/TrackersViews/Cell/TrackersCollectionViewCell.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "156" - endingLineNumber = "156" + startingLineNumber = "157" + endingLineNumber = "157" landmarkName = "updateLableCountAndImageAddButton(_:)" landmarkType = "7"> @@ -6931,9 +6931,9 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "254" - endingLineNumber = "254" - landmarkName = "filterForState(state:)" + startingLineNumber = "257" + endingLineNumber = "257" + landmarkName = "filterState(state:)" landmarkType = "7"> @@ -6947,9 +6947,9 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "257" - endingLineNumber = "257" - landmarkName = "filterForState(state:)" + startingLineNumber = "260" + endingLineNumber = "260" + landmarkName = "filterState(state:)" landmarkType = "7"> @@ -6963,9 +6963,9 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "263" - endingLineNumber = "263" - landmarkName = "filterForState(state:)" + startingLineNumber = "266" + endingLineNumber = "266" + landmarkName = "filterState(state:)" landmarkType = "7"> @@ -6979,9 +6979,9 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "260" - endingLineNumber = "260" - landmarkName = "filterForState(state:)" + startingLineNumber = "263" + endingLineNumber = "263" + landmarkName = "filterState(state:)" landmarkType = "7"> @@ -6995,8 +6995,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "169" - endingLineNumber = "169" + startingLineNumber = "172" + endingLineNumber = "172" landmarkName = "viewDidLoad()" landmarkType = "7"> @@ -7011,8 +7011,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "172" - endingLineNumber = "172" + startingLineNumber = "175" + endingLineNumber = "175" landmarkName = "viewDidLoad()" landmarkType = "7"> @@ -7027,8 +7027,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "182" - endingLineNumber = "182" + startingLineNumber = "185" + endingLineNumber = "185" landmarkName = "bind()" landmarkType = "7"> @@ -7059,8 +7059,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "33" - endingLineNumber = "33" + startingLineNumber = "32" + endingLineNumber = "32" landmarkName = "fetchedTrackerResultController" landmarkType = "24"> @@ -7068,80 +7068,17 @@ - - - - - - - - - - - - + startingLineNumber = "34" + endingLineNumber = "34" + landmarkName = "fetchedTrackerResultController" + landmarkType = "24"> diff --git a/Tracker/Domain/Store/TrackerCategoryStore.swift b/Tracker/Domain/Store/TrackerCategoryStore.swift index 494f149..a26180f 100644 --- a/Tracker/Domain/Store/TrackerCategoryStore.swift +++ b/Tracker/Domain/Store/TrackerCategoryStore.swift @@ -32,7 +32,7 @@ final class TrackerCategoryStore: NSObject { request.sortDescriptors = [sortPinned, sortName] let fetchedResultController = NSFetchedResultsController(fetchRequest: request, managedObjectContext: context, - sectionNameKeyPath: nil, + sectionNameKeyPath: #keyPath(TrackerCategoryCoreData.nameCategory), cacheName: nil) fetchedResultController.delegate = self try? fetchedResultController.performFetch() diff --git a/Tracker/Domain/Store/TrackerStore.swift b/Tracker/Domain/Store/TrackerStore.swift index 51946cf..6641bed 100644 --- a/Tracker/Domain/Store/TrackerStore.swift +++ b/Tracker/Domain/Store/TrackerStore.swift @@ -10,7 +10,6 @@ import CoreData protocol TrackerStoreProtocol { func addNewTracker(_ tracker: Tracker, nameCategory: String) -> Result - func getTracker(indexPath: IndexPath) -> TrackerCoreData func getTrackers(_ objects: [TrackerCoreData]) -> Result<[Tracker], Error> func updateTracker(tracker: Tracker, nameCategory: String) -> Result func deleteTracker(_ id: UUID) -> Result @@ -31,6 +30,7 @@ final class TrackerStore: NSObject { private lazy var fetchedTrackerResultController: NSFetchedResultsController = { let request = TrackerCoreData.fetchRequest() let sortName = NSSortDescriptor(keyPath: \TrackerCoreData.name, ascending: true) + let sortId = NSSortDescriptor(keyPath: \TrackerCoreData.id, ascending: true) request.sortDescriptors = [sortName] let fetchedResultController = NSFetchedResultsController(fetchRequest: request, managedObjectContext: context, @@ -96,6 +96,10 @@ private extension TrackerStore { } } + func getTrackerFor(_ indexPath: IndexPath) -> TrackerCoreData { + fetchedTrackerResultController.object(at: indexPath) + } + func searchTracker(id: UUID) throws -> TrackerCoreData? { let request = NSFetchRequest(entityName: "\(TrackerCoreData.self)") request.returnsObjectsAsFaults = false @@ -132,7 +136,6 @@ extension TrackerStore: TrackerStoreProtocol { } func updateTracker(tracker: Tracker, nameCategory: String) -> Result { - let _ = fetchedTrackerResultController do { if let categoryCD = try searchCategory(name: nameCategory) { do { @@ -178,16 +181,12 @@ extension TrackerStore: TrackerStoreProtocol { func getTrackers(_ objects: [TrackerCoreData]) -> Result<[Tracker], Error> { do { let trackers = try objects.map({ try tracker(from: $0) }) - return .success(trackers) + return .success(trackers.sorted { $0.name < $1.name }) } catch { return .failure(error) } } - - func getTracker(indexPath: IndexPath) -> TrackerCoreData { - fetchedTrackerResultController.object(at: indexPath) - } - + func addPinnedCategory(_ id: UUID, pinnedCategory: PinnCategoryCoreData) -> Result { do { if let trackerCD = try searchTracker(id: id) { diff --git a/Tracker/Helpers/Colors.swift b/Tracker/Helpers/Colors.swift new file mode 100644 index 0000000..65b125e --- /dev/null +++ b/Tracker/Helpers/Colors.swift @@ -0,0 +1,28 @@ +// +// Colors.swift +// Tracker +// +// Created by Григорий Машук on 21.12.23. +// + +import UIKit + +final class Colors { + let viewBackground = UIColor.systemBackground + + let buttonDisabledColor = UIColor { (traits: UITraitCollection) -> UIColor in + if traits.userInterfaceStyle == .light { + return UIColor.lightGray + } else { + return UIColor(red: 0.8, green: 0.5, blue: 0.8, alpha: 1) + } + } + + let whiteBlackItemColor = UIColor { (traits: UITraitCollection) -> UIColor in + if traits.userInterfaceStyle == .light { + return UIColor.black + } else { + return UIColor.white + } + } +} diff --git a/Tracker/TabBarController/TabBarController.swift b/Tracker/TabBarController/TabBarController.swift index 1868f8f..d2dab7f 100644 --- a/Tracker/TabBarController/TabBarController.swift +++ b/Tracker/TabBarController/TabBarController.swift @@ -19,6 +19,8 @@ final class TabBarController: UITabBarController { static let insertImage = UIEdgeInsets(top: 5, left: 0, bottom: 0, right: 0) } + private let colors = Colors() + override func viewDidLoad() { super.viewDidLoad() setupTabBar() @@ -40,6 +42,19 @@ final class TabBarController: UITabBarController { insert: ConstantsTabBar.insertImage) ] } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + if #available(iOS 13.0, *), + // Проверяем только изменение цветовой схемы + traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) { + if traitCollection.userInterfaceStyle == .dark { + tabBar.layer.borderColor = UIColor.black.cgColor + } else { + tabBar.layer.borderColor = UIColor.grayDay.cgColor + } + } + } } //MARK: - Setup @@ -58,7 +73,6 @@ private extension TabBarController { func setupTabBar() { tabBar.layer.borderWidth = 1 - tabBar.layer.borderColor = UIColor.grayDay.cgColor tabBar.clipsToBounds = true } } diff --git a/Tracker/TabBarController/TrackersViews/Cell/TrackersCollectionViewCell.swift b/Tracker/TabBarController/TrackersViews/Cell/TrackersCollectionViewCell.swift index 20198ce..ab5ffd2 100644 --- a/Tracker/TabBarController/TrackersViews/Cell/TrackersCollectionViewCell.swift +++ b/Tracker/TabBarController/TrackersViews/Cell/TrackersCollectionViewCell.swift @@ -34,6 +34,7 @@ final class TrackersCollectionViewCell: UICollectionViewCell { weak var delegate: TrackersCollectionViewCellDelegate? private var isSelectedAddButton: Bool = false + private let colors = Colors() private lazy var colorView: UIView = { let colorView = UIView() @@ -76,7 +77,7 @@ final class TrackersCollectionViewCell: UICollectionViewCell { private lazy var dayCounterLable: UILabel = { let dayCounterLable = UILabel() - dayCounterLable.textColor = .blackDay + dayCounterLable.textColor = colors.whiteBlackItemColor dayCounterLable.font = ConstantsTrackerCell.fontLableDayCounter return dayCounterLable @@ -157,7 +158,6 @@ extension TrackersCollectionViewCell { } } - //метод удаляет фон на кнопку addButton func updateBackgraundAddButton(isHidden: Bool) { backgroundAddButtonView.isHidden = isHidden } diff --git a/Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift b/Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift index b99d2da..4f42949 100644 --- a/Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift +++ b/Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift @@ -51,6 +51,7 @@ final class CreateTrackerViewController: UIViewController { private let viewModel: EditTrackerModelProtocol private let handlerResultType = HandlerResultType() + private let colors = Colors() private lazy var newHabitLabel: UILabel = { let newHabitLabel = UILabel() diff --git a/Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift b/Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift index 73b7aa3..09623c1 100644 --- a/Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift +++ b/Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift @@ -36,17 +36,20 @@ final class TrackersViewController: UIViewController { static let datePickerCornerRadius = CGFloat(8) static let buttonFilterCornerRadius = CGFloat(16) static let heightCollectionView = CGFloat(148) + static let cellSpacing = CGFloat(12) static let fontLableTextStab = UIFont.systemFont(ofSize: 12, weight: .medium) static let fontLabelHeader = UIFont.boldSystemFont(ofSize: 34) static let fontButtonFilter = UIFont.systemFont(ofSize: 17, weight: .medium) + static let cellCount = 2 static let firstWeekday = 2 } weak var delegate: TrackersViewControllerDelegate? private var viewModel: TrackerViewModelProtocol? private let handler = HandlerResultType() + private let colors = Colors() private lazy var horisontalStack: UIStackView = { let horisontalStack = UIStackView() @@ -75,12 +78,15 @@ final class TrackersViewController: UIViewController { private lazy var datePicker: UIDatePicker = { let datePicker = UIDatePicker() + datePicker.backgroundColor = .liteGray datePicker.datePickerMode = .date + datePicker.tintColor = .blueDay datePicker.preferredDatePickerStyle = .compact datePicker.calendar.firstWeekday = ConstantsTrackerVc.firstWeekday datePicker.layer.cornerRadius = ConstantsTrackerVc.datePickerCornerRadius datePicker.layer.masksToBounds = true - datePicker.locale = .current + datePicker.overrideUserInterfaceStyle = .light + datePicker.locale = .autoupdatingCurrent datePicker.addTarget(self, action: #selector(actionForTapDatePicker), for: .valueChanged) return datePicker @@ -155,18 +161,15 @@ final class TrackersViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - view.backgroundColor = .white + view.backgroundColor = colors.viewBackground setupUIElement() viewModel = TrackerViewModel() bind() guard let viewModel = viewModel as? TrackerViewModel else { return } -// guard let visibleCategories = handler.resultTypeHandlerGetValue(viewModel.visibleCategory) -// else { return } -// self.showStabView(flag: !visibleCategories.isEmpty) setColorButtonFilter(state: viewModel.getFilterState(), button: buttonFilter) if let state = viewModel.getSelectFilter() { if let state = FiltersState(rawValue: state) { - filterForState(state: state) + filterState(state: state) } } else { viewModel.allTrackers(date: datePicker.date) @@ -179,7 +182,7 @@ private extension TrackersViewController { guard let viewModel = viewModel as? TrackerViewModel else { return } viewModel.$category.bind { [weak self] _ in guard let self else { return } - filterForState(state: viewModel.getFilterState()) + filterState(state: viewModel.getFilterState()) } viewModel.$indexPath.bind { [weak self] indexPath in @@ -227,7 +230,7 @@ private extension TrackersViewController { @objc func actionForTapDatePicker() { guard let viewModel else { return } - filterForState(state: viewModel.getFilterState()) + filterState(state: viewModel.getFilterState()) } @objc @@ -247,7 +250,7 @@ private extension TrackersViewController { [imageViewStab, lableTextStab].forEach { $0.isHidden = flag } } - func filterForState(state: FiltersState) { + func filterState(state: FiltersState) { guard let viewModel else { return } switch state { case .allTrackers: @@ -270,7 +273,10 @@ private extension TrackersViewController { } func greateTrackerCollectionView() -> TrackerCollectionView { - let params = GeometricParams(cellCount: 2, leftInset: .zero, rightInset: .zero, cellSpacing: 12) + let params = GeometricParams(cellCount: ConstantsTrackerVc.cellCount, + leftInset: .zero, + rightInset: .zero, + cellSpacing: ConstantsTrackerVc.cellSpacing) let layout = UICollectionViewFlowLayout() let trackerCollectionView = TrackerCollectionView(frame: .zero, collectionViewLayout: layout, @@ -382,7 +388,7 @@ private extension TrackersViewController { topItem.rightBarButtonItem = UIBarButtonItem(customView: datePicker) topItem.leftBarButtonItem?.imageInsets = UIEdgeInsets(top: .zero, left: -10, bottom: .zero, right: .zero) - topItem.leftBarButtonItem?.tintColor = .blackDay + topItem.leftBarButtonItem?.tintColor = colors.whiteBlackItemColor navBar.backgroundColor = .clear navigationItem.title = ConstantsTrackerVc.headerText diff --git a/TrackerTests/TrackerTests.swift b/TrackerTests/TrackerTests.swift index 8d7f59e..2b64bef 100644 --- a/TrackerTests/TrackerTests.swift +++ b/TrackerTests/TrackerTests.swift @@ -13,7 +13,6 @@ final class TrackerTests: XCTestCase { func testViewController() { let vc = TabBarController() - assertSnapshot(matching: vc, as: .image) } } diff --git a/TrackerTests/__Snapshots__/TrackerTests/testViewController.1.png b/TrackerTests/__Snapshots__/TrackerTests/testViewController.1.png new file mode 100644 index 0000000000000000000000000000000000000000..55b9338152d0c3b0e8f2302e24c6935625bb476b GIT binary patch literal 197401 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

mCAf#?SAJOb0rArlJwzgdgA7*E<#Jl89Djz-fY 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

NT*(|tlX>Q<7HQofEq9AH1wo1 zeQrERTvK!OwHxX6xO1mufhKU!tEp5JY2UyU!`uRwq=M#&jZ|gRY>zuj!SU z2hDvwt9tIaho|k^K1Q)_Sx113@HUC7Zx0mG_XL`YCJGe9?G>2Qg5o;7Nl%p356G!- z5^f`E63Bcx>T|&@-69XICk_~`F<-eZAQW9$(Y=DVbaS;55WAQG3pJ=ck`;&fACJhS zTN!y#+ZvzrcEjV#7l-0Ua{WSVnf9?EctLwjTB>rh`@7cGfj^MKAJ%B3-Bv3V%!r1X z1I^B+$22L+NBeiYB3Tx&Zp!-_4Wf@ZMj^_h*qIkO3a1}l*YB5E-fh3`&$8)xVf_#? z2}#-qPQ_l!A$K6;3V>rt3Ox6v*txE`w^U5=O0#U~iQ?cyApcdZ9CmI^Qbd*x$v(N| zE72~}8^~OlAkNZHA+-Z9?io1Db0(y(C=s&8%Z~YlIKND2Q=?Xmz!fEg3gRZbTll$n ze{+8L>wdX(h@~ap!A%FIY>zuPR?<>cK;3=npC-w;c%J=pbQ;Yg;_QXw9J8t@N^l`x z8Ed>NUXa*!W0)TESPu7<#=?!T!tD;nlc-O|muLLIEx9JV)p@MirP&B!;|OMZeD%W= z_sCDjOjdnK;veSUy}F2XSRle0lFi0&5W$!Zp}V z{V+x9*WaQVAM02;gt%7Bz1iE@Am*4n5+Z;84_e^W;9`hyh!Y=f3Z;7Ma~&6RxaMpF z?Df%KYH<1(Mdr>;rbBJu_5?B|(884uNP~vxnBa)mAZiVZ30pn{KeuhbWtS+bW~pap z_NaG)kHJNSvFnFRUiLv~+ojl0V)4RCen9DE$`=^?=FI3w5SaSvbXL1B;}_`G<1y#X zVYl@5xuZ`u$GLnJjvEzpFMZ(|=Wi7xMqX>d%}>MWGsM)?tf_I9JlFs1BF@%Y^O27R9%#ieV8iJ~eIf*Bc4eR{ zUp#SFOj=sE5!Wf&FbZ`}oy>iZHWcO?ql>roRvYJzp?yM0p6v^0Kgm;^eDfKY$(i>0 z0G0;x4FftoL|9w4D8SQ3@Q+Z)u!01y2IuRMY8{j)=H!x}-(HCw`{lq8VG}+sDYLZR zd{~g06pneuITFu%@>lhk{4un&+RbuidOR~QNY`pm04{W4BsjSnSn~4~$9+5rsaFr1 z0@HP$NyC2kX6`#o+GPB^cH6{UK1E<3E*6 zaz)Ey%|nD0AL*#GZ&Il8*4x*+Xl7WXpN~3#Obv0n7@2CQ0rav9!7s~v@D99?) z{0a#fd*%$mYc7JYnhkKt0R%p1)-lBqidjWypFNRf~H0?LQf)3wrE>FGMz$KTMxti*;@E@rtsdK z7o%5m$C@!p@Ou^X*~-oZB9djbd*qjNofeqr!vsr&qDDP_G0+scWVOS|U}tqCV|8Zu zy!hI|tUTFW*hdIY$+)=ugf37XZAROg&ngzaEQasmPH(+G?|yUC2$HQEk-yJihIiMN zgwkK2=4X2Jq0!Ev#-E~+?ODKXHic0S%6;T~9ltF8Hj@xnvk}9B7D_V(7G|c&&^f2q zTcThTwLgu*BvC(x9JMn+7{m})6$k8r!2&oAS4vznBX9A39h^5YzA*W?ed`bK>~9Al zaTu6C`s2R=VA0i3{pVSCoqzwsCNPFrVPiMnx3#}r37z0%baf2R K Date: Mon, 25 Dec 2023 10:06:48 +0300 Subject: [PATCH 08/24] add analytics --- .gitignore | 90 +++++++++ Podfile | 12 ++ Podfile.lock | 23 +++ Tracker.xcodeproj/project.pbxproj | 104 ++++++++++- .../xcdebugger/Breakpoints_v2.xcbkptlist | 176 +++++++++--------- Tracker.xcworkspace/contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/swiftpm/Package.resolved | 23 +++ .../AnalyticsService/AnalyticsService.swift | 77 ++++++++ Tracker/Application/AppDelegate.swift | 6 + Tracker/Models/Enum/EventAnalytics.swift | 32 ++++ Tracker/Models/MetrycEvent.swift | 14 ++ .../TabBarController/TabBarController.swift | 4 + .../StatisticViewController.swift | 0 .../CreateTrackerViewController.swift | 1 + .../TrackersViewController.swift | 6 + 16 files changed, 497 insertions(+), 89 deletions(-) create mode 100644 .gitignore create mode 100644 Podfile create mode 100644 Podfile.lock create mode 100644 Tracker.xcworkspace/contents.xcworkspacedata create mode 100644 Tracker.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 Tracker.xcworkspace/xcshareddata/swiftpm/Package.resolved create mode 100644 Tracker/AnalyticsService/AnalyticsService.swift create mode 100644 Tracker/Models/Enum/EventAnalytics.swift create mode 100644 Tracker/Models/MetrycEvent.swift rename Tracker/TabBarController/{ => TrackersViews}/StatisticViews/StatisticViewController.swift (100%) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9311143 --- /dev/null +++ b/.gitignore @@ -0,0 +1,90 @@ +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## User settings +xcuserdata/ + +## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) +*.xcscmblueprint +*.xccheckout + +## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) +build/ +DerivedData/ +*.moved-aside +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 + +## Obj-C/Swift specific +*.hmap + +## App packaging +*.ipa +*.dSYM.zip +*.dSYM + +## Playgrounds +timeline.xctimeline +playground.xcworkspace + +# Swift Package Manager +# +# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. +# Packages/ +# Package.pins +# Package.resolved +# *.xcodeproj +# +# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata +# hence it is not needed unless you have added a package configuration file to your project +# .swiftpm + +.build/ + +# CocoaPods +# +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control +# +Pods/ +# +# Add this line if you want to avoid checking in source code from the Xcode workspace +# *.xcworkspace + +# Carthage +# +# Add this line if you want to avoid checking in source code from Carthage dependencies. +# Carthage/Checkouts + +Carthage/Build/ + +# Accio dependency management +Dependencies/ +.accio/ + +# fastlane +# +# It is recommended to not store the screenshots in the git repo. +# Instead, use fastlane to re-generate the screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://docs.fastlane.tools/best-practices/source-control/#source-control + +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots/**/*.png +fastlane/test_output + +# Code Injection +# +# After new code Injection tools there's a generated folder /iOSInjectionProject +# https://github.com/johnno1962/injectionforxcode + +iOSInjectionProject/ \ No newline at end of file diff --git a/Podfile b/Podfile new file mode 100644 index 0000000..ba8c6fb --- /dev/null +++ b/Podfile @@ -0,0 +1,12 @@ +platform :ios, '13.4' + +target 'Tracker' do + use_frameworks! + + pod 'YandexMobileMetrica' + + target 'TrackerTests' do + inherit! :search_paths + end + +end diff --git a/Podfile.lock b/Podfile.lock new file mode 100644 index 0000000..99cf77c --- /dev/null +++ b/Podfile.lock @@ -0,0 +1,23 @@ +PODS: + - YandexMobileMetrica (4.5.2): + - YandexMobileMetrica/Static (= 4.5.2) + - YandexMobileMetrica/Static (4.5.2): + - YandexMobileMetrica/Static/Core (= 4.5.2) + - YandexMobileMetrica/Static/Crashes (= 4.5.2) + - YandexMobileMetrica/Static/Core (4.5.2) + - YandexMobileMetrica/Static/Crashes (4.5.2): + - YandexMobileMetrica/Static/Core + +DEPENDENCIES: + - YandexMobileMetrica + +SPEC REPOS: + trunk: + - YandexMobileMetrica + +SPEC CHECKSUMS: + YandexMobileMetrica: f5368ee93f286c793d73b58da00929babfc897c1 + +PODFILE CHECKSUM: 27628e4a578a0edaa576d75cf46598218538c633 + +COCOAPODS: 1.14.3 diff --git a/Tracker.xcodeproj/project.pbxproj b/Tracker.xcodeproj/project.pbxproj index 28de951..130995a 100644 --- a/Tracker.xcodeproj/project.pbxproj +++ b/Tracker.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 1A0077F82B2DD6DF000EAE69 /* CategoryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A0077F72B2DD6DF000EAE69 /* CategoryViewModel.swift */; }; + 1A0C3C192B388CC3008E5202 /* MetrycEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A0C3C182B388CC3008E5202 /* MetrycEvent.swift */; }; 1A14106A2ADD5185001D6DE6 /* ColorCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A1410692ADD5185001D6DE6 /* ColorCollectionViewCell.swift */; }; 1A19A7462B1672A200DD415B /* UpdateTrackerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A19A7452B1672A200DD415B /* UpdateTrackerModel.swift */; }; 1A1E94B52AD952EF00FE9A0C /* CustomTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A1E94B42AD952EF00FE9A0C /* CustomTableViewCell.swift */; }; @@ -60,6 +61,8 @@ 1AB78AA32AC750A900904AB2 /* TrackersCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AB78AA22AC750A900904AB2 /* TrackersCollectionViewCell.swift */; }; 1ABABBF72AFC10AD007281E0 /* CustomCellModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ABABBF62AFC10AD007281E0 /* CustomCellModel.swift */; }; 1ABB3D2C2AC9AAB40037B601 /* HeaderReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ABB3D2B2AC9AAB40037B601 /* HeaderReusableView.swift */; }; + 1AC642722B383AD50072E081 /* AnalyticsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AC642712B383AD50072E081 /* AnalyticsService.swift */; }; + 1AC642742B38442E0072E081 /* EventAnalytics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AC642732B38442E0072E081 /* EventAnalytics.swift */; }; 1AD277712ACD4B1800F92219 /* UITextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AD277702ACD4B1800F92219 /* UITextField.swift */; }; 1ADD3D742ACBDBB1005F639F /* TrackerCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ADD3D732ACBDBB1005F639F /* TrackerCollectionView.swift */; }; 1AE08AB82B2F5C250042CE59 /* FiltersViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AE08AB72B2F5C250042CE59 /* FiltersViewModel.swift */; }; @@ -72,6 +75,8 @@ 1AE989302ACAF14B00392315 /* TrackerCategoryModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AE9892F2ACAF14B00392315 /* TrackerCategoryModel.swift */; }; 1AE989322ACAF20500392315 /* TrackerRecordModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AE989312ACAF20500392315 /* TrackerRecordModel.swift */; }; 1AF05E062B2B011D0099D022 /* ShedulViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AF05E052B2B011D0099D022 /* ShedulViewModel.swift */; }; + 767676C468C29BFDD74BC6BA /* Pods_TrackerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D9581169B12C04C00893FD1F /* Pods_TrackerTests.framework */; }; + A080B51F7DCAD954FB042507 /* Pods_Tracker.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 236701E2AE3D50DC6C3D59C3 /* Pods_Tracker.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -86,6 +91,7 @@ /* Begin PBXFileReference section */ 1A0077F72B2DD6DF000EAE69 /* CategoryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CategoryViewModel.swift; sourceTree = ""; }; + 1A0C3C182B388CC3008E5202 /* MetrycEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetrycEvent.swift; sourceTree = ""; }; 1A1410692ADD5185001D6DE6 /* ColorCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorCollectionViewCell.swift; sourceTree = ""; }; 1A19A7452B1672A200DD415B /* UpdateTrackerModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateTrackerModel.swift; sourceTree = ""; }; 1A1E94B42AD952EF00FE9A0C /* CustomTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTableViewCell.swift; sourceTree = ""; }; @@ -144,6 +150,8 @@ 1AB78AA22AC750A900904AB2 /* TrackersCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackersCollectionViewCell.swift; sourceTree = ""; }; 1ABABBF62AFC10AD007281E0 /* CustomCellModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomCellModel.swift; sourceTree = ""; }; 1ABB3D2B2AC9AAB40037B601 /* HeaderReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderReusableView.swift; sourceTree = ""; }; + 1AC642712B383AD50072E081 /* AnalyticsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsService.swift; sourceTree = ""; }; + 1AC642732B38442E0072E081 /* EventAnalytics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventAnalytics.swift; sourceTree = ""; }; 1AD277702ACD4B1800F92219 /* UITextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITextField.swift; sourceTree = ""; }; 1ADD3D732ACBDBB1005F639F /* TrackerCollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerCollectionView.swift; sourceTree = ""; }; 1AE08AB72B2F5C250042CE59 /* FiltersViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FiltersViewModel.swift; sourceTree = ""; }; @@ -155,6 +163,12 @@ 1AE9892F2ACAF14B00392315 /* TrackerCategoryModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerCategoryModel.swift; sourceTree = ""; }; 1AE989312ACAF20500392315 /* TrackerRecordModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerRecordModel.swift; sourceTree = ""; }; 1AF05E052B2B011D0099D022 /* ShedulViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShedulViewModel.swift; sourceTree = ""; }; + 236701E2AE3D50DC6C3D59C3 /* Pods_Tracker.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Tracker.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 6682ACA7FBAC750BABF3A93E /* Pods-Tracker.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tracker.release.xcconfig"; path = "Target Support Files/Pods-Tracker/Pods-Tracker.release.xcconfig"; sourceTree = ""; }; + 6E5A6EC6ED52C7BE406D080A /* Pods-TrackerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TrackerTests.release.xcconfig"; path = "Target Support Files/Pods-TrackerTests/Pods-TrackerTests.release.xcconfig"; sourceTree = ""; }; + A4D50E97DFCE527E61A29054 /* Pods-Tracker.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tracker.debug.xcconfig"; path = "Target Support Files/Pods-Tracker/Pods-Tracker.debug.xcconfig"; sourceTree = ""; }; + B99BCC20A3F3BF18F8D8A525 /* Pods-TrackerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TrackerTests.debug.xcconfig"; path = "Target Support Files/Pods-TrackerTests/Pods-TrackerTests.debug.xcconfig"; sourceTree = ""; }; + D9581169B12C04C00893FD1F /* Pods_TrackerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_TrackerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -162,6 +176,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + A080B51F7DCAD954FB042507 /* Pods_Tracker.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -171,6 +186,7 @@ files = ( 1AE96E2B2B34278E0061F10D /* InlineSnapshotTesting in Frameworks */, 1AE96E2D2B34278E0061F10D /* SnapshotTesting in Frameworks */, + 767676C468C29BFDD74BC6BA /* Pods_TrackerTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -263,7 +279,6 @@ children = ( 1A598BB52AC6F352009B166D /* TabBarController.swift */, 1A598BB92AC6FB97009B166D /* TrackersViews */, - 1A8A576D2ACD603200C060FA /* StatisticViews */, ); path = TabBarController; sourceTree = ""; @@ -274,6 +289,7 @@ 1A3ED44E2ACDE1020006EBBB /* ViewControllers */, 1A3ED44D2ACDE0B40006EBBB /* CollectionView */, 1AAC05C12ACC56EC00AA4B6B /* SuplementaryView */, + 1A8A576D2ACD603200C060FA /* StatisticViews */, 1AAC05C02ACC56A500AA4B6B /* Cell */, ); path = TrackersViews; @@ -291,6 +307,7 @@ 1ABABBF62AFC10AD007281E0 /* CustomCellModel.swift */, 1A19A7452B1672A200DD415B /* UpdateTrackerModel.swift */, 1A66BB182B25D16500838F5F /* PinnTrackerModel.swift */, + 1A0C3C182B388CC3008E5202 /* MetrycEvent.swift */, ); path = Models; sourceTree = ""; @@ -309,6 +326,8 @@ 1A8A7FE42AC55D970038CB42 /* Tracker */, 1AE96E212B3424E40061F10D /* TrackerTests */, 1A8A7FE32AC55D970038CB42 /* Products */, + 2BB783427AB838841367EF6B /* Pods */, + 34047FD0212AA329047260EE /* Frameworks */, ); sourceTree = ""; usesTabs = 0; @@ -325,6 +344,7 @@ 1A8A7FE42AC55D970038CB42 /* Tracker */ = { isa = PBXGroup; children = ( + 1AC642702B383AB60072E081 /* AnalyticsService */, 1A9AC5B52AF3854A00A9213D /* ViewModel */, 1A8ED50B2AEFE9AA00BA4EE4 /* Onboarding */, 1A2FC2482AED1E05000156FE /* Domain */, @@ -404,6 +424,14 @@ path = PropertyWrappers; sourceTree = ""; }; + 1AC642702B383AB60072E081 /* AnalyticsService */ = { + isa = PBXGroup; + children = ( + 1AC642712B383AD50072E081 /* AnalyticsService.swift */, + ); + path = AnalyticsService; + sourceTree = ""; + }; 1AE96E212B3424E40061F10D /* TrackerTests */ = { isa = PBXGroup; children = ( @@ -421,6 +449,7 @@ 1A32FF902ADC72880065E44C /* Header.swift */, 1AE27C2C2B0604A800C4A925 /* Errors.swift */, 1A6724122B2F108000719A9D /* FiltersState.swift */, + 1AC642732B38442E0072E081 /* EventAnalytics.swift */, ); path = Enum; sourceTree = ""; @@ -434,6 +463,26 @@ path = Application; sourceTree = ""; }; + 2BB783427AB838841367EF6B /* Pods */ = { + isa = PBXGroup; + children = ( + A4D50E97DFCE527E61A29054 /* Pods-Tracker.debug.xcconfig */, + 6682ACA7FBAC750BABF3A93E /* Pods-Tracker.release.xcconfig */, + B99BCC20A3F3BF18F8D8A525 /* Pods-TrackerTests.debug.xcconfig */, + 6E5A6EC6ED52C7BE406D080A /* Pods-TrackerTests.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + 34047FD0212AA329047260EE /* Frameworks */ = { + isa = PBXGroup; + children = ( + 236701E2AE3D50DC6C3D59C3 /* Pods_Tracker.framework */, + D9581169B12C04C00893FD1F /* Pods_TrackerTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -441,6 +490,7 @@ isa = PBXNativeTarget; buildConfigurationList = 1A8A7FF62AC55D980038CB42 /* Build configuration list for PBXNativeTarget "Tracker" */; buildPhases = ( + 780E122B20FF45050EAC7691 /* [CP] Check Pods Manifest.lock */, 1A0775FC2B0A30EA00155ED6 /* SwiftGenPlugin */, 1A8A7FDE2AC55D960038CB42 /* Sources */, 1A8A7FDF2AC55D960038CB42 /* Frameworks */, @@ -461,6 +511,7 @@ isa = PBXNativeTarget; buildConfigurationList = 1AE96E262B3424E40061F10D /* Build configuration list for PBXNativeTarget "TrackerTests" */; buildPhases = ( + AA8DEACE0F216FA3CFB687F3 /* [CP] Check Pods Manifest.lock */, 1AE96E1C2B3424E40061F10D /* Sources */, 1AE96E1D2B3424E40061F10D /* Frameworks */, 1AE96E1E2B3424E40061F10D /* Resources */, @@ -562,6 +613,50 @@ shellPath = /bin/sh; shellScript = "echo \"Build Phase - Begin SwiftGen\"\n./runSwiftGenPlugin.sh\necho \"Build Phase - End SwiftGen\"\n"; }; + 780E122B20FF45050EAC7691 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Tracker-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + AA8DEACE0F216FA3CFB687F3 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-TrackerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -576,6 +671,7 @@ 1A8A576C2ACD5F4D00C060FA /* CreateTrackerTableViewCell.swift in Sources */, 1A598BB82AC6FAC5009B166D /* TrackersViewController.swift in Sources */, 1A8A7FEA2AC55D970038CB42 /* StatisticViewController.swift in Sources */, + 1AC642742B38442E0072E081 /* EventAnalytics.swift in Sources */, 1A66BB192B25D16500838F5F /* PinnTrackerModel.swift in Sources */, 1A598BB62AC6F352009B166D /* TabBarController.swift in Sources */, 1ABB3D2C2AC9AAB40037B601 /* HeaderReusableView.swift in Sources */, @@ -593,6 +689,7 @@ 1A8ED5122AEFFADF00BA4EE4 /* OnboardingModel.swift in Sources */, 1AE27C2D2B0604A800C4A925 /* Errors.swift in Sources */, 1AE989302ACAF14B00392315 /* TrackerCategoryModel.swift in Sources */, + 1AC642722B383AD50072E081 /* AnalyticsService.swift in Sources */, 1A3ED44C2ACDDE4A0006EBBB /* ScheduleViewController.swift in Sources */, 1A2980BD2AC864CB007FE528 /* UIColor.swift in Sources */, 1AAD15652AF14E220080EB72 /* UserDefaultsBacked.swift in Sources */, @@ -609,6 +706,7 @@ 1A8ED50F2AEFEB5A00BA4EE4 /* OnboardingViewController.swift in Sources */, 1A272CE42B27922A00D39A44 /* PinnedCategoryStore.swift in Sources */, 1AD277712ACD4B1800F92219 /* UITextField.swift in Sources */, + 1A0C3C192B388CC3008E5202 /* MetrycEvent.swift in Sources */, 1A46558A2AD3D2B8006DE343 /* FormatDate.swift in Sources */, 1A52EFAF2ADC194C0002BD4B /* Date.swift in Sources */, 1AE9892D2ACAE76E00392315 /* WeekDay.swift in Sources */, @@ -805,6 +903,7 @@ }; 1A8A7FF72AC55D980038CB42 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = A4D50E97DFCE527E61A29054 /* Pods-Tracker.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -833,6 +932,7 @@ }; 1A8A7FF82AC55D980038CB42 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 6682ACA7FBAC750BABF3A93E /* Pods-Tracker.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -861,6 +961,7 @@ }; 1AE96E272B3424E40061F10D /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = B99BCC20A3F3BF18F8D8A525 /* Pods-TrackerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -880,6 +981,7 @@ }; 1AE96E282B3424E40061F10D /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 6E5A6EC6ED52C7BE406D080A /* Pods-TrackerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; diff --git a/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index 317cd05..1d19926 100644 --- a/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -866,8 +866,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "579" - endingLineNumber = "579" + startingLineNumber = "583" + endingLineNumber = "583" landmarkName = "collectionView(_:viewForSupplementaryElementOfKind:at:)" landmarkType = "7"> @@ -1064,8 +1064,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "577" - endingLineNumber = "577" + startingLineNumber = "581" + endingLineNumber = "581" landmarkName = "collectionView(_:viewForSupplementaryElementOfKind:at:)" landmarkType = "7"> @@ -1172,8 +1172,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "562" - endingLineNumber = "562" + startingLineNumber = "566" + endingLineNumber = "566" landmarkName = "collectionView(_:layout:insetForSectionAt:)" landmarkType = "7"> @@ -1188,8 +1188,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "555" - endingLineNumber = "555" + startingLineNumber = "559" + endingLineNumber = "559" landmarkName = "collectionView(_:layout:sizeForItemAt:)" landmarkType = "7"> @@ -1204,8 +1204,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "576" - endingLineNumber = "576" + startingLineNumber = "580" + endingLineNumber = "580" landmarkName = "collectionView(_:viewForSupplementaryElementOfKind:at:)" landmarkType = "7"> @@ -1312,8 +1312,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "290" - endingLineNumber = "290" + startingLineNumber = "292" + endingLineNumber = "292" landmarkName = "registerCellAndHeader(collectionView:)" landmarkType = "7"> @@ -1328,8 +1328,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "571" - endingLineNumber = "571" + startingLineNumber = "575" + endingLineNumber = "575" landmarkName = "collectionView(_:layout:referenceSizeForHeaderInSection:)" landmarkType = "7"> @@ -1455,8 +1455,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "637" - endingLineNumber = "637" + startingLineNumber = "642" + endingLineNumber = "642" landmarkName = "eventSelectionViewControllerDidCancel(_:)" landmarkType = "7"> @@ -1519,8 +1519,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "597" - endingLineNumber = "597" + startingLineNumber = "601" + endingLineNumber = "601" landmarkName = "updateSearchResults(for:)" landmarkType = "7"> @@ -1803,8 +1803,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "604" - endingLineNumber = "604" + startingLineNumber = "608" + endingLineNumber = "608" landmarkName = "updateSearchResults(for:)" landmarkType = "7"> @@ -1851,8 +1851,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "558" - endingLineNumber = "558" + startingLineNumber = "562" + endingLineNumber = "562" landmarkName = "collectionView(_:layout:sizeForItemAt:)" landmarkType = "7"> @@ -2169,8 +2169,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "567" - endingLineNumber = "567" + startingLineNumber = "568" + endingLineNumber = "568" landmarkName = "collectionView(_:viewForSupplementaryElementOfKind:at:)" landmarkType = "7"> @@ -2876,8 +2876,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "605" - endingLineNumber = "605" + startingLineNumber = "609" + endingLineNumber = "609" landmarkName = "updateSearchResults(for:)" landmarkType = "7"> @@ -2924,8 +2924,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "424" - endingLineNumber = "424" + startingLineNumber = "425" + endingLineNumber = "425" landmarkName = "tableView(_:cellForRowAt:)" landmarkType = "7"> @@ -3847,8 +3847,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "508" - endingLineNumber = "508" + startingLineNumber = "512" + endingLineNumber = "512" landmarkName = "collectionView(_:cellForItemAt:)" landmarkType = "7"> @@ -3863,8 +3863,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "534" - endingLineNumber = "534" + startingLineNumber = "538" + endingLineNumber = "538" landmarkName = "collectionView(_:contextMenuConfigurationForItemsAt:point:)" landmarkType = "7"> @@ -3879,8 +3879,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "523" - endingLineNumber = "523" + startingLineNumber = "527" + endingLineNumber = "527" landmarkName = "collectionView(_:contextMenuConfigurationForItemsAt:point:)" landmarkType = "7"> @@ -4460,8 +4460,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "633" - endingLineNumber = "633" + startingLineNumber = "638" + endingLineNumber = "638" landmarkName = "eventSelectionViewController(vc:nameCategories:tracker:)" landmarkType = "7"> @@ -4730,8 +4730,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "190" - endingLineNumber = "190" + startingLineNumber = "191" + endingLineNumber = "191" landmarkName = "bind()" landmarkType = "7"> @@ -4886,8 +4886,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "199" - endingLineNumber = "199" + startingLineNumber = "200" + endingLineNumber = "200" landmarkName = "bind()" landmarkType = "7"> @@ -5312,8 +5312,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "602" - endingLineNumber = "602" + startingLineNumber = "603" + endingLineNumber = "603" landmarkName = "editTracker(vc:editTracker:nameCategory:indexPath:)" landmarkType = "7"> @@ -5328,8 +5328,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "545" - endingLineNumber = "545" + startingLineNumber = "546" + endingLineNumber = "546" landmarkName = "collectionView(_:layout:sizeForItemAt:)" landmarkType = "7"> @@ -5344,8 +5344,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "558" - endingLineNumber = "558" + startingLineNumber = "559" + endingLineNumber = "559" landmarkName = "collectionView(_:layout:referenceSizeForHeaderInSection:)" landmarkType = "7"> @@ -5360,8 +5360,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "569" - endingLineNumber = "569" + startingLineNumber = "570" + endingLineNumber = "570" landmarkName = "collectionView(_:viewForSupplementaryElementOfKind:at:)" landmarkType = "7"> @@ -5408,8 +5408,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "572" - endingLineNumber = "572" + startingLineNumber = "573" + endingLineNumber = "573" landmarkName = "collectionView(_:viewForSupplementaryElementOfKind:at:)" landmarkType = "7"> @@ -5456,8 +5456,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "517" - endingLineNumber = "517" + startingLineNumber = "518" + endingLineNumber = "518" landmarkName = "collectionView(_:didSelectItemAt:)" landmarkType = "7"> @@ -5472,8 +5472,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "524" - endingLineNumber = "524" + startingLineNumber = "525" + endingLineNumber = "525" landmarkName = "collectionView(_:didSelectItemAt:)" landmarkType = "7"> @@ -5488,8 +5488,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "492" - endingLineNumber = "492" + startingLineNumber = "493" + endingLineNumber = "493" landmarkName = "collectionView(_:cellForItemAt:)" landmarkType = "7"> @@ -5520,8 +5520,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "606" - endingLineNumber = "606" + startingLineNumber = "607" + endingLineNumber = "607" landmarkName = "editTracker(vc:editTracker:nameCategory:indexPath:)" landmarkType = "7"> @@ -5583,8 +5583,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "532" - endingLineNumber = "532" + startingLineNumber = "533" + endingLineNumber = "533" landmarkName = "collectionView(_:didDeselectItemAt:)" landmarkType = "7"> @@ -5599,8 +5599,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "537" - endingLineNumber = "537" + startingLineNumber = "538" + endingLineNumber = "538" landmarkName = "collectionView(_:didDeselectItemAt:)" landmarkType = "7"> @@ -5695,8 +5695,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "645" - endingLineNumber = "645" + startingLineNumber = "650" + endingLineNumber = "650" landmarkName = "trackerViewController(vc:nameCategory:tracker:)" landmarkType = "7"> @@ -6505,8 +6505,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "212" - endingLineNumber = "212" + startingLineNumber = "213" + endingLineNumber = "213" landmarkName = "bind()" landmarkType = "7"> @@ -6521,8 +6521,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "210" - endingLineNumber = "210" + startingLineNumber = "211" + endingLineNumber = "211" landmarkName = "bind()" landmarkType = "7"> @@ -6708,8 +6708,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "215" - endingLineNumber = "215" + startingLineNumber = "216" + endingLineNumber = "216" landmarkName = "bind()" landmarkType = "7"> @@ -6724,8 +6724,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "213" - endingLineNumber = "213" + startingLineNumber = "214" + endingLineNumber = "214" landmarkName = "bind()" landmarkType = "7"> @@ -6788,8 +6788,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "625" - endingLineNumber = "625" + startingLineNumber = "629" + endingLineNumber = "629" landmarkName = "didTrackerCompleted(_:)" landmarkType = "7"> @@ -6931,8 +6931,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "257" - endingLineNumber = "257" + startingLineNumber = "259" + endingLineNumber = "259" landmarkName = "filterState(state:)" landmarkType = "7"> @@ -6947,8 +6947,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "260" - endingLineNumber = "260" + startingLineNumber = "262" + endingLineNumber = "262" landmarkName = "filterState(state:)" landmarkType = "7"> @@ -6963,8 +6963,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "266" - endingLineNumber = "266" + startingLineNumber = "268" + endingLineNumber = "268" landmarkName = "filterState(state:)" landmarkType = "7"> @@ -6979,8 +6979,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "263" - endingLineNumber = "263" + startingLineNumber = "265" + endingLineNumber = "265" landmarkName = "filterState(state:)" landmarkType = "7"> @@ -6995,8 +6995,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "172" - endingLineNumber = "172" + startingLineNumber = "173" + endingLineNumber = "173" landmarkName = "viewDidLoad()" landmarkType = "7"> @@ -7011,8 +7011,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "175" - endingLineNumber = "175" + startingLineNumber = "176" + endingLineNumber = "176" landmarkName = "viewDidLoad()" landmarkType = "7"> @@ -7027,8 +7027,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "185" - endingLineNumber = "185" + startingLineNumber = "186" + endingLineNumber = "186" landmarkName = "bind()" landmarkType = "7"> diff --git a/Tracker.xcworkspace/contents.xcworkspacedata b/Tracker.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..0f1322d --- /dev/null +++ b/Tracker.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/Tracker.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Tracker.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/Tracker.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Tracker.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Tracker.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..ddf0806 --- /dev/null +++ b/Tracker.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,23 @@ +{ + "pins" : [ + { + "identity" : "swift-snapshot-testing", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-snapshot-testing", + "state" : { + "revision" : "59b663f68e69f27a87b45de48cb63264b8194605", + "version" : "1.15.1" + } + }, + { + "identity" : "swift-syntax", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-syntax.git", + "state" : { + "revision" : "6ad4ea24b01559dde0773e3d091f1b9e36175036", + "version" : "509.0.2" + } + } + ], + "version" : 2 +} diff --git a/Tracker/AnalyticsService/AnalyticsService.swift b/Tracker/AnalyticsService/AnalyticsService.swift new file mode 100644 index 0000000..183686e --- /dev/null +++ b/Tracker/AnalyticsService/AnalyticsService.swift @@ -0,0 +1,77 @@ +// +// AnalyticsService.swift +// Tracker +// +// Created by Григорий Машук on 24.12.23. +// + +import Foundation +import YandexMobileMetrica + +protocol AnalyticsServiceProtocol { + func open() + func close() + func addTracker() + func track() + func edit() + func delete() + func filter() +} + +struct AnalyticsService { + static let shared = AnalyticsService() + private let handler: (MetricEvent) -> Void = { field in + YMMYandexMetrica.reportEvent(field.event.rawValue, parameters: field.params, onFailure: { error in + print("REPORT ERROR: %@", error.localizedDescription) + }) + } +} + +extension AnalyticsService: AnalyticsServiceProtocol { + func open() { + let params = [EventFields.screen.rawValue: Screen.Main.rawValue] + let metricEvent = MetricEvent(event: .open, params: params) + handler(metricEvent) + } + + func close() { + let params = [EventFields.screen.rawValue: Screen.Main.rawValue] + let metricEvent = MetricEvent(event: .close, params: params) + handler(metricEvent) + } + + func addTracker() { + let params = [EventFields.screen.rawValue: Screen.Main.rawValue, + EventFields.item.rawValue: ItemClick.add_track.rawValue] + let metricEvent = MetricEvent(event: .click, params: params) + handler(metricEvent) + } + + func track() { + let params = [EventFields.screen.rawValue: Screen.Main.rawValue, + EventFields.item.rawValue: ItemClick.track.rawValue] + let metricEvent = MetricEvent(event: .click, params: params) + handler(metricEvent) + } + + func edit() { + let params = [EventFields.screen.rawValue: Screen.Main.rawValue, + EventFields.item.rawValue: ItemClick.edit.rawValue] + let metricEvent = MetricEvent(event: .click, params: params) + handler(metricEvent) + } + + func delete() { + let params = [EventFields.screen.rawValue: Screen.Main.rawValue, + EventFields.item.rawValue: ItemClick.delete.rawValue] + let metricEvent = MetricEvent(event: .click, params: params) + handler(metricEvent) + } + + func filter() { + let params = [EventFields.screen.rawValue: Screen.Main.rawValue, + EventFields.item.rawValue: ItemClick.filter.rawValue] + let metricEvent = MetricEvent(event: .click, params: params) + handler(metricEvent) + } +} diff --git a/Tracker/Application/AppDelegate.swift b/Tracker/Application/AppDelegate.swift index f05a096..cd30e59 100644 --- a/Tracker/Application/AppDelegate.swift +++ b/Tracker/Application/AppDelegate.swift @@ -7,6 +7,10 @@ import UIKit import CoreData +import YandexMobileMetrica + +//please insert your key +private let apiKeyYMM = "" @main class AppDelegate: UIResponder, UIApplicationDelegate { @@ -16,6 +20,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { DaysValueTransformer.register() + guard let configuration = YMMYandexMetricaConfiguration(apiKey: apiKeyYMM) else { return true } + YMMYandexMetrica.activate(with: configuration) return true } diff --git a/Tracker/Models/Enum/EventAnalytics.swift b/Tracker/Models/Enum/EventAnalytics.swift new file mode 100644 index 0000000..18bddc7 --- /dev/null +++ b/Tracker/Models/Enum/EventAnalytics.swift @@ -0,0 +1,32 @@ +// +// EventFields.swift +// Tracker +// +// Created by Григорий Машук on 24.12.23. +// + +import Foundation +enum EventFields: String { + case event + case screen + case item +} + +enum Event: String { + case open + case close + case click +} + +enum Screen: String { + case Main +} + +enum ItemClick: String { + case add_track + case track + case filter + case edit + case delete +} + diff --git a/Tracker/Models/MetrycEvent.swift b/Tracker/Models/MetrycEvent.swift new file mode 100644 index 0000000..f64e52a --- /dev/null +++ b/Tracker/Models/MetrycEvent.swift @@ -0,0 +1,14 @@ +// +// MetrycEvent.swift +// Tracker +// +// Created by Григорий Машук on 24.12.23. +// + +import Foundation + +struct MetricEvent { + let event: Event + let params: [String: Any] +} + diff --git a/Tracker/TabBarController/TabBarController.swift b/Tracker/TabBarController/TabBarController.swift index d2dab7f..655c3de 100644 --- a/Tracker/TabBarController/TabBarController.swift +++ b/Tracker/TabBarController/TabBarController.swift @@ -55,6 +55,10 @@ final class TabBarController: UITabBarController { } } } + + deinit { + AnalyticsService.shared.close() + } } //MARK: - Setup diff --git a/Tracker/TabBarController/StatisticViews/StatisticViewController.swift b/Tracker/TabBarController/TrackersViews/StatisticViews/StatisticViewController.swift similarity index 100% rename from Tracker/TabBarController/StatisticViews/StatisticViewController.swift rename to Tracker/TabBarController/TrackersViews/StatisticViews/StatisticViewController.swift diff --git a/Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift b/Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift index 4f42949..660e919 100644 --- a/Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift +++ b/Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift @@ -299,6 +299,7 @@ extension CreateTrackerViewController { tracker: tracker) createTrackerDelegate.trackerViewControllerDidCancel(self) } + AnalyticsService.shared.addTracker() } @objc diff --git a/Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift b/Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift index 09623c1..0ad9cc5 100644 --- a/Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift +++ b/Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift @@ -161,6 +161,7 @@ final class TrackersViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() + AnalyticsService.shared.open() view.backgroundColor = colors.viewBackground setupUIElement() viewModel = TrackerViewModel() @@ -222,6 +223,7 @@ private extension TrackersViewController { //MARK: - обработка событий @objc func actionButtonFilter() { + AnalyticsService.shared.filter() let filtersVC = FiltersViewController(viewModel: FiltersViewModel(), delegate: self) filtersVC.modalPresentationStyle = .formSheet present(filtersVC, animated: true) @@ -344,6 +346,7 @@ private extension TrackersViewController { nameCategory: category.nameCategory, indexPath: indexPath) } + AnalyticsService.shared.edit() } } @@ -356,6 +359,7 @@ private extension TrackersViewController { let deleteAction = UIAlertAction(title: ConstantsTrackerVc.textDelete, style: .destructive) { _ in self.handler.resultTypeHandler(viewModel.deleteTracker(tracker.id)) {} + AnalyticsService.shared.delete() alert.dismiss(animated: true) } let cancelAction = UIAlertAction(title: ConstantsTrackerVc.textCancel, @@ -623,6 +627,7 @@ extension TrackersViewController: TrackersCollectionViewCellDelegate { let tracker = visibleCategories[indexPath.section].arrayTrackers[indexPath.row] viewModel.setIndexPath(indexPath) handler.resultTypeHandler(viewModel.updateCompletedTrackers(tracker: tracker, date: datePicker.date)) {} + AnalyticsService.shared.track() } } @@ -650,6 +655,7 @@ extension TrackersViewController: UpdateTrackerViewControllerDelegate { } } +//MARK: - FiltersViewControllerDelegate extension TrackersViewController: FiltersViewControllerDelegate { func filter(vc: FiltersViewController, filterState: FiltersState) { guard let viewModel else { return } From 3d40d248c5858b392742cafdf93204bbc29e5e28 Mon Sep 17 00:00:00 2001 From: Grifon Date: Tue, 26 Dec 2023 11:40:44 +0300 Subject: [PATCH 09/24] add screen Statistics --- Tracker.xcodeproj/project.pbxproj | 54 +++--- .../xcdebugger/Breakpoints_v2.xcbkptlist | 88 ++++----- .../blueGradient.colorset/Contents.json | 41 +++++ .../greenGradient.colorset/Contents.json | 38 ++++ .../Colors/redGradient.colorset/Contents.json | 38 ++++ Tracker/Domain/Store/TrackerRecordStore.swift | 12 +- Tracker/Domain/Store/TrackerStore.swift | 2 + Tracker/Extension/CGPoint.swift | 13 ++ Tracker/{Extension + => Extension}/Date.swift | 0 .../{Extension + => Extension}/String.swift | 0 .../StringProtocol.swift | 0 .../{Extension + => Extension}/UIColor.swift | 6 +- .../UITextField.swift | 0 Tracker/Models/ConfigModels.swift | 28 +++ Tracker/Models/CustomCellModel.swift | 13 -- Tracker/Models/Enum/UserDefaultKeys.swift | 14 ++ Tracker/Models/OnboardingModel.swift | 13 -- Tracker/Models/UpdateTrackerModel.swift | 13 -- .../Resources/en.lproj/Localizable.strings | 5 + .../Resources/ru.lproj/Localizable.strings | 5 + .../TabBarController/TabBarController.swift | 6 +- .../TrackersViews/Cell/StatisticView.swift | 77 ++++++++ .../StatisticViewController.swift | 20 -- .../StatisticsViewController.swift | 174 ++++++++++++++++++ .../CreateTrackerViewController.swift | 4 +- ...t => SelectCategoriesViewController.swift} | 20 +- Tracker/ViewModel/CategoryViewModel.swift | 2 +- Tracker/ViewModel/EditTrackerViewModel.swift | 2 +- Tracker/ViewModel/FiltersViewModel.swift | 3 +- Tracker/ViewModel/StatisticViewModel.swift | 47 +++++ Tracker/ViewModel/TrackerViewModel.swift | 8 +- 31 files changed, 592 insertions(+), 154 deletions(-) create mode 100644 Tracker/Assets.xcassets/Colors/blueGradient.colorset/Contents.json create mode 100644 Tracker/Assets.xcassets/Colors/greenGradient.colorset/Contents.json create mode 100644 Tracker/Assets.xcassets/Colors/redGradient.colorset/Contents.json create mode 100644 Tracker/Extension/CGPoint.swift rename Tracker/{Extension + => Extension}/Date.swift (100%) rename Tracker/{Extension + => Extension}/String.swift (100%) rename Tracker/{Extension + => Extension}/StringProtocol.swift (100%) rename Tracker/{Extension + => Extension}/UIColor.swift (88%) rename Tracker/{Extension + => Extension}/UITextField.swift (100%) create mode 100644 Tracker/Models/ConfigModels.swift delete mode 100644 Tracker/Models/CustomCellModel.swift create mode 100644 Tracker/Models/Enum/UserDefaultKeys.swift delete mode 100644 Tracker/Models/OnboardingModel.swift delete mode 100644 Tracker/Models/UpdateTrackerModel.swift create mode 100644 Tracker/TabBarController/TrackersViews/Cell/StatisticView.swift delete mode 100644 Tracker/TabBarController/TrackersViews/StatisticViews/StatisticViewController.swift create mode 100644 Tracker/TabBarController/TrackersViews/StatisticViews/StatisticsViewController.swift rename Tracker/TabBarController/TrackersViews/ViewControllers/{CreateCategoriesViewController.swift => SelectCategoriesViewController.swift} (95%) create mode 100644 Tracker/ViewModel/StatisticViewModel.swift diff --git a/Tracker.xcodeproj/project.pbxproj b/Tracker.xcodeproj/project.pbxproj index 130995a..ad917f8 100644 --- a/Tracker.xcodeproj/project.pbxproj +++ b/Tracker.xcodeproj/project.pbxproj @@ -10,7 +10,6 @@ 1A0077F82B2DD6DF000EAE69 /* CategoryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A0077F72B2DD6DF000EAE69 /* CategoryViewModel.swift */; }; 1A0C3C192B388CC3008E5202 /* MetrycEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A0C3C182B388CC3008E5202 /* MetrycEvent.swift */; }; 1A14106A2ADD5185001D6DE6 /* ColorCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A1410692ADD5185001D6DE6 /* ColorCollectionViewCell.swift */; }; - 1A19A7462B1672A200DD415B /* UpdateTrackerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A19A7452B1672A200DD415B /* UpdateTrackerModel.swift */; }; 1A1E94B52AD952EF00FE9A0C /* CustomTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A1E94B42AD952EF00FE9A0C /* CustomTableViewCell.swift */; }; 1A21DD952ADE5A6A00583A01 /* DaysValueTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A21DD942ADE5A6A00583A01 /* DaysValueTransformer.swift */; }; 1A272CE42B27922A00D39A44 /* PinnedCategoryStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A272CE32B27922A00D39A44 /* PinnedCategoryStore.swift */; }; @@ -36,30 +35,32 @@ 1A6724132B2F108000719A9D /* FiltersState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A6724122B2F108000719A9D /* FiltersState.swift */; }; 1A704D3F2B2B76C60035C94D /* EmojiCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A704D3E2B2B76C60035C94D /* EmojiCollectionViewCell.swift */; }; 1A760A9D2B29EDC900B0C1CC /* EditTrackerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A760A9C2B29EDC900B0C1CC /* EditTrackerViewModel.swift */; }; + 1A79C42D2B39C3E4009CE43A /* StatisticViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A79C42C2B39C3E4009CE43A /* StatisticViewModel.swift */; }; + 1A79C42F2B39FBCC009CE43A /* UserDefaultKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A79C42E2B39FBCC009CE43A /* UserDefaultKeys.swift */; }; + 1A80951A2B3964A000591911 /* StatisticView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A8095192B3964A000591911 /* StatisticView.swift */; }; 1A83156E2B2C786000097DE7 /* HandlerResultType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A83156D2B2C786000097DE7 /* HandlerResultType.swift */; }; 1A84F3792AC87F4E001DD42F /* GeometricParams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A84F3782AC87F4E001DD42F /* GeometricParams.swift */; }; 1A8A576C2ACD5F4D00C060FA /* CreateTrackerTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A8A576B2ACD5F4D00C060FA /* CreateTrackerTableViewCell.swift */; }; 1A8A576F2ACD65C400C060FA /* ChoiceParametrs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A8A576E2ACD65C400C060FA /* ChoiceParametrs.swift */; }; 1A8A7FE62AC55D970038CB42 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A8A7FE52AC55D970038CB42 /* AppDelegate.swift */; }; 1A8A7FE82AC55D970038CB42 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A8A7FE72AC55D970038CB42 /* SceneDelegate.swift */; }; - 1A8A7FEA2AC55D970038CB42 /* StatisticViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A8A7FE92AC55D970038CB42 /* StatisticViewController.swift */; }; + 1A8A7FEA2AC55D970038CB42 /* StatisticsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A8A7FE92AC55D970038CB42 /* StatisticsViewController.swift */; }; 1A8A7FEF2AC55D980038CB42 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1A8A7FEE2AC55D980038CB42 /* Assets.xcassets */; }; 1A8A7FF22AC55D980038CB42 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1A8A7FF02AC55D980038CB42 /* LaunchScreen.storyboard */; }; 1A8ED50D2AEFEA1900BA4EE4 /* OnboardingPageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A8ED50C2AEFEA1900BA4EE4 /* OnboardingPageViewController.swift */; }; 1A8ED50F2AEFEB5A00BA4EE4 /* OnboardingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A8ED50E2AEFEB5A00BA4EE4 /* OnboardingViewController.swift */; }; - 1A8ED5122AEFFADF00BA4EE4 /* OnboardingModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A8ED5112AEFFADF00BA4EE4 /* OnboardingModel.swift */; }; 1A959AF42AE24C44001C0EAE /* UIColorMarshalling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A959AF32AE24C44001C0EAE /* UIColorMarshalling.swift */; }; 1A9AC5AF2AF373F000A9213D /* Observable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A9AC5AE2AF373F000A9213D /* Observable.swift */; }; 1A9AC5B42AF37AED00A9213D /* TrackerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A9AC5B32AF37AED00A9213D /* TrackerViewModel.swift */; }; 1A9E5C962AD2FF6100010C30 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A9E5C952AD2FF6100010C30 /* String.swift */; }; - 1AA5B2372AD91D1800CB38A2 /* CreateCategoriesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AA5B2362AD91D1800CB38A2 /* CreateCategoriesViewController.swift */; }; + 1AA5B2372AD91D1800CB38A2 /* SelectCategoriesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AA5B2362AD91D1800CB38A2 /* SelectCategoriesViewController.swift */; }; 1AA5B2392AD9424C00CB38A2 /* NewCategoriViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AA5B2382AD9424C00CB38A2 /* NewCategoriViewController.swift */; }; 1AA7BFC92B00E82E00C678FF /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1AA7BFCB2B00E82E00C678FF /* Localizable.strings */; }; 1AAC05C32ACC595800AA4B6B /* CreateTrackerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AAC05C22ACC595800AA4B6B /* CreateTrackerViewController.swift */; }; 1AAD15652AF14E220080EB72 /* UserDefaultsBacked.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AAD15642AF14E220080EB72 /* UserDefaultsBacked.swift */; }; 1AB0E14F2B036CE8007183D8 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1AB0E1512B036CE8007183D8 /* InfoPlist.strings */; }; 1AB78AA32AC750A900904AB2 /* TrackersCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AB78AA22AC750A900904AB2 /* TrackersCollectionViewCell.swift */; }; - 1ABABBF72AFC10AD007281E0 /* CustomCellModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ABABBF62AFC10AD007281E0 /* CustomCellModel.swift */; }; + 1ABABBF72AFC10AD007281E0 /* ConfigModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ABABBF62AFC10AD007281E0 /* ConfigModels.swift */; }; 1ABB3D2C2AC9AAB40037B601 /* HeaderReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ABB3D2B2AC9AAB40037B601 /* HeaderReusableView.swift */; }; 1AC642722B383AD50072E081 /* AnalyticsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AC642712B383AD50072E081 /* AnalyticsService.swift */; }; 1AC642742B38442E0072E081 /* EventAnalytics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AC642732B38442E0072E081 /* EventAnalytics.swift */; }; @@ -67,6 +68,7 @@ 1ADD3D742ACBDBB1005F639F /* TrackerCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ADD3D732ACBDBB1005F639F /* TrackerCollectionView.swift */; }; 1AE08AB82B2F5C250042CE59 /* FiltersViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AE08AB72B2F5C250042CE59 /* FiltersViewModel.swift */; }; 1AE27C2D2B0604A800C4A925 /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AE27C2C2B0604A800C4A925 /* Errors.swift */; }; + 1AE790232B399A1D00A2E9AB /* CGPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AE790222B399A1D00A2E9AB /* CGPoint.swift */; }; 1AE96E232B3424E40061F10D /* TrackerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AE96E222B3424E40061F10D /* TrackerTests.swift */; }; 1AE96E2B2B34278E0061F10D /* InlineSnapshotTesting in Frameworks */ = {isa = PBXBuildFile; productRef = 1AE96E2A2B34278E0061F10D /* InlineSnapshotTesting */; }; 1AE96E2D2B34278E0061F10D /* SnapshotTesting in Frameworks */ = {isa = PBXBuildFile; productRef = 1AE96E2C2B34278E0061F10D /* SnapshotTesting */; }; @@ -93,7 +95,6 @@ 1A0077F72B2DD6DF000EAE69 /* CategoryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CategoryViewModel.swift; sourceTree = ""; }; 1A0C3C182B388CC3008E5202 /* MetrycEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetrycEvent.swift; sourceTree = ""; }; 1A1410692ADD5185001D6DE6 /* ColorCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorCollectionViewCell.swift; sourceTree = ""; }; - 1A19A7452B1672A200DD415B /* UpdateTrackerModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateTrackerModel.swift; sourceTree = ""; }; 1A1E94B42AD952EF00FE9A0C /* CustomTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTableViewCell.swift; sourceTree = ""; }; 1A21DD942ADE5A6A00583A01 /* DaysValueTransformer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DaysValueTransformer.swift; sourceTree = ""; }; 1A272CE32B27922A00D39A44 /* PinnedCategoryStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinnedCategoryStore.swift; sourceTree = ""; }; @@ -120,6 +121,9 @@ 1A6724122B2F108000719A9D /* FiltersState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FiltersState.swift; sourceTree = ""; }; 1A704D3E2B2B76C60035C94D /* EmojiCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiCollectionViewCell.swift; sourceTree = ""; }; 1A760A9C2B29EDC900B0C1CC /* EditTrackerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditTrackerViewModel.swift; sourceTree = ""; }; + 1A79C42C2B39C3E4009CE43A /* StatisticViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatisticViewModel.swift; sourceTree = ""; }; + 1A79C42E2B39FBCC009CE43A /* UserDefaultKeys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultKeys.swift; sourceTree = ""; }; + 1A8095192B3964A000591911 /* StatisticView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatisticView.swift; sourceTree = ""; }; 1A83156D2B2C786000097DE7 /* HandlerResultType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandlerResultType.swift; sourceTree = ""; }; 1A84F3782AC87F4E001DD42F /* GeometricParams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeometricParams.swift; sourceTree = ""; }; 1A8A576B2ACD5F4D00C060FA /* CreateTrackerTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateTrackerTableViewCell.swift; sourceTree = ""; }; @@ -127,18 +131,17 @@ 1A8A7FE22AC55D970038CB42 /* Tracker.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Tracker.app; sourceTree = BUILT_PRODUCTS_DIR; }; 1A8A7FE52AC55D970038CB42 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 1A8A7FE72AC55D970038CB42 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; - 1A8A7FE92AC55D970038CB42 /* StatisticViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatisticViewController.swift; sourceTree = ""; }; + 1A8A7FE92AC55D970038CB42 /* StatisticsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatisticsViewController.swift; sourceTree = ""; }; 1A8A7FEE2AC55D980038CB42 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 1A8A7FF12AC55D980038CB42 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 1A8A7FF32AC55D980038CB42 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 1A8ED50C2AEFEA1900BA4EE4 /* OnboardingPageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingPageViewController.swift; sourceTree = ""; }; 1A8ED50E2AEFEB5A00BA4EE4 /* OnboardingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingViewController.swift; sourceTree = ""; }; - 1A8ED5112AEFFADF00BA4EE4 /* OnboardingModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingModel.swift; sourceTree = ""; }; 1A959AF32AE24C44001C0EAE /* UIColorMarshalling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIColorMarshalling.swift; sourceTree = ""; }; 1A9AC5AE2AF373F000A9213D /* Observable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Observable.swift; sourceTree = ""; }; 1A9AC5B32AF37AED00A9213D /* TrackerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerViewModel.swift; sourceTree = ""; }; 1A9E5C952AD2FF6100010C30 /* String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = ""; }; - 1AA5B2362AD91D1800CB38A2 /* CreateCategoriesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateCategoriesViewController.swift; sourceTree = ""; }; + 1AA5B2362AD91D1800CB38A2 /* SelectCategoriesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectCategoriesViewController.swift; sourceTree = ""; }; 1AA5B2382AD9424C00CB38A2 /* NewCategoriViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewCategoriViewController.swift; sourceTree = ""; }; 1AA7BFC82B00E77300C678FF /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/LaunchScreen.strings; sourceTree = ""; }; 1AA7BFCA2B00E82E00C678FF /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; @@ -148,7 +151,7 @@ 1AB0E1502B036CE8007183D8 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 1AB0E1522B036CEF007183D8 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/InfoPlist.strings; sourceTree = ""; }; 1AB78AA22AC750A900904AB2 /* TrackersCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackersCollectionViewCell.swift; sourceTree = ""; }; - 1ABABBF62AFC10AD007281E0 /* CustomCellModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomCellModel.swift; sourceTree = ""; }; + 1ABABBF62AFC10AD007281E0 /* ConfigModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigModels.swift; sourceTree = ""; }; 1ABB3D2B2AC9AAB40037B601 /* HeaderReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderReusableView.swift; sourceTree = ""; }; 1AC642712B383AD50072E081 /* AnalyticsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsService.swift; sourceTree = ""; }; 1AC642732B38442E0072E081 /* EventAnalytics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventAnalytics.swift; sourceTree = ""; }; @@ -156,6 +159,7 @@ 1ADD3D732ACBDBB1005F639F /* TrackerCollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerCollectionView.swift; sourceTree = ""; }; 1AE08AB72B2F5C250042CE59 /* FiltersViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FiltersViewModel.swift; sourceTree = ""; }; 1AE27C2C2B0604A800C4A925 /* Errors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = ""; }; + 1AE790222B399A1D00A2E9AB /* CGPoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CGPoint.swift; sourceTree = ""; }; 1AE96E202B3424E40061F10D /* TrackerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TrackerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 1AE96E222B3424E40061F10D /* TrackerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerTests.swift; sourceTree = ""; }; 1AE989282ACAE13800392315 /* TrackerModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerModel.swift; sourceTree = ""; }; @@ -210,7 +214,7 @@ path = Helpers; sourceTree = ""; }; - 1A2980BB2AC86499007FE528 /* Extension + */ = { + 1A2980BB2AC86499007FE528 /* Extension */ = { isa = PBXGroup; children = ( 1A2980BC2AC864CB007FE528 /* UIColor.swift */, @@ -218,8 +222,9 @@ 1A9E5C952AD2FF6100010C30 /* String.swift */, 1A27E6B82AD9871400ED3DF3 /* StringProtocol.swift */, 1A52EFAE2ADC194C0002BD4B /* Date.swift */, + 1AE790222B399A1D00A2E9AB /* CGPoint.swift */, ); - path = "Extension +"; + path = Extension; sourceTree = ""; }; 1A2FC2482AED1E05000156FE /* Domain */ = { @@ -255,7 +260,7 @@ 1A598BB72AC6FAC5009B166D /* TrackersViewController.swift */, 1AAC05C22ACC595800AA4B6B /* CreateTrackerViewController.swift */, 1A3ED44B2ACDDE4A0006EBBB /* ScheduleViewController.swift */, - 1AA5B2362AD91D1800CB38A2 /* CreateCategoriesViewController.swift */, + 1AA5B2362AD91D1800CB38A2 /* SelectCategoriesViewController.swift */, 1A2F9B642AC82FBF00914B60 /* EventSelectionViewController.swift */, 1AA5B2382AD9424C00CB38A2 /* NewCategoriViewController.swift */, 1A6724102B2EBE3700719A9D /* FiltersViewController.swift */, @@ -303,9 +308,7 @@ 1AE989282ACAE13800392315 /* TrackerModel.swift */, 1AE9892F2ACAF14B00392315 /* TrackerCategoryModel.swift */, 1AE989312ACAF20500392315 /* TrackerRecordModel.swift */, - 1A8ED5112AEFFADF00BA4EE4 /* OnboardingModel.swift */, - 1ABABBF62AFC10AD007281E0 /* CustomCellModel.swift */, - 1A19A7452B1672A200DD415B /* UpdateTrackerModel.swift */, + 1ABABBF62AFC10AD007281E0 /* ConfigModels.swift */, 1A66BB182B25D16500838F5F /* PinnTrackerModel.swift */, 1A0C3C182B388CC3008E5202 /* MetrycEvent.swift */, ); @@ -315,7 +318,7 @@ 1A8A576D2ACD603200C060FA /* StatisticViews */ = { isa = PBXGroup; children = ( - 1A8A7FE92AC55D970038CB42 /* StatisticViewController.swift */, + 1A8A7FE92AC55D970038CB42 /* StatisticsViewController.swift */, ); path = StatisticViews; sourceTree = ""; @@ -352,7 +355,7 @@ 1A598BB42AC6F0A9009B166D /* TabBarController */, 1A84F3772AC87F28001DD42F /* Models */, 1A3477CB2AD18D2100EDEAED /* Helpers */, - 1A2980BB2AC86499007FE528 /* Extension + */, + 1A2980BB2AC86499007FE528 /* Extension */, 1AAD15662AF14E2D0080EB72 /* PropertyWrappers */, 1AA7BFC52B00E41A00C678FF /* Resources */, 1AF783BD2ACD9D6C0002BB54 /* Application */, @@ -380,6 +383,7 @@ 1AF05E052B2B011D0099D022 /* ShedulViewModel.swift */, 1A0077F72B2DD6DF000EAE69 /* CategoryViewModel.swift */, 1AE08AB72B2F5C250042CE59 /* FiltersViewModel.swift */, + 1A79C42C2B39C3E4009CE43A /* StatisticViewModel.swift */, ); path = ViewModel; sourceTree = ""; @@ -403,6 +407,7 @@ 1A1E94B42AD952EF00FE9A0C /* CustomTableViewCell.swift */, 1A1410692ADD5185001D6DE6 /* ColorCollectionViewCell.swift */, 1A704D3E2B2B76C60035C94D /* EmojiCollectionViewCell.swift */, + 1A8095192B3964A000591911 /* StatisticView.swift */, ); path = Cell; sourceTree = ""; @@ -450,6 +455,7 @@ 1AE27C2C2B0604A800C4A925 /* Errors.swift */, 1A6724122B2F108000719A9D /* FiltersState.swift */, 1AC642732B38442E0072E081 /* EventAnalytics.swift */, + 1A79C42E2B39FBCC009CE43A /* UserDefaultKeys.swift */, ); path = Enum; sourceTree = ""; @@ -670,7 +676,7 @@ 1A1E94B52AD952EF00FE9A0C /* CustomTableViewCell.swift in Sources */, 1A8A576C2ACD5F4D00C060FA /* CreateTrackerTableViewCell.swift in Sources */, 1A598BB82AC6FAC5009B166D /* TrackersViewController.swift in Sources */, - 1A8A7FEA2AC55D970038CB42 /* StatisticViewController.swift in Sources */, + 1A8A7FEA2AC55D970038CB42 /* StatisticsViewController.swift in Sources */, 1AC642742B38442E0072E081 /* EventAnalytics.swift in Sources */, 1A66BB192B25D16500838F5F /* PinnTrackerModel.swift in Sources */, 1A598BB62AC6F352009B166D /* TabBarController.swift in Sources */, @@ -679,14 +685,13 @@ 1AE989322ACAF20500392315 /* TrackerRecordModel.swift in Sources */, 1A45A67A2ADE8EDB0063F08C /* TrackerCategoryStore.swift in Sources */, 1A6724132B2F108000719A9D /* FiltersState.swift in Sources */, - 1ABABBF72AFC10AD007281E0 /* CustomCellModel.swift in Sources */, + 1ABABBF72AFC10AD007281E0 /* ConfigModels.swift in Sources */, 1A32FF912ADC72880065E44C /* Header.swift in Sources */, 1A760A9D2B29EDC900B0C1CC /* EditTrackerViewModel.swift in Sources */, - 1A19A7462B1672A200DD415B /* UpdateTrackerModel.swift in Sources */, + 1A80951A2B3964A000591911 /* StatisticView.swift in Sources */, 1A8A7FE62AC55D970038CB42 /* AppDelegate.swift in Sources */, 1A3B83AA2AD56323003C3897 /* NameEvent.swift in Sources */, 1A45A6782ADE8E9E0063F08C /* TrackerStore.swift in Sources */, - 1A8ED5122AEFFADF00BA4EE4 /* OnboardingModel.swift in Sources */, 1AE27C2D2B0604A800C4A925 /* Errors.swift in Sources */, 1AE989302ACAF14B00392315 /* TrackerCategoryModel.swift in Sources */, 1AC642722B383AD50072E081 /* AnalyticsService.swift in Sources */, @@ -719,8 +724,11 @@ 1AAC05C32ACC595800AA4B6B /* CreateTrackerViewController.swift in Sources */, 1A21DD952ADE5A6A00583A01 /* DaysValueTransformer.swift in Sources */, 1A2F9B652AC82FBF00914B60 /* EventSelectionViewController.swift in Sources */, - 1AA5B2372AD91D1800CB38A2 /* CreateCategoriesViewController.swift in Sources */, + 1A79C42F2B39FBCC009CE43A /* UserDefaultKeys.swift in Sources */, + 1AA5B2372AD91D1800CB38A2 /* SelectCategoriesViewController.swift in Sources */, + 1A79C42D2B39C3E4009CE43A /* StatisticViewModel.swift in Sources */, 1A8A7FE82AC55D970038CB42 /* SceneDelegate.swift in Sources */, + 1AE790232B399A1D00A2E9AB /* CGPoint.swift in Sources */, 1A27E6B92AD9871400ED3DF3 /* StringProtocol.swift in Sources */, 1A704D3F2B2B76C60035C94D /* EmojiCollectionViewCell.swift in Sources */, 1AF05E062B2B011D0099D022 /* ShedulViewModel.swift in Sources */, diff --git a/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index 1d19926..4bf26c4 100644 --- a/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -4021,8 +4021,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "173" - endingLineNumber = "173" + startingLineNumber = "174" + endingLineNumber = "174" landmarkName = "deleteTracker(_:)" landmarkType = "7"> @@ -4130,8 +4130,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "178" - endingLineNumber = "178" + startingLineNumber = "179" + endingLineNumber = "179" landmarkName = "deleteTracker(_:)" landmarkType = "7"> @@ -4634,8 +4634,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "130" - endingLineNumber = "130" + startingLineNumber = "131" + endingLineNumber = "131" landmarkName = "addNewTracker(_:nameCategory:)" landmarkType = "7"> @@ -4666,8 +4666,8 @@ filePath = "Tracker/Domain/Store/TrackerRecordStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "125" - endingLineNumber = "125" + startingLineNumber = "126" + endingLineNumber = "126" landmarkName = "updateTrackerRecord(_:)" landmarkType = "7"> @@ -4682,8 +4682,8 @@ filePath = "Tracker/Domain/Store/TrackerRecordStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "80" - endingLineNumber = "80" + startingLineNumber = "81" + endingLineNumber = "81" landmarkName = "addNewTrackerRecord(_:)" landmarkType = "7"> @@ -4918,8 +4918,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "193" - endingLineNumber = "193" + startingLineNumber = "195" + endingLineNumber = "195" landmarkName = "addPinnedCategory(_:pinnedCategory:)" landmarkType = "7"> @@ -5615,8 +5615,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "107" - endingLineNumber = "107" + startingLineNumber = "108" + endingLineNumber = "108" landmarkName = "searchTracker(id:)" landmarkType = "7"> @@ -5663,8 +5663,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "105" - endingLineNumber = "105" + startingLineNumber = "106" + endingLineNumber = "106" landmarkName = "searchTracker(id:)" landmarkType = "7"> @@ -5679,8 +5679,8 @@ filePath = "Tracker/ViewModel/TrackerViewModel.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "373" - endingLineNumber = "373" + startingLineNumber = "371" + endingLineNumber = "371" landmarkName = "storeCategory(trackerCategoryStore:)" landmarkType = "7"> @@ -5773,8 +5773,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "217" - endingLineNumber = "217" + startingLineNumber = "219" + endingLineNumber = "219" landmarkName = "controllerDidChangeContent(_:)" landmarkType = "7"> @@ -6079,8 +6079,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "156" - endingLineNumber = "156" + startingLineNumber = "157" + endingLineNumber = "157" landmarkName = "updateTracker(tracker:nameCategory:)" landmarkType = "7"> @@ -6157,8 +6157,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "144" - endingLineNumber = "144" + startingLineNumber = "145" + endingLineNumber = "145" landmarkName = "updateTracker(tracker:nameCategory:)" landmarkType = "7"> @@ -6268,8 +6268,8 @@ filePath = "Tracker/ViewModel/TrackerViewModel.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "256" - endingLineNumber = "256" + startingLineNumber = "254" + endingLineNumber = "254" landmarkName = "updateTracker(tracker:nameCategory:)" landmarkType = "7"> @@ -6284,8 +6284,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "140" - endingLineNumber = "140" + startingLineNumber = "141" + endingLineNumber = "141" landmarkName = "updateTracker(tracker:nameCategory:)" landmarkType = "7"> @@ -6537,8 +6537,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "209" - endingLineNumber = "209" + startingLineNumber = "211" + endingLineNumber = "211" landmarkName = "TrackerStore" landmarkType = "21"> @@ -6630,8 +6630,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "207" - endingLineNumber = "207" + startingLineNumber = "209" + endingLineNumber = "209" landmarkName = "TrackerStore" landmarkType = "21"> @@ -6740,8 +6740,8 @@ filePath = "Tracker/ViewModel/TrackerViewModel.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "130" - endingLineNumber = "130" + startingLineNumber = "128" + endingLineNumber = "128" landmarkName = "comparisonWeekDays(date:day:)" landmarkType = "7"> @@ -6756,8 +6756,8 @@ filePath = "Tracker/ViewModel/TrackerViewModel.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "182" - endingLineNumber = "182" + startingLineNumber = "180" + endingLineNumber = "180" landmarkName = "filterListTrackersCompleted(trackerCategory:date:)" landmarkType = "7"> @@ -6772,8 +6772,8 @@ filePath = "Tracker/ViewModel/TrackerViewModel.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "167" - endingLineNumber = "167" + startingLineNumber = "165" + endingLineNumber = "165" landmarkName = "filterListTrackersCompleted(trackerCategory:date:)" landmarkType = "7"> @@ -6883,8 +6883,8 @@ filePath = "Tracker/ViewModel/TrackerViewModel.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "196" - endingLineNumber = "196" + startingLineNumber = "194" + endingLineNumber = "194" landmarkName = "filterListTrackersNotCompleted(trackerCategory:date:)" landmarkType = "7"> @@ -7059,8 +7059,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "32" - endingLineNumber = "32" + startingLineNumber = "33" + endingLineNumber = "33" landmarkName = "fetchedTrackerResultController" landmarkType = "24"> @@ -7075,8 +7075,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "34" - endingLineNumber = "34" + startingLineNumber = "35" + endingLineNumber = "35" landmarkName = "fetchedTrackerResultController" landmarkType = "24"> diff --git a/Tracker/Assets.xcassets/Colors/blueGradient.colorset/Contents.json b/Tracker/Assets.xcassets/Colors/blueGradient.colorset/Contents.json new file mode 100644 index 0000000..5945e54 --- /dev/null +++ b/Tracker/Assets.xcassets/Colors/blueGradient.colorset/Contents.json @@ -0,0 +1,41 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.980", + "green" : "0.482", + "red" : "0.000" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.980", + "green" : "0.482", + "red" : "0.000" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "localizable" : true + } +} diff --git a/Tracker/Assets.xcassets/Colors/greenGradient.colorset/Contents.json b/Tracker/Assets.xcassets/Colors/greenGradient.colorset/Contents.json new file mode 100644 index 0000000..840792d --- /dev/null +++ b/Tracker/Assets.xcassets/Colors/greenGradient.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.616", + "green" : "0.902", + "red" : "0.275" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.616", + "green" : "0.902", + "red" : "0.275" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tracker/Assets.xcassets/Colors/redGradient.colorset/Contents.json b/Tracker/Assets.xcassets/Colors/redGradient.colorset/Contents.json new file mode 100644 index 0000000..3bddddd --- /dev/null +++ b/Tracker/Assets.xcassets/Colors/redGradient.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.286", + "green" : "0.298", + "red" : "0.992" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.286", + "green" : "0.298", + "red" : "0.992" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tracker/Domain/Store/TrackerRecordStore.swift b/Tracker/Domain/Store/TrackerRecordStore.swift index 6c9d956..0a466f3 100644 --- a/Tracker/Domain/Store/TrackerRecordStore.swift +++ b/Tracker/Domain/Store/TrackerRecordStore.swift @@ -10,8 +10,9 @@ import CoreData protocol TrackerRecordStoreProtocol { func updateTrackerRecord(_ trackerRecord: TrackerRecord) -> Result - func loadTrackerRecord(id: UUID) -> Result - func treckersRecordsResult() -> Result, Error> + func getCountTrackerRecordForDate(id: UUID) -> Result + func treckersRecordsResult() -> Result, Error> + func getCountTrackerComplet() -> Int? } protocol TrackerRecordStoreDelegate: AnyObject { @@ -132,7 +133,7 @@ extension TrackerRecordStore: TrackerRecordStoreProtocol { } } - func loadTrackerRecord(id: UUID) -> Result { + func getCountTrackerRecordForDate(id: UUID) -> Result { do { if let countTrackers = try getCountTrackersRecord(id: id) { return .success(countTrackers) @@ -142,6 +143,11 @@ extension TrackerRecordStore: TrackerRecordStoreProtocol { } return .failure(StoreErrors.TrackrerRecordStoreError.loadTrackerRecord) } + + func getCountTrackerComplet() -> Int? { + fetchedTrackerRecordResultController.fetchedObjects?.count + } + } //MARK: - NSFetchedResultsControllerDelegate diff --git a/Tracker/Domain/Store/TrackerStore.swift b/Tracker/Domain/Store/TrackerStore.swift index 6641bed..d2c4f7d 100644 --- a/Tracker/Domain/Store/TrackerStore.swift +++ b/Tracker/Domain/Store/TrackerStore.swift @@ -23,6 +23,7 @@ protocol TrackerStoreDelegate: AnyObject { //MARK: - TrackerStore final class TrackerStore: NSObject { weak var delegate: TrackerStoreDelegate? + @UserDefaultsBacked(key: UserDefaultKeys.isTracker.rawValue) private(set) var isTracker: Bool? private let context: NSManagedObjectContext private let colorMarshalling = UIColorMarshalling() @@ -179,6 +180,7 @@ extension TrackerStore: TrackerStoreProtocol { } func getTrackers(_ objects: [TrackerCoreData]) -> Result<[Tracker], Error> { + isTracker = objects.isEmpty do { let trackers = try objects.map({ try tracker(from: $0) }) return .success(trackers.sorted { $0.name < $1.name }) diff --git a/Tracker/Extension/CGPoint.swift b/Tracker/Extension/CGPoint.swift new file mode 100644 index 0000000..494a635 --- /dev/null +++ b/Tracker/Extension/CGPoint.swift @@ -0,0 +1,13 @@ +// +// CGPoint.swift +// Tracker +// +// Created by Григорий Машук on 25.12.23. +// + +import Foundation + +extension CGPoint { + static let centerLeft = CGPoint(x: 0.0, y: 0.5) + static let centerRight = CGPoint(x: 1.0, y: 0.5) +} diff --git a/Tracker/Extension +/Date.swift b/Tracker/Extension/Date.swift similarity index 100% rename from Tracker/Extension +/Date.swift rename to Tracker/Extension/Date.swift diff --git a/Tracker/Extension +/String.swift b/Tracker/Extension/String.swift similarity index 100% rename from Tracker/Extension +/String.swift rename to Tracker/Extension/String.swift diff --git a/Tracker/Extension +/StringProtocol.swift b/Tracker/Extension/StringProtocol.swift similarity index 100% rename from Tracker/Extension +/StringProtocol.swift rename to Tracker/Extension/StringProtocol.swift diff --git a/Tracker/Extension +/UIColor.swift b/Tracker/Extension/UIColor.swift similarity index 88% rename from Tracker/Extension +/UIColor.swift rename to Tracker/Extension/UIColor.swift index 1dd9a63..a468d5c 100644 --- a/Tracker/Extension +/UIColor.swift +++ b/Tracker/Extension/UIColor.swift @@ -15,7 +15,7 @@ extension UIColor { static var whiteDay: UIColor {UIColor(named: "whiteDay") ?? .white} static var redDay: UIColor {UIColor(named: "redDay") ?? .red} static var backgroundDay: UIColor {UIColor(named: "backgroundDay") ?? .gray} - static var lightGray: UIColor {UIColor(named: "lightGray") ?? .gray} + static var liteGray: UIColor {UIColor(named: "liteGray") ?? .gray} static var backgroundNight: UIColor {UIColor(named: "backgroundNight") ?? .gray} static var grayOpacity30: UIColor {UIColor(named: "grayOpacity30") ?? .gray} @@ -37,4 +37,8 @@ extension UIColor { static var colorSelection16: UIColor {UIColor(named: "colorSelection16") ?? .red} static var colorSelection17: UIColor {UIColor(named: "colorSelection17") ?? .red} static var colorSelection18: UIColor {UIColor(named: "colorSelection18") ?? .green} + + static var redGradient: UIColor {UIColor(named: "redGradient") ?? .red} + static var greenGradient: UIColor {UIColor(named: "greenGradient") ?? .green} + static var blueGradient: UIColor {UIColor(named: "blueGradient") ?? .blue} } diff --git a/Tracker/Extension +/UITextField.swift b/Tracker/Extension/UITextField.swift similarity index 100% rename from Tracker/Extension +/UITextField.swift rename to Tracker/Extension/UITextField.swift diff --git a/Tracker/Models/ConfigModels.swift b/Tracker/Models/ConfigModels.swift new file mode 100644 index 0000000..615324e --- /dev/null +++ b/Tracker/Models/ConfigModels.swift @@ -0,0 +1,28 @@ +// +// ConfigModels.swift +// Tracker +// +// Created by Григорий Машук on 8.11.23. +// + +import UIKit + +struct CustomCellModel { + let text: String + let color: UIColor +} + +struct Onboarding { + let imageName: String + let textLable: String +} + +struct UpdateTracker { + let count: Int + let flag: Bool +} + +struct StatisticViewModel { + let item: Int + let secondaryText: String +} diff --git a/Tracker/Models/CustomCellModel.swift b/Tracker/Models/CustomCellModel.swift deleted file mode 100644 index 7d1fe92..0000000 --- a/Tracker/Models/CustomCellModel.swift +++ /dev/null @@ -1,13 +0,0 @@ -// -// CreateCategoryCellModel.swift -// Tracker -// -// Created by Григорий Машук on 8.11.23. -// - -import UIKit - -struct CustomCellModel { - let text: String - let color: UIColor -} diff --git a/Tracker/Models/Enum/UserDefaultKeys.swift b/Tracker/Models/Enum/UserDefaultKeys.swift new file mode 100644 index 0000000..ee5dcbe --- /dev/null +++ b/Tracker/Models/Enum/UserDefaultKeys.swift @@ -0,0 +1,14 @@ +// +// UserDefaultKeys.swift +// Tracker +// +// Created by Григорий Машук on 25.12.23. +// + +import Foundation + +enum UserDefaultKeys: String { + case isTracker + case selectNameCategory + case selectFilter +} diff --git a/Tracker/Models/OnboardingModel.swift b/Tracker/Models/OnboardingModel.swift deleted file mode 100644 index 7edc18e..0000000 --- a/Tracker/Models/OnboardingModel.swift +++ /dev/null @@ -1,13 +0,0 @@ -// -// OnboardingModel.swift -// Tracker -// -// Created by Григорий Машук on 30.10.23. -// - -import Foundation - -struct Onboarding { - let imageName: String - let textLable: String -} diff --git a/Tracker/Models/UpdateTrackerModel.swift b/Tracker/Models/UpdateTrackerModel.swift deleted file mode 100644 index 398a957..0000000 --- a/Tracker/Models/UpdateTrackerModel.swift +++ /dev/null @@ -1,13 +0,0 @@ -// -// UpdateTrackerModel.swift -// Tracker -// -// Created by Григорий Машук on 28.11.23. -// - -import Foundation - -struct UpdateTracker { - let count: Int - let flag: Bool -} diff --git a/Tracker/Resources/en.lproj/Localizable.strings b/Tracker/Resources/en.lproj/Localizable.strings index a949f73..31208ac 100644 --- a/Tracker/Resources/en.lproj/Localizable.strings +++ b/Tracker/Resources/en.lproj/Localizable.strings @@ -22,6 +22,11 @@ "completed" = "Completed"; "notCompleted" = "Not Completed"; +//MARK: - StatisticViewController +"headerStatisticText" = "Statistics"; +"labelStubStatisticsText" = "There is nothing to analyze yet"; +"secondaryStatisticText" = "Trackers completed"; + //MARK: - TrackersViewController "headerText" = "Trackers"; "labelStabText" = "What will we track?"; diff --git a/Tracker/Resources/ru.lproj/Localizable.strings b/Tracker/Resources/ru.lproj/Localizable.strings index 3a6bac1..3d88f24 100644 --- a/Tracker/Resources/ru.lproj/Localizable.strings +++ b/Tracker/Resources/ru.lproj/Localizable.strings @@ -21,6 +21,11 @@ "completed" = "Завершенные"; "notCompleted" = "Не завершенные"; +//MARK: - StatisticViewController +"headerStatisticText" = "Статистика"; +"labelStubStatisticsText" = "Анализировать пока нечего"; +"secondaryStatisticText" = "Трекеров завершено"; + //MARK: - TrackersViewController "headerText" = "Трекеры"; "labelStabText" = "Что будем отслеживать?"; diff --git a/Tracker/TabBarController/TabBarController.swift b/Tracker/TabBarController/TabBarController.swift index 655c3de..40d69c7 100644 --- a/Tracker/TabBarController/TabBarController.swift +++ b/Tracker/TabBarController/TabBarController.swift @@ -25,10 +25,10 @@ final class TabBarController: UITabBarController { super.viewDidLoad() setupTabBar() let trackerViewController = TrackersViewController(viewModel: TrackerViewModel()) - let statisticViewController = StatisticViewController() + let statisticsViewController = StatisticsViewController() let trackerNavigationController = UINavigationController(rootViewController: trackerViewController) - let statisticNavigationController = UINavigationController(rootViewController: statisticViewController) + let statisticsNavigationController = UINavigationController(rootViewController: statisticsViewController) viewControllers = [ generateViewController(vc: trackerNavigationController, @@ -36,7 +36,7 @@ final class TabBarController: UITabBarController { title: ConstantsTabBar.tabBarTitleTrecker, insert: ConstantsTabBar.insertImage), - generateViewController(vc: statisticNavigationController, + generateViewController(vc: statisticsNavigationController, imageName: ConstantsTabBar.tabBarImageStatistic, title: ConstantsTabBar.tabBarTitleStatistic, insert: ConstantsTabBar.insertImage) diff --git a/Tracker/TabBarController/TrackersViews/Cell/StatisticView.swift b/Tracker/TabBarController/TrackersViews/Cell/StatisticView.swift new file mode 100644 index 0000000..eae91b0 --- /dev/null +++ b/Tracker/TabBarController/TrackersViews/Cell/StatisticView.swift @@ -0,0 +1,77 @@ +// +// StatisticTableViewCell.swift +// Tracker +// +// Created by Григорий Машук on 25.12.23. +// + +import UIKit + +final class StatisticsView: UIView { + private struct ConstantsStatistics { + static let fontLabelHeader = UIFont.boldSystemFont(ofSize: 34) + static let lableFont = UIFont.systemFont(ofSize: 12, weight: .medium) + static let cornerRadius = CGFloat(16) + static let borderWidth = CGFloat(1) + } + + private lazy var lableHeader: UILabel = { + let lableHeader = UILabel() + lableHeader.font = ConstantsStatistics.fontLabelHeader + lableHeader.textColor = .blackDay + lableHeader.translatesAutoresizingMaskIntoConstraints = false + lableHeader.backgroundColor = .clear + + return lableHeader + }() + + private lazy var secondaryTextLable: UILabel = { + let secondaryTextLable = UILabel() + secondaryTextLable.font = ConstantsStatistics.lableFont + secondaryTextLable.textColor = .blackDay + lableStackView.translatesAutoresizingMaskIntoConstraints = false + + return secondaryTextLable + }() + + private lazy var lableStackView: UIStackView = { + let lableStackView = UIStackView() + lableStackView.translatesAutoresizingMaskIntoConstraints = false + lableStackView.axis = .vertical + + return lableStackView + }() + + override init(frame: CGRect) { + super.init(frame: frame) + setupSteckView() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +extension StatisticsView { + private func setupSteckView() { + addSubview(lableStackView) + [lableHeader, secondaryTextLable].forEach { + $0.backgroundColor = .clear + $0.translatesAutoresizingMaskIntoConstraints = false + lableStackView.addArrangedSubview($0) + } + + NSLayoutConstraint.activate([ + lableStackView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 12), + lableStackView.centerYAnchor.constraint(equalTo: centerYAnchor) + ]) + } + + func setCount(count: Int) { + lableHeader.text = String(count) + } + + func setSecondaryTextLable(text: String) { + secondaryTextLable.text = text + } +} diff --git a/Tracker/TabBarController/TrackersViews/StatisticViews/StatisticViewController.swift b/Tracker/TabBarController/TrackersViews/StatisticViews/StatisticViewController.swift deleted file mode 100644 index 9a94aa1..0000000 --- a/Tracker/TabBarController/TrackersViews/StatisticViews/StatisticViewController.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// StatisticViewController.swift -// Tracker -// -// Created by Григорий Машук on 28.09.23. -// - -import UIKit - -//MARK: - StatisticViewController -class StatisticViewController: UIViewController { - - override func viewDidLoad() { - super.viewDidLoad() - view.backgroundColor = .white - } - - -} - diff --git a/Tracker/TabBarController/TrackersViews/StatisticViews/StatisticsViewController.swift b/Tracker/TabBarController/TrackersViews/StatisticViews/StatisticsViewController.swift new file mode 100644 index 0000000..8cc09ba --- /dev/null +++ b/Tracker/TabBarController/TrackersViews/StatisticViews/StatisticsViewController.swift @@ -0,0 +1,174 @@ +// +// StatisticViewController.swift +// Tracker +// +// Created by Григорий Машук on 28.09.23. +// + +import UIKit + +//MARK: - StatisticsViewController +class StatisticsViewController: UIViewController { + private struct ConstantsStatisticVC { + static let headerStatisticText = NSLocalizedString("headerStatisticText", comment: "") + static let secondaryStatisticText = NSLocalizedString("secondaryStatisticText", comment: "") + static let labelStubStatisticsText = NSLocalizedString("labelStubStatisticsText", comment: "") + static let imageNothingFound = "NothingFound" + + static let borderRadius = CGFloat(16) + static let borderWidth = CGFloat(2) + static let fontLabelHeader = UIFont.boldSystemFont(ofSize: 34) + static let fontLabelTextStub = UIFont.systemFont(ofSize: 12, weight: .medium) + } + + private var viewModel: StatisticsViewModelProtocol? + + private lazy var labelHeader: UILabel = { + let labelHeader = UILabel() + labelHeader.text = ConstantsStatisticVC.headerStatisticText + labelHeader.font = ConstantsStatisticVC.fontLabelHeader + labelHeader.textColor = .blackDay + labelHeader.translatesAutoresizingMaskIntoConstraints = false + labelHeader.backgroundColor = .clear + + return labelHeader + }() + + private lazy var cardStatysticsView: StatisticsView = { + let cardStatistics = StatisticsView() + cardStatistics.setSecondaryTextLable(text: ConstantsStatisticVC.secondaryStatisticText) + cardStatistics.translatesAutoresizingMaskIntoConstraints = false + cardStatistics.backgroundColor = .clear + + return cardStatistics + }() + + private lazy var imageViewStab: UIImageView = { + let imageViewStab = UIImageView() + imageViewStab.image = UIImage(named: ConstantsStatisticVC.imageNothingFound) + + return imageViewStab + }() + + private lazy var lableTextStab: UILabel = { + let lableTextStab = UILabel() + lableTextStab.text = ConstantsStatisticVC.labelStubStatisticsText + lableTextStab.font = ConstantsStatisticVC.fontLabelTextStub + + return lableTextStab + }() + + override func viewDidLoad() { + super.viewDidLoad() + viewModel = StatisticsViewModel() + showStabView(flag: viewModel?.getIsTracker()) + bind() + cardStatysticsView.setCount(count: viewModel?.getCountTrackerComplet() ?? 0) + view.backgroundColor = .white + setupSubView() + } + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + addBorderGradient(view: cardStatysticsView, + colors: [UIColor.redGradient.cgColor, + UIColor.greenGradient.cgColor, + UIColor.blueGradient.cgColor], + borderWidth: ConstantsStatisticVC.borderWidth, + startPoint: CGPoint.centerLeft, + endPoint: CGPoint.centerRight, + cornerRadius: ConstantsStatisticVC.borderRadius) + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + showStabView(flag: viewModel?.getIsTracker()) + } +} + +extension StatisticsViewController { + func bind() { + guard let viewModel = viewModel as? StatisticsViewModel else { return } + viewModel.$countTrackerComplet.bind { [weak self] count in + guard let self else { return } + cardStatysticsView.setCount(count: count ?? 0) + } + } + + private func addBorderGradient(view: UIView, + colors: [CGColor], + borderWidth: CGFloat, + startPoint: CGPoint, + endPoint: CGPoint, + cornerRadius: CGFloat) + { + view.layer.cornerRadius = cornerRadius + view.clipsToBounds = true + + let gradient = CAGradientLayer() + gradient.frame = view.bounds + gradient.colors = colors + gradient.startPoint = startPoint + gradient.endPoint = endPoint + + let shape = CAShapeLayer() + shape.lineWidth = borderWidth + let path = UIBezierPath(roundedRect: view.bounds, cornerRadius: cornerRadius).cgPath + shape.path = path + shape.strokeColor = UIColor.blackDay.cgColor + shape.fillColor = UIColor.clear.cgColor + gradient.mask = shape + view.layer.addSublayer(gradient) + } + + private func showStabView(flag: Bool?) { + guard let flag else { + imageViewStab.isHidden = false + lableTextStab.isHidden = false + cardStatysticsView.isHidden = true + return } + imageViewStab.isHidden = !flag + lableTextStab.isHidden = !flag + cardStatysticsView.isHidden = flag + } + + private func setupSubView() { + setupLabelHeader() + setupStabView() + setupCardStatysticsView() + } + + private func setupLabelHeader() { + view.addSubview(labelHeader) + NSLayoutConstraint.activate([ + labelHeader.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16), + labelHeader.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 44) + ]) + } + + private func setupStabView() { + [imageViewStab, lableTextStab].forEach { + view.addSubview($0) + $0.translatesAutoresizingMaskIntoConstraints = false + $0.backgroundColor = .clear + } + + NSLayoutConstraint.activate([ + imageViewStab.centerYAnchor.constraint(equalTo: view.centerYAnchor), + imageViewStab.centerXAnchor.constraint(equalTo: view.centerXAnchor), + + lableTextStab.centerXAnchor.constraint(equalTo: view.centerXAnchor), + lableTextStab.topAnchor.constraint(equalTo: imageViewStab.bottomAnchor, constant: 10) + ]) + } + + func setupCardStatysticsView() { + view.addSubview(cardStatysticsView) + NSLayoutConstraint.activate([ + cardStatysticsView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16), + cardStatysticsView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16), + cardStatysticsView.topAnchor.constraint(equalTo: labelHeader.bottomAnchor, constant: 77), + cardStatysticsView.heightAnchor.constraint(equalToConstant: 90) + ]) + } +} diff --git a/Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift b/Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift index 660e919..ce2a1ca 100644 --- a/Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift +++ b/Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift @@ -452,7 +452,7 @@ extension CreateTrackerViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { switch viewModel.listSettings[indexPath.row] { case .category: - let greateCategoriViewController = CreateCategoriesViewController(delegate: self, viewModel: CategoryViewModel()) + let greateCategoriViewController = SelectCategoriesViewController(delegate: self, viewModel: CategoryViewModel()) presentViewController(vc: greateCategoriViewController, modalStyle: .formSheet) case .schedule: let viewModel = SheduleViewModel() @@ -587,7 +587,7 @@ extension CreateTrackerViewController: ScheduleViewControllerDelegate { } //MARK: - GreateCategoriesViewControllerDelegate -extension CreateTrackerViewController: CreateCategoriesViewControllerDelegate { +extension CreateTrackerViewController: SelectCategoriesViewControllerDelegate { func createCategoriesViewController(vc: UIViewController, nameCategory: String) { viewModel.setNameNewCategory(self, nameCategory: nameCategory) vc.dismiss(animated: true) diff --git a/Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift b/Tracker/TabBarController/TrackersViews/ViewControllers/SelectCategoriesViewController.swift similarity index 95% rename from Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift rename to Tracker/TabBarController/TrackersViews/ViewControllers/SelectCategoriesViewController.swift index 6d86b29..fadd17a 100644 --- a/Tracker/TabBarController/TrackersViews/ViewControllers/CreateCategoriesViewController.swift +++ b/Tracker/TabBarController/TrackersViews/ViewControllers/SelectCategoriesViewController.swift @@ -7,13 +7,13 @@ import UIKit -//MARK: - CreateCategoriesViewControllerDelegate -protocol CreateCategoriesViewControllerDelegate: AnyObject { +//MARK: - SelectCategoriesViewControllerDelegate +protocol SelectCategoriesViewControllerDelegate: AnyObject { func createCategoriesViewController(vc: UIViewController, nameCategory: String) } -//MARK: - CreateCategoriesViewController -final class CreateCategoriesViewController: UIViewController { +//MARK: - SelectCategoriesViewController +final class SelectCategoriesViewController: UIViewController { private struct ConstantsCreateCatVc { static let imageViewImageStab = "Star" static let alertActionErrorTitle = "Ok" @@ -32,7 +32,7 @@ final class CreateCategoriesViewController: UIViewController { static let insertSeparatorTableView = UIEdgeInsets(top: .zero, left: 12, bottom: .zero, right: 12) } - weak var delegate: CreateCategoriesViewControllerDelegate? + weak var delegate: SelectCategoriesViewControllerDelegate? private var viewModel: CategoryViewModelProtocol private let handler = HandlerResultType() @@ -101,7 +101,7 @@ final class CreateCategoriesViewController: UIViewController { return createCategoryButton }() - init(delegate: CreateCategoriesViewControllerDelegate, viewModel: CategoryViewModelProtocol) { + init(delegate: SelectCategoriesViewControllerDelegate, viewModel: CategoryViewModelProtocol) { self.delegate = delegate self.viewModel = viewModel super.init(nibName: nil, bundle: nil) @@ -128,7 +128,7 @@ final class CreateCategoriesViewController: UIViewController { } } -extension CreateCategoriesViewController { +extension SelectCategoriesViewController { //MARK: - обработка событий @objc private func didTapСategoryButton() { @@ -217,7 +217,7 @@ extension CreateCategoriesViewController { } //MARK: - UITableViewDataSource -extension CreateCategoriesViewController: UITableViewDataSource { +extension SelectCategoriesViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { handler.resultTypeHandlerGetValue(viewModel.сategoryExcludingFixed())?.count ?? 0 } @@ -258,7 +258,7 @@ extension CreateCategoriesViewController: UITableViewDataSource { } //MARK: - UITableViewDelegate -extension CreateCategoriesViewController: UITableViewDelegate { +extension SelectCategoriesViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { guard let cell = tableView.cellForRow(at: indexPath) as? CustomTableViewCell, let delegate @@ -273,7 +273,7 @@ extension CreateCategoriesViewController: UITableViewDelegate { } //MARK: - NewCategoriViewControllerDelegate -extension CreateCategoriesViewController: NewCategoriViewControllerDelegate { +extension SelectCategoriesViewController: NewCategoriViewControllerDelegate { func didNewCategoryName(_ vc: UIViewController, nameCategory: String) { guard let category = handler.resultTypeHandlerGetValue(viewModel.getCategory()) else { return } diff --git a/Tracker/ViewModel/CategoryViewModel.swift b/Tracker/ViewModel/CategoryViewModel.swift index 1c4eab8..97c6817 100644 --- a/Tracker/ViewModel/CategoryViewModel.swift +++ b/Tracker/ViewModel/CategoryViewModel.swift @@ -21,7 +21,7 @@ final class CategoryViewModel { private let textFixed = NSLocalizedString("textFixed", comment: "") @Observable> private(set) var category: Result<[TrackerCategory], Error> - @UserDefaultsBacked(key: "select_name_category") private var selectNameCategory: String? + @UserDefaultsBacked(key: UserDefaultKeys.selectNameCategory.rawValue) private var selectNameCategory: String? private let trackerCategoryStore: TrackerCategoryStoreProtocol private let trackerStore: TrackerStoreProtocol diff --git a/Tracker/ViewModel/EditTrackerViewModel.swift b/Tracker/ViewModel/EditTrackerViewModel.swift index dd479ad..7f8c3d8 100644 --- a/Tracker/ViewModel/EditTrackerViewModel.swift +++ b/Tracker/ViewModel/EditTrackerViewModel.swift @@ -90,7 +90,7 @@ extension EditTrackerViewModel: EditTrackerModelProtocol { func jonedSchedule(schedule: [WeekDay], stringArrayDay: String) -> String { var stringListDay: String - if schedule.count == 7 { + if schedule.count == regular.count { stringListDay = stringArrayDay return stringListDay } diff --git a/Tracker/ViewModel/FiltersViewModel.swift b/Tracker/ViewModel/FiltersViewModel.swift index 512c4cc..db94cc6 100644 --- a/Tracker/ViewModel/FiltersViewModel.swift +++ b/Tracker/ViewModel/FiltersViewModel.swift @@ -15,9 +15,8 @@ protocol FiltersViewModelProtocol { } final class FiltersViewModel { - private static let keySelectFilter = "select_filter" let filtersState: [FiltersState] = [.allTrackers, .toDayTrackers, .completed, .notCompleted] - @UserDefaultsBacked(key: FiltersViewModel.keySelectFilter) private var selectFilter: String? + @UserDefaultsBacked(key: UserDefaultKeys.selectFilter.rawValue) private var selectFilter: String? } extension FiltersViewModel: FiltersViewModelProtocol { diff --git a/Tracker/ViewModel/StatisticViewModel.swift b/Tracker/ViewModel/StatisticViewModel.swift new file mode 100644 index 0000000..501f52e --- /dev/null +++ b/Tracker/ViewModel/StatisticViewModel.swift @@ -0,0 +1,47 @@ +// +// StatisticViewModel.swift +// Tracker +// +// Created by Григорий Машук on 25.12.23. +// + +import Foundation + +protocol StatisticsViewModelProtocol { + func getCountTrackerComplet() -> Int? + func getIsTracker() -> Bool? +} + +final class StatisticsViewModel{ + @Observable private(set) var countTrackerComplet: Int? + @UserDefaultsBacked(key: UserDefaultKeys.isTracker.rawValue) private(set) var isTracker: Bool? + + let trackerRecordStore: TrackerRecordStoreProtocol + + convenience init() { + let trackerRecordStore = TrackerRecordStore() + self.init(trackerRecordStore: trackerRecordStore) + countTrackerComplet = getCountTrackerComplet() + trackerRecordStore.delegate = self + } + + init(trackerRecordStore: TrackerRecordStoreProtocol) { + self.trackerRecordStore = trackerRecordStore + } +} + +extension StatisticsViewModel: StatisticsViewModelProtocol { + func getCountTrackerComplet() -> Int? { + trackerRecordStore.getCountTrackerComplet() + } + + func getIsTracker() -> Bool? { + isTracker + } +} + +extension StatisticsViewModel: TrackerRecordStoreDelegate { + func trackerRecordStore(trackerRecordStore: TrackerRecordStoreProtocol) { + countTrackerComplet = getCountTrackerComplet() + } +} diff --git a/Tracker/ViewModel/TrackerViewModel.swift b/Tracker/ViewModel/TrackerViewModel.swift index d90d1eb..a4d6f95 100644 --- a/Tracker/ViewModel/TrackerViewModel.swift +++ b/Tracker/ViewModel/TrackerViewModel.swift @@ -43,15 +43,13 @@ protocol TrackerViewModelProtocol { //MARK: - ViewModel final class TrackerViewModel { private let textFixed = NSLocalizedString("textFixed", comment: "") - private static let keySelectFilter = "select_filter" - private static let keySelectNameCategory = "select_name_category" @Observable> private(set) var category: Result<[TrackerCategory], Error> @Observable private(set) var indexPath: IndexPath @Observable> private(set) var visibleCategory: Result<[TrackerCategory], Error> @Observableprivate(set) var filterState: FiltersState - @UserDefaultsBacked(key: TrackerViewModel.keySelectNameCategory) private var selectNameCategory: String? - @UserDefaultsBacked(key: TrackerViewModel.keySelectFilter) private var selectFilter: String? + @UserDefaultsBacked(key: UserDefaultKeys.selectNameCategory.rawValue) private var selectNameCategory: String? + @UserDefaultsBacked(key: UserDefaultKeys.selectFilter.rawValue) private var selectFilter: String? private(set) var completedTrackers: Result, Error> @@ -244,7 +242,7 @@ extension TrackerViewModel: TrackerViewModelProtocol { } func loadTrackerRecord(id: UUID) -> Result { - trackerRecordStore.loadTrackerRecord(id: id) + trackerRecordStore.getCountTrackerRecordForDate(id: id) } func addNewTracker(_ tracker: Tracker, nameCategory: String) -> Result { From 52bbdc072b3b3376b4d6d5d74bb4aa144b477dc2 Mon Sep 17 00:00:00 2001 From: Grifon Date: Wed, 27 Dec 2023 22:55:19 +0300 Subject: [PATCH 10/24] add edit + delete category for menu --- Tracker.xcodeproj/project.pbxproj | 4 + .../xcdebugger/Breakpoints_v2.xcbkptlist | 398 ++++++++---------- .../Model.xcdatamodel/contents | 5 +- .../Domain/Store/PinnedCategoryStore.swift | 22 +- .../Domain/Store/TrackerCategoryStore.swift | 46 +- Tracker/Domain/Store/TrackerRecordStore.swift | 7 +- Tracker/Domain/Store/TrackerStore.swift | 20 +- .../Cell/CustomTableViewCell.swift | 4 +- .../StatisticsViewController.swift | 12 +- .../CreateTrackerViewController.swift | 6 +- .../NewCategoriViewController.swift | 57 ++- .../SelectCategoriesViewController.swift | 57 ++- .../TrackersViewController.swift | 12 +- Tracker/ViewModel/CategoryViewModel.swift | 14 +- Tracker/ViewModel/NewCategoryViewModel.swift | 39 ++ Tracker/ViewModel/StatisticViewModel.swift | 4 +- Tracker/ViewModel/TrackerViewModel.swift | 7 +- 17 files changed, 418 insertions(+), 296 deletions(-) create mode 100644 Tracker/ViewModel/NewCategoryViewModel.swift diff --git a/Tracker.xcodeproj/project.pbxproj b/Tracker.xcodeproj/project.pbxproj index ad917f8..fae143d 100644 --- a/Tracker.xcodeproj/project.pbxproj +++ b/Tracker.xcodeproj/project.pbxproj @@ -65,6 +65,7 @@ 1AC642722B383AD50072E081 /* AnalyticsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AC642712B383AD50072E081 /* AnalyticsService.swift */; }; 1AC642742B38442E0072E081 /* EventAnalytics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AC642732B38442E0072E081 /* EventAnalytics.swift */; }; 1AD277712ACD4B1800F92219 /* UITextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AD277702ACD4B1800F92219 /* UITextField.swift */; }; + 1AD888BA2B3AF7A400F8C26D /* NewCategoryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AD888B92B3AF7A400F8C26D /* NewCategoryViewModel.swift */; }; 1ADD3D742ACBDBB1005F639F /* TrackerCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ADD3D732ACBDBB1005F639F /* TrackerCollectionView.swift */; }; 1AE08AB82B2F5C250042CE59 /* FiltersViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AE08AB72B2F5C250042CE59 /* FiltersViewModel.swift */; }; 1AE27C2D2B0604A800C4A925 /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AE27C2C2B0604A800C4A925 /* Errors.swift */; }; @@ -156,6 +157,7 @@ 1AC642712B383AD50072E081 /* AnalyticsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsService.swift; sourceTree = ""; }; 1AC642732B38442E0072E081 /* EventAnalytics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventAnalytics.swift; sourceTree = ""; }; 1AD277702ACD4B1800F92219 /* UITextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITextField.swift; sourceTree = ""; }; + 1AD888B92B3AF7A400F8C26D /* NewCategoryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewCategoryViewModel.swift; sourceTree = ""; }; 1ADD3D732ACBDBB1005F639F /* TrackerCollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerCollectionView.swift; sourceTree = ""; }; 1AE08AB72B2F5C250042CE59 /* FiltersViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FiltersViewModel.swift; sourceTree = ""; }; 1AE27C2C2B0604A800C4A925 /* Errors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = ""; }; @@ -384,6 +386,7 @@ 1A0077F72B2DD6DF000EAE69 /* CategoryViewModel.swift */, 1AE08AB72B2F5C250042CE59 /* FiltersViewModel.swift */, 1A79C42C2B39C3E4009CE43A /* StatisticViewModel.swift */, + 1AD888B92B3AF7A400F8C26D /* NewCategoryViewModel.swift */, ); path = ViewModel; sourceTree = ""; @@ -677,6 +680,7 @@ 1A8A576C2ACD5F4D00C060FA /* CreateTrackerTableViewCell.swift in Sources */, 1A598BB82AC6FAC5009B166D /* TrackersViewController.swift in Sources */, 1A8A7FEA2AC55D970038CB42 /* StatisticsViewController.swift in Sources */, + 1AD888BA2B3AF7A400F8C26D /* NewCategoryViewModel.swift in Sources */, 1AC642742B38442E0072E081 /* EventAnalytics.swift in Sources */, 1A66BB192B25D16500838F5F /* PinnTrackerModel.swift in Sources */, 1A598BB62AC6F352009B166D /* TabBarController.swift in Sources */, diff --git a/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index 4bf26c4..89c2734 100644 --- a/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -3004,8 +3004,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/NewCategoriViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "147" - endingLineNumber = "147" + startingLineNumber = "176" + endingLineNumber = "176" landmarkName = "textFieldDidEndEditing(_:)" landmarkType = "7"> @@ -4021,8 +4021,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "174" - endingLineNumber = "174" + startingLineNumber = "169" + endingLineNumber = "169" landmarkName = "deleteTracker(_:)" landmarkType = "7"> @@ -4130,8 +4130,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "179" - endingLineNumber = "179" + startingLineNumber = "174" + endingLineNumber = "174" landmarkName = "deleteTracker(_:)" landmarkType = "7"> @@ -4286,8 +4286,8 @@ filePath = "Tracker/Domain/Store/TrackerCategoryStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "37" - endingLineNumber = "37" + startingLineNumber = "38" + endingLineNumber = "38" landmarkName = "fetchedCategoryResultController" landmarkType = "24"> @@ -4634,8 +4634,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "131" - endingLineNumber = "131" + startingLineNumber = "126" + endingLineNumber = "126" landmarkName = "addNewTracker(_:nameCategory:)" landmarkType = "7"> @@ -4666,8 +4666,8 @@ filePath = "Tracker/Domain/Store/TrackerRecordStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "126" - endingLineNumber = "126" + startingLineNumber = "127" + endingLineNumber = "127" landmarkName = "updateTrackerRecord(_:)" landmarkType = "7"> @@ -4902,8 +4902,8 @@ filePath = "Tracker/Domain/Store/TrackerCategoryStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "98" - endingLineNumber = "98" + startingLineNumber = "122" + endingLineNumber = "122" landmarkName = "controllerDidChangeContent(_:)" landmarkType = "7"> @@ -4918,8 +4918,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "195" - endingLineNumber = "195" + startingLineNumber = "190" + endingLineNumber = "190" landmarkName = "addPinnedCategory(_:pinnedCategory:)" landmarkType = "7"> @@ -5280,8 +5280,8 @@ filePath = "Tracker/ViewModel/CategoryViewModel.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "41" - endingLineNumber = "41" + startingLineNumber = "43" + endingLineNumber = "43" landmarkName = "init(trackerCategoryStore:trackerStore:category:)" landmarkType = "7"> @@ -5615,8 +5615,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "108" - endingLineNumber = "108" + startingLineNumber = "103" + endingLineNumber = "103" landmarkName = "searchTracker(id:)" landmarkType = "7"> @@ -5663,8 +5663,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "106" - endingLineNumber = "106" + startingLineNumber = "101" + endingLineNumber = "101" landmarkName = "searchTracker(id:)" landmarkType = "7"> @@ -5679,8 +5679,8 @@ filePath = "Tracker/ViewModel/TrackerViewModel.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "371" - endingLineNumber = "371" + startingLineNumber = "372" + endingLineNumber = "372" landmarkName = "storeCategory(trackerCategoryStore:)" landmarkType = "7"> @@ -5760,6 +5760,36 @@ endingLineNumber = "532" offsetFromSymbolStart = "0"> + + + + @@ -5773,8 +5803,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "219" - endingLineNumber = "219" + startingLineNumber = "205" + endingLineNumber = "205" landmarkName = "controllerDidChangeContent(_:)" landmarkType = "7"> @@ -5808,6 +5838,51 @@ endingLineNumber = "196" offsetFromSymbolStart = "52"> + + + + + + @@ -6066,6 +6141,36 @@ endingLineNumber = "266" offsetFromSymbolStart = "440"> + + + + @@ -6079,8 +6184,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "157" - endingLineNumber = "157" + startingLineNumber = "152" + endingLineNumber = "152" landmarkName = "updateTracker(tracker:nameCategory:)" landmarkType = "7"> @@ -6144,6 +6249,21 @@ endingLineNumber = "152" offsetFromSymbolStart = "992"> + + @@ -6157,8 +6277,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "145" - endingLineNumber = "145" + startingLineNumber = "140" + endingLineNumber = "140" landmarkName = "updateTracker(tracker:nameCategory:)" landmarkType = "7"> @@ -6207,6 +6327,21 @@ endingLineNumber = "140" offsetFromSymbolStart = "504"> + + @@ -6268,8 +6403,8 @@ filePath = "Tracker/ViewModel/TrackerViewModel.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "254" - endingLineNumber = "254" + startingLineNumber = "255" + endingLineNumber = "255" landmarkName = "updateTracker(tracker:nameCategory:)" landmarkType = "7"> @@ -6284,8 +6419,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "141" - endingLineNumber = "141" + startingLineNumber = "136" + endingLineNumber = "136" landmarkName = "updateTracker(tracker:nameCategory:)" landmarkType = "7"> @@ -6527,177 +6662,6 @@ landmarkType = "7"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -6756,8 +6720,8 @@ filePath = "Tracker/ViewModel/TrackerViewModel.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "180" - endingLineNumber = "180" + startingLineNumber = "181" + endingLineNumber = "181" landmarkName = "filterListTrackersCompleted(trackerCategory:date:)" landmarkType = "7"> @@ -6772,8 +6736,8 @@ filePath = "Tracker/ViewModel/TrackerViewModel.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "165" - endingLineNumber = "165" + startingLineNumber = "166" + endingLineNumber = "166" landmarkName = "filterListTrackersCompleted(trackerCategory:date:)" landmarkType = "7"> @@ -6883,8 +6847,8 @@ filePath = "Tracker/ViewModel/TrackerViewModel.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "194" - endingLineNumber = "194" + startingLineNumber = "195" + endingLineNumber = "195" landmarkName = "filterListTrackersNotCompleted(trackerCategory:date:)" landmarkType = "7"> @@ -7059,8 +7023,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "33" - endingLineNumber = "33" + startingLineNumber = "32" + endingLineNumber = "32" landmarkName = "fetchedTrackerResultController" landmarkType = "24"> @@ -7075,8 +7039,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "35" - endingLineNumber = "35" + startingLineNumber = "34" + endingLineNumber = "34" landmarkName = "fetchedTrackerResultController" landmarkType = "24"> diff --git a/Tracker/CoreData/Model.xcdatamodeld/Model.xcdatamodel/contents b/Tracker/CoreData/Model.xcdatamodeld/Model.xcdatamodel/contents index bb1e0d5..e379a24 100644 --- a/Tracker/CoreData/Model.xcdatamodeld/Model.xcdatamodel/contents +++ b/Tracker/CoreData/Model.xcdatamodeld/Model.xcdatamodel/contents @@ -18,9 +18,10 @@ + - - + + \ No newline at end of file diff --git a/Tracker/Domain/Store/PinnedCategoryStore.swift b/Tracker/Domain/Store/PinnedCategoryStore.swift index 0c87b5e..706cb43 100644 --- a/Tracker/Domain/Store/PinnedCategoryStore.swift +++ b/Tracker/Domain/Store/PinnedCategoryStore.swift @@ -2,7 +2,7 @@ // PinnedCategoryStore.swift // Tracker // -// Created by Марина Машук on 11.12.23. +// Created by Григорий Машук on 11.12.23. // import Foundation @@ -34,7 +34,7 @@ final class PinnedCategoryStore: NSObject { } private extension PinnedCategoryStore { - func save(context: NSManagedObjectContext) -> Result { + func save() -> Result { do { try context.save() return .success(()) @@ -42,6 +42,15 @@ private extension PinnedCategoryStore { return .failure(error) } } + + func searchCategoryById(id: UUID) throws -> PinnCategoryCoreData? { + let request = NSFetchRequest(entityName: "\(PinnCategoryCoreData.self)") + request.returnsObjectsAsFaults = false + guard let keyPath = (\PinnCategoryCoreData.trackerId)._kvcKeyPathString + else { throw StoreErrors.TrackrerStoreError.getTrackerError } + request.predicate = NSPredicate(format: "%K == %@", keyPath, id as CVarArg) + return try context.fetch(request).first + } } extension PinnedCategoryStore: PinnedCategoryStoreProtocol { @@ -56,17 +65,12 @@ extension PinnedCategoryStore: PinnedCategoryStoreProtocol { } func deleteAndGetPinnedCategory(_ id: UUID) -> Result { - let request = NSFetchRequest(entityName: "\(PinnCategoryCoreData.self)") - request.returnsObjectsAsFaults = false - guard let keyPath = (\PinnCategoryCoreData.trackerId)._kvcKeyPathString - else { return .failure(StoreErrors.TrackrerStoreError.getTrackerError) } - request.predicate = NSPredicate(format: "%K == %@", keyPath, id as CVarArg) do { - if let pinnCategoryCoreData = try context.fetch(request).first { + if let pinnCategoryCoreData = try searchCategoryById(id: id) { let nameCategory = pinnCategoryCoreData.nameCategory pinnCategoryCoreData.nameCategory = nil pinnCategoryCoreData.trackerId = nil - let _ = save(context: context) + let _ = save() return .success(nameCategory) } } catch { diff --git a/Tracker/Domain/Store/TrackerCategoryStore.swift b/Tracker/Domain/Store/TrackerCategoryStore.swift index a26180f..3125ce2 100644 --- a/Tracker/Domain/Store/TrackerCategoryStore.swift +++ b/Tracker/Domain/Store/TrackerCategoryStore.swift @@ -12,6 +12,7 @@ protocol TrackerCategoryStoreProtocol { func addCategory(_ nameCategory: String) -> Result func getListTrackerCategoryCoreData() -> [TrackerCategoryCoreData]? func deleteCategory(nameCategory: String) -> Result + func updateNameCategory(newNameCategory: String, oldNameCategory: String) -> Result } protocol TrackerCategoryStoreDelegate: AnyObject { @@ -22,7 +23,7 @@ protocol TrackerCategoryStoreDelegate: AnyObject { final class TrackerCategoryStore: NSObject { private let textFixed = NSLocalizedString("textFixed", comment: "") weak var delegate: TrackerCategoryStoreDelegate? - + @UserDefaultsBacked(key: UserDefaultKeys.isTracker.rawValue) private(set) var isTracker: Bool? private let context: NSManagedObjectContext private lazy var fetchedCategoryResultController: NSFetchedResultsController = { @@ -32,7 +33,7 @@ final class TrackerCategoryStore: NSObject { request.sortDescriptors = [sortPinned, sortName] let fetchedResultController = NSFetchedResultsController(fetchRequest: request, managedObjectContext: context, - sectionNameKeyPath: #keyPath(TrackerCategoryCoreData.nameCategory), + sectionNameKeyPath: nil, cacheName: nil) fetchedResultController.delegate = self try? fetchedResultController.performFetch() @@ -51,7 +52,7 @@ final class TrackerCategoryStore: NSObject { } private extension TrackerCategoryStore { - func save(context: NSManagedObjectContext) -> Result { + func save() -> Result { do { try context.save() return .success(()) @@ -59,6 +60,18 @@ private extension TrackerCategoryStore { return .failure(error) } } + + func searchTrackerCategoryCD(nameCategory: String) throws -> TrackerCategoryCoreData? { + let request = NSFetchRequest(entityName: "\(TrackerCategoryCoreData.self)") + request.returnsObjectsAsFaults = false + request.predicate = NSPredicate(format: "%K == %@", #keyPath(TrackerCategoryCoreData.nameCategory), nameCategory as CVarArg) + return try context.fetch(request).first + } + + func update(trackerCategoryCD: TrackerCategoryCoreData, newNameCategory: String) -> Result { + trackerCategoryCD.nameCategory = newNameCategory + return save() + } } //MARK: - TrackerCategoryStoreProtocol @@ -67,26 +80,37 @@ extension TrackerCategoryStore: TrackerCategoryStoreProtocol { let categoryCoreData = TrackerCategoryCoreData(context: context) categoryCoreData.nameCategory = nameCategory categoryCoreData.isPinned = nameCategory == textFixed ? false : true - return save(context: context) + return save() } func getListTrackerCategoryCoreData() -> [TrackerCategoryCoreData]? { - fetchedCategoryResultController.fetchedObjects + let trackCatCD = fetchedCategoryResultController.fetchedObjects + isTracker = trackCatCD?.first { $0.trakers?.count ?? 0 > 0 } == nil + return trackCatCD } func deleteCategory(nameCategory: String) -> Result { - let request = NSFetchRequest(entityName: "\(TrackerCategoryCoreData.self)") - request.returnsObjectsAsFaults = false - request.predicate = NSPredicate(format: "%K == %@", #keyPath(TrackerCategoryCoreData.nameCategory), nameCategory as CVarArg) do { - if let category = try context.fetch(request).first { + if let category = try searchTrackerCategoryCD(nameCategory: nameCategory) { context.delete(category) - return save(context: context) + category.trakers = nil + return save() + } + } catch { + return .failure(error) + } + return save() + } + + func updateNameCategory(newNameCategory: String, oldNameCategory: String) -> Result { + do { + if let trackCatCD = try searchTrackerCategoryCD(nameCategory: oldNameCategory) { + return update(trackerCategoryCD: trackCatCD, newNameCategory: newNameCategory) } } catch { return .failure(error) } - return save(context: context) + return save() } } diff --git a/Tracker/Domain/Store/TrackerRecordStore.swift b/Tracker/Domain/Store/TrackerRecordStore.swift index 0a466f3..fa45773 100644 --- a/Tracker/Domain/Store/TrackerRecordStore.swift +++ b/Tracker/Domain/Store/TrackerRecordStore.swift @@ -10,7 +10,7 @@ import CoreData protocol TrackerRecordStoreProtocol { func updateTrackerRecord(_ trackerRecord: TrackerRecord) -> Result - func getCountTrackerRecordForDate(id: UUID) -> Result + func getCountTrackerRecord(id: UUID) -> Result func treckersRecordsResult() -> Result, Error> func getCountTrackerComplet() -> Int? } @@ -110,7 +110,8 @@ private extension TrackerRecordStore { //MARK: - TrackerRecordStoreProtocol extension TrackerRecordStore: TrackerRecordStoreProtocol { func treckersRecordsResult() -> Result, Error> { - guard let trackerRecordsCoreData = fetchedTrackerRecordResultController.fetchedObjects + let trackerRecordsCoreData = fetchedTrackerRecordResultController.fetchedObjects + guard let trackerRecordsCoreData else { return .failure(StoreErrors.TrackrerRecordStoreError.loadTrackerRecord) } do { let trackerRecord = try trackerRecordsCoreData.map ({ try self.trackerRecord(from: $0) }) @@ -133,7 +134,7 @@ extension TrackerRecordStore: TrackerRecordStoreProtocol { } } - func getCountTrackerRecordForDate(id: UUID) -> Result { + func getCountTrackerRecord(id: UUID) -> Result { do { if let countTrackers = try getCountTrackersRecord(id: id) { return .success(countTrackers) diff --git a/Tracker/Domain/Store/TrackerStore.swift b/Tracker/Domain/Store/TrackerStore.swift index d2c4f7d..e9bcb2d 100644 --- a/Tracker/Domain/Store/TrackerStore.swift +++ b/Tracker/Domain/Store/TrackerStore.swift @@ -23,7 +23,6 @@ protocol TrackerStoreDelegate: AnyObject { //MARK: - TrackerStore final class TrackerStore: NSObject { weak var delegate: TrackerStoreDelegate? - @UserDefaultsBacked(key: UserDefaultKeys.isTracker.rawValue) private(set) var isTracker: Bool? private let context: NSManagedObjectContext private let colorMarshalling = UIColorMarshalling() @@ -97,10 +96,6 @@ private extension TrackerStore { } } - func getTrackerFor(_ indexPath: IndexPath) -> TrackerCoreData { - fetchedTrackerResultController.object(at: indexPath) - } - func searchTracker(id: UUID) throws -> TrackerCoreData? { let request = NSFetchRequest(entityName: "\(TrackerCoreData.self)") request.returnsObjectsAsFaults = false @@ -180,10 +175,10 @@ extension TrackerStore: TrackerStoreProtocol { } func getTrackers(_ objects: [TrackerCoreData]) -> Result<[Tracker], Error> { - isTracker = objects.isEmpty + let _ = fetchedTrackerResultController do { - let trackers = try objects.map({ try tracker(from: $0) }) - return .success(trackers.sorted { $0.name < $1.name }) + let trackers = try objects.map({ try tracker(from: $0) }).sorted { $0.name < $1.name } + return .success(trackers) } catch { return .failure(error) } @@ -202,15 +197,6 @@ extension TrackerStore: TrackerStoreProtocol { } return save() } - -// func getCompletedTrackers(id: UUID, date: Date) -> TrackerCoreData? { -// let request = NSFetchRequest(entityName: "\(TrackerCoreData.self)") -// request.returnsObjectsAsFaults = false -// guard let keyPath = (\TrackerRecordCoreData.trackerId)._kvcKeyPathString -// else { return nil } -// request.predicate = NSPredicate(format: "%K == %@ AND %K == %@", keyPath, id as CVarArg, #keyPath(TrackerRecordCoreData.date), date as CVarArg) -// return try? context.fetch(request).first -// } } extension TrackerStore: NSFetchedResultsControllerDelegate { diff --git a/Tracker/TabBarController/TrackersViews/Cell/CustomTableViewCell.swift b/Tracker/TabBarController/TrackersViews/Cell/CustomTableViewCell.swift index 305894f..d6aa948 100644 --- a/Tracker/TabBarController/TrackersViews/Cell/CustomTableViewCell.swift +++ b/Tracker/TabBarController/TrackersViews/Cell/CustomTableViewCell.swift @@ -1,5 +1,5 @@ // -// CreateCategoriesTableViewCell.swift +// CustomTableViewCell.swift // Tracker // // Created by Григорий Машук on 13.10.23. @@ -7,7 +7,7 @@ import UIKit -//MARK: - CreateCategoriesTableViewCell +//MARK: - CustomTableViewCell final class CustomTableViewCell: UITableViewCell { private struct ConstantsGreateCell { static let iconButton = "IconButtonCell" diff --git a/Tracker/TabBarController/TrackersViews/StatisticViews/StatisticsViewController.swift b/Tracker/TabBarController/TrackersViews/StatisticViews/StatisticsViewController.swift index 8cc09ba..618168d 100644 --- a/Tracker/TabBarController/TrackersViews/StatisticViews/StatisticsViewController.swift +++ b/Tracker/TabBarController/TrackersViews/StatisticViews/StatisticsViewController.swift @@ -86,7 +86,7 @@ class StatisticsViewController: UIViewController { } } -extension StatisticsViewController { +private extension StatisticsViewController { func bind() { guard let viewModel = viewModel as? StatisticsViewModel else { return } viewModel.$countTrackerComplet.bind { [weak self] count in @@ -95,7 +95,7 @@ extension StatisticsViewController { } } - private func addBorderGradient(view: UIView, + func addBorderGradient(view: UIView, colors: [CGColor], borderWidth: CGFloat, startPoint: CGPoint, @@ -121,7 +121,7 @@ extension StatisticsViewController { view.layer.addSublayer(gradient) } - private func showStabView(flag: Bool?) { + func showStabView(flag: Bool?) { guard let flag else { imageViewStab.isHidden = false lableTextStab.isHidden = false @@ -132,13 +132,13 @@ extension StatisticsViewController { cardStatysticsView.isHidden = flag } - private func setupSubView() { + func setupSubView() { setupLabelHeader() setupStabView() setupCardStatysticsView() } - private func setupLabelHeader() { + func setupLabelHeader() { view.addSubview(labelHeader) NSLayoutConstraint.activate([ labelHeader.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16), @@ -146,7 +146,7 @@ extension StatisticsViewController { ]) } - private func setupStabView() { + func setupStabView() { [imageViewStab, lableTextStab].forEach { view.addSubview($0) $0.translatesAutoresizingMaskIntoConstraints = false diff --git a/Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift b/Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift index ce2a1ca..7838e36 100644 --- a/Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift +++ b/Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift @@ -452,8 +452,8 @@ extension CreateTrackerViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { switch viewModel.listSettings[indexPath.row] { case .category: - let greateCategoriViewController = SelectCategoriesViewController(delegate: self, viewModel: CategoryViewModel()) - presentViewController(vc: greateCategoriViewController, modalStyle: .formSheet) + let selectCategoriesViewController = SelectCategoriesViewController(delegate: self, viewModel: CategoryViewModel()) + presentViewController(vc: selectCategoriesViewController, modalStyle: .formSheet) case .schedule: let viewModel = SheduleViewModel() let scheduleViewController = ScheduleViewController(viewModel: viewModel) @@ -588,7 +588,7 @@ extension CreateTrackerViewController: ScheduleViewControllerDelegate { //MARK: - GreateCategoriesViewControllerDelegate extension CreateTrackerViewController: SelectCategoriesViewControllerDelegate { - func createCategoriesViewController(vc: UIViewController, nameCategory: String) { + func categoriesViewController(vc: UIViewController, nameCategory: String) { viewModel.setNameNewCategory(self, nameCategory: nameCategory) vc.dismiss(animated: true) } diff --git a/Tracker/TabBarController/TrackersViews/ViewControllers/NewCategoriViewController.swift b/Tracker/TabBarController/TrackersViews/ViewControllers/NewCategoriViewController.swift index 7ba8435..bcc4dea 100644 --- a/Tracker/TabBarController/TrackersViews/ViewControllers/NewCategoriViewController.swift +++ b/Tracker/TabBarController/TrackersViews/ViewControllers/NewCategoriViewController.swift @@ -11,6 +11,10 @@ protocol NewCategoriViewControllerDelegate: AnyObject { func didNewCategoryName(_ vc: UIViewController, nameCategory: String) } +protocol UpdateCategoriViewControllerDelegate: AnyObject { + func didUpdateCategoryName(_ vc: UIViewController, newNameCategory: String, oldNameCategory: String) +} + //MARK: - NewCategoriViewController class NewCategoriViewController: UIViewController { private struct ConstantsNewCatVc { @@ -24,13 +28,10 @@ class NewCategoriViewController: UIViewController { static let textFieldFont = UIFont.systemFont(ofSize: 17, weight: .regular) } - private var nameCategori: String = "" { - didSet { - chengeHiddenButton(flag: nameCategori.isEmpty) - } - } + weak var createCategorydelegate: NewCategoriViewControllerDelegate? + weak var updateCategoryrDelegate: UpdateCategoriViewControllerDelegate? - weak var delegate: NewCategoriViewControllerDelegate? + private var viewModel: NewCategoryViewModelProtocol? private lazy var newCategoriLabel: UILabel = { let newCategoriLabel = UILabel() @@ -72,8 +73,22 @@ class NewCategoriViewController: UIViewController { return createNameTextField }() + init(createCategorydelegate: NewCategoriViewControllerDelegate? = nil, + updateCategoryrDelegate: UpdateCategoriViewControllerDelegate? = nil) + { + self.createCategorydelegate = createCategorydelegate + self.updateCategoryrDelegate = updateCategoryrDelegate + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + override func viewDidLoad() { super.viewDidLoad() + viewModel = NewCategoryViewModel() + bind() let tapGesture = UITapGestureRecognizer(target: self, action: #selector(hideKeyboard)) tapGesture.cancelsTouchesInView = false view.addGestureRecognizer(tapGesture) @@ -83,10 +98,24 @@ class NewCategoriViewController: UIViewController { } private extension NewCategoriViewController { + func bind() { + guard let viewModel = viewModel as? NewCategoryViewModel else { return } + viewModel.$newNameCategory.bind { [weak self] text in + guard let self else { return } + chengeHiddenButton(flag: viewModel.getNewNameCategory().isEmpty) + } + } + //MARK: - Обработка событий @objc func didTapNewСategoriButton() { - delegate?.didNewCategoryName(self, nameCategory: nameCategori) + guard let viewModel else { return } + if let createCategorydelegate { + createCategorydelegate.didNewCategoryName(self, nameCategory: viewModel.getNewNameCategory()) + } + if let updateCategoryrDelegate { + updateCategoryrDelegate.didUpdateCategoryName(self, newNameCategory: viewModel.getNewNameCategory(), oldNameCategory: viewModel.getOldNameCategory()) + } dismiss(animated: true) } @@ -144,8 +173,9 @@ private extension NewCategoriViewController { //MARK: - UITextFieldDelegate extension NewCategoriViewController: UITextFieldDelegate { func textFieldDidEndEditing(_ textField: UITextField) { - guard let text = textField.text else { return } - nameCategori = text + guard let text = textField.text, let viewModel + else { return } + viewModel.setNewNameCategory(text: text) } func textFieldShouldReturn(_ textField: UITextField) -> Bool { @@ -153,3 +183,12 @@ extension NewCategoriViewController: UITextFieldDelegate { return true } } + +extension NewCategoriViewController: SelectCategoryEditViewControllerDelegate { + func editCategoriesViewController(vc: UIViewController, oldNameCategory: String) { + guard let viewModel else { return } + createNameTextField.text = oldNameCategory + viewModel.setNewNameCategory(text: oldNameCategory) + viewModel.setOldNameCategory(text: oldNameCategory) + } +} diff --git a/Tracker/TabBarController/TrackersViews/ViewControllers/SelectCategoriesViewController.swift b/Tracker/TabBarController/TrackersViews/ViewControllers/SelectCategoriesViewController.swift index fadd17a..d6a7d63 100644 --- a/Tracker/TabBarController/TrackersViews/ViewControllers/SelectCategoriesViewController.swift +++ b/Tracker/TabBarController/TrackersViews/ViewControllers/SelectCategoriesViewController.swift @@ -9,7 +9,11 @@ import UIKit //MARK: - SelectCategoriesViewControllerDelegate protocol SelectCategoriesViewControllerDelegate: AnyObject { - func createCategoriesViewController(vc: UIViewController, nameCategory: String) + func categoriesViewController(vc: UIViewController, nameCategory: String) +} + +protocol SelectCategoryEditViewControllerDelegate: AnyObject { + func editCategoriesViewController(vc: UIViewController, oldNameCategory: String) } //MARK: - SelectCategoriesViewController @@ -21,6 +25,8 @@ final class SelectCategoriesViewController: UIViewController { static let categoryLabelText = NSLocalizedString("categoriLabelText", comment: "") static let categoryAddButtonText = NSLocalizedString("categoriAddButtonText", comment: "") static let labelStabText = NSLocalizedString("labelStabText", comment: "") + static let textEdit = NSLocalizedString("textEdit", comment: "") + static let textDelete = NSLocalizedString("textDelete", comment: "") static let textFixed = NSLocalizedString("textFixed", comment: "") static let lableTextStabNumberOfLines = 2 @@ -33,6 +39,7 @@ final class SelectCategoriesViewController: UIViewController { } weak var delegate: SelectCategoriesViewControllerDelegate? + weak var editDelegate: SelectCategoryEditViewControllerDelegate? private var viewModel: CategoryViewModelProtocol private let handler = HandlerResultType() @@ -132,8 +139,7 @@ extension SelectCategoriesViewController { //MARK: - обработка событий @objc private func didTapСategoryButton() { - let newCategoryVC = NewCategoriViewController() - newCategoryVC.delegate = self + let newCategoryVC = NewCategoriViewController(createCategorydelegate: self) newCategoryVC.modalPresentationStyle = .formSheet present(newCategoryVC, animated: true) } @@ -153,6 +159,27 @@ extension SelectCategoriesViewController { } } + private func editAction(text: String, + category: TrackerCategory) -> UIAction + { + UIAction(title: text) { [weak self] _ in + guard let self else { return } + let newCategoryVC = NewCategoriViewController(updateCategoryrDelegate: self) + self.editDelegate = newCategoryVC + newCategoryVC.modalPresentationStyle = .formSheet + present(newCategoryVC, animated: true) { + self.editDelegate?.editCategoriesViewController(vc: self, oldNameCategory: category.nameCategory) + } + } + } + + private func deleteAction(text: String, category: TrackerCategory) -> UIAction { + UIAction(title: text, attributes: .destructive) { [weak self] _ in + guard let self else { return } + handler.resultTypeHandler(viewModel.deleteCategory(nameCategory: category.nameCategory)) {} + } + } + //MARK: - SetupUI private func setupUIElement() { setupCategoryButton() @@ -267,9 +294,24 @@ extension SelectCategoriesViewController: UITableViewDelegate { handler.resultTypeHandler(viewModel.selectСategory(at: indexPath.row)) {} handler.resultTypeHandler(viewModel.createNameCategory(at: indexPath.row)) { [weak self] cat in guard let self else { return } - delegate.createCategoriesViewController(vc: self, nameCategory: cat) + delegate.categoriesViewController(vc: self, nameCategory: cat) } } + + func tableView(_ tableView: UITableView, + contextMenuConfigurationForRowAt indexPath: IndexPath, + point: CGPoint + ) -> UIContextMenuConfiguration? { + return UIContextMenuConfiguration(actionProvider: { [weak self] _ in + guard let self, + let category = handler.resultTypeHandlerGetValue(viewModel.сategoryExcludingFixed())?[indexPath.row] + else { return UIMenu()} + return UIMenu(children: [ + editAction(text: ConstantsCreateCatVc.textEdit, category: category), + deleteAction(text: ConstantsCreateCatVc.textDelete, category: category) + ]) + }) + } } //MARK: - NewCategoriViewControllerDelegate @@ -287,3 +329,10 @@ extension SelectCategoriesViewController: NewCategoriViewControllerDelegate { handler.resultTypeHandler(viewModel.addCategory(nameCategory: nameFirstUppercased)) {} } } + +//MARK: - UpdateCategoriViewControllerDelegate +extension SelectCategoriesViewController: UpdateCategoriViewControllerDelegate { + func didUpdateCategoryName(_ vc: UIViewController, newNameCategory: String, oldNameCategory: String) { + handler.resultTypeHandler(viewModel.updateNameCategory(newNameCategory: newNameCategory, oldNameCaetegory: oldNameCategory)) {} + } +} diff --git a/Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift b/Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift index 0ad9cc5..3623830 100644 --- a/Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift +++ b/Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift @@ -173,7 +173,7 @@ final class TrackersViewController: UIViewController { filterState(state: state) } } else { - viewModel.allTrackers(date: datePicker.date) + viewModel.allTrackersByDate(date: datePicker.date) } } } @@ -204,11 +204,11 @@ private extension TrackersViewController { guard let self else { return } switch state { case .allTrackers: - viewModel.allTrackers(date: datePicker.date) + viewModel.allTrackersByDate(date: datePicker.date) setColorButtonFilter(state: viewModel.getFilterState(), button: buttonFilter) case .toDayTrackers: datePicker.date = Date() - viewModel.allTrackers(date: datePicker.date) + viewModel.allTrackersByDate(date: datePicker.date) setColorButtonFilter(state: viewModel.getFilterState(), button: buttonFilter) case .completed: viewModel.getCompleted(date: datePicker.date) @@ -256,10 +256,10 @@ private extension TrackersViewController { guard let viewModel else { return } switch state { case .allTrackers: - viewModel.allTrackers(date: datePicker.date) + viewModel.allTrackersByDate(date: datePicker.date) setColorButtonFilter(state: viewModel.getFilterState(), button: buttonFilter) case .toDayTrackers: - viewModel.allTrackers(date: datePicker.date) + viewModel.allTrackersByDate(date: datePicker.date) setColorButtonFilter(state: viewModel.getFilterState(), button: buttonFilter) case .completed: viewModel.getCompleted(date: datePicker.date) @@ -334,7 +334,7 @@ private extension TrackersViewController { UIAction(title: text) { [weak self] _ in guard let self else { return } let viewModel = EditTrackerViewModel() - let createTrackerVC = CreateTrackerViewController(viewModel: viewModel, updateTrackerDelegate: self, createTrackerDelegate: nil) + let createTrackerVC = CreateTrackerViewController(viewModel: viewModel, updateTrackerDelegate: self) delegate = createTrackerVC if tracker.schedule.count != viewModel.regular.count { createTrackerVC.reverseIsSchedul() diff --git a/Tracker/ViewModel/CategoryViewModel.swift b/Tracker/ViewModel/CategoryViewModel.swift index 97c6817..fd6bf3d 100644 --- a/Tracker/ViewModel/CategoryViewModel.swift +++ b/Tracker/ViewModel/CategoryViewModel.swift @@ -11,9 +11,11 @@ protocol CategoryViewModelProtocol { func isCategorySelected(at index: Int) -> Result func getCategory() -> Result<[TrackerCategory], Error> func сategoryExcludingFixed() -> Result<[TrackerCategory], Error> + func addCategory(nameCategory: String) -> Result func createNameCategory(at index: Int) -> Result + func updateNameCategory(newNameCategory: String, oldNameCaetegory: String) -> Result + func deleteCategory(nameCategory: String) -> Result func selectСategory(at index: Int) -> Result - func addCategory(nameCategory: String) -> Result } //MARK: - ViewModel @@ -21,7 +23,7 @@ final class CategoryViewModel { private let textFixed = NSLocalizedString("textFixed", comment: "") @Observable> private(set) var category: Result<[TrackerCategory], Error> - @UserDefaultsBacked(key: UserDefaultKeys.selectNameCategory.rawValue) private var selectNameCategory: String? + @UserDefaultsBacked(key: UserDefaultKeys.selectNameCategory.rawValue) private(set) var selectNameCategory: String? private let trackerCategoryStore: TrackerCategoryStoreProtocol private let trackerStore: TrackerStoreProtocol @@ -74,6 +76,10 @@ extension CategoryViewModel: CategoryViewModelProtocol { trackerCategoryStore.addCategory(nameCategory) } + func updateNameCategory(newNameCategory: String, oldNameCaetegory: String) -> Result { + trackerCategoryStore.updateNameCategory(newNameCategory: newNameCategory, oldNameCategory: oldNameCaetegory) + } + func isCategorySelected(at index: Int) -> Result { switch category { case .success(let category): @@ -114,6 +120,10 @@ extension CategoryViewModel: CategoryViewModelProtocol { } } + func deleteCategory(nameCategory: String) -> Result { + trackerCategoryStore.deleteCategory(nameCategory: nameCategory) + } + func selectСategory(at index: Int) -> Result { switch category { case .success(let category): diff --git a/Tracker/ViewModel/NewCategoryViewModel.swift b/Tracker/ViewModel/NewCategoryViewModel.swift new file mode 100644 index 0000000..165d2ab --- /dev/null +++ b/Tracker/ViewModel/NewCategoryViewModel.swift @@ -0,0 +1,39 @@ +// +// NewCategoryViewModel.swift +// Tracker +// +// Created by Григорий Машук on 26.12.23. +// + +import Foundation + +protocol NewCategoryViewModelProtocol { + func setNewNameCategory(text: String) + func getNewNameCategory() -> String + + func setOldNameCategory(text: String) + func getOldNameCategory() -> String +} + +final class NewCategoryViewModel { + @Observable private(set) var newNameCategory: String = "" + private var oldNameCategory: String = "" +} + +extension NewCategoryViewModel: NewCategoryViewModelProtocol { + func setOldNameCategory(text: String) { + oldNameCategory = text + } + + func getOldNameCategory() -> String { + oldNameCategory + } + + func setNewNameCategory(text: String) { + newNameCategory = text + } + + func getNewNameCategory() -> String { + newNameCategory + } +} diff --git a/Tracker/ViewModel/StatisticViewModel.swift b/Tracker/ViewModel/StatisticViewModel.swift index 501f52e..cc8aa65 100644 --- a/Tracker/ViewModel/StatisticViewModel.swift +++ b/Tracker/ViewModel/StatisticViewModel.swift @@ -12,10 +12,10 @@ protocol StatisticsViewModelProtocol { func getIsTracker() -> Bool? } -final class StatisticsViewModel{ +final class StatisticsViewModel { @Observable private(set) var countTrackerComplet: Int? @UserDefaultsBacked(key: UserDefaultKeys.isTracker.rawValue) private(set) var isTracker: Bool? - + let trackerRecordStore: TrackerRecordStoreProtocol convenience init() { diff --git a/Tracker/ViewModel/TrackerViewModel.swift b/Tracker/ViewModel/TrackerViewModel.swift index a4d6f95..56f0a70 100644 --- a/Tracker/ViewModel/TrackerViewModel.swift +++ b/Tracker/ViewModel/TrackerViewModel.swift @@ -31,7 +31,7 @@ protocol TrackerViewModelProtocol { func addPinnedCategory(id: UUID, nameCategory: String) -> Result func deleteAndGetPinnedCategory(id: UUID) -> Result - func allTrackers(date: Date) + func allTrackersByDate(date: Date) func getToDayTrackers(date: Date) func getNotCompleted(date: Date) func getCompleted(date: Date) @@ -50,6 +50,7 @@ final class TrackerViewModel { @Observableprivate(set) var filterState: FiltersState @UserDefaultsBacked(key: UserDefaultKeys.selectNameCategory.rawValue) private var selectNameCategory: String? @UserDefaultsBacked(key: UserDefaultKeys.selectFilter.rawValue) private var selectFilter: String? + @UserDefaultsBacked(key: UserDefaultKeys.isTracker.rawValue) private(set) var isTracker: Bool? private(set) var completedTrackers: Result, Error> @@ -242,7 +243,7 @@ extension TrackerViewModel: TrackerViewModelProtocol { } func loadTrackerRecord(id: UUID) -> Result { - trackerRecordStore.getCountTrackerRecordForDate(id: id) + trackerRecordStore.getCountTrackerRecord(id: id) } func addNewTracker(_ tracker: Tracker, nameCategory: String) -> Result { @@ -326,7 +327,7 @@ extension TrackerViewModel: TrackerViewModelProtocol { pinnedCategoryStore.deleteAndGetPinnedCategory(id) } - func allTrackers(date: Date) { + func allTrackersByDate(date: Date) { getShowListTrackersForDay(date: date) } From 6699f3059a030c973a92a9f86e79b7524859c621 Mon Sep 17 00:00:00 2001 From: Grifon Date: Tue, 2 Jan 2024 18:22:27 +0300 Subject: [PATCH 11/24] refactoring --- Tracker.xcodeproj/project.pbxproj | 8 +- .../xcdebugger/Breakpoints_v2.xcbkptlist | 728 ++++++++---------- Tracker/Application/SceneDelegate.swift | 12 +- .../Colors/Color.colorset/Contents.json | 38 + .../textEventColor.colorset/Contents.json | 38 + .../Model.xcdatamodel/contents | 1 + .../Domain/Store/TrackerCategoryStore.swift | 3 +- Tracker/Domain/Store/TrackerRecordStore.swift | 77 +- Tracker/Domain/Store/TrackerStore.swift | 25 +- Tracker/Extension/Date.swift | 4 +- Tracker/Extension/UIColor.swift | 3 + Tracker/Helpers/Colors.swift | 19 +- ...serDefaultKeys.swift => HelpersEnum.swift} | 5 + .../TabBarController/TabBarController.swift | 8 +- .../Cell/CreateTrackerTableViewCell.swift | 5 +- .../Cell/CustomTableViewCell.swift | 8 +- .../TrackersViews/Cell/StatisticView.swift | 31 +- .../Cell/TrackersCollectionViewCell.swift | 20 +- .../StatisticsViewController.swift | 4 +- .../SuplementaryView/HeaderReusableView.swift | 13 +- .../CreateTrackerViewController.swift | 60 +- .../EventSelectionViewController.swift | 9 +- .../FiltersViewController.swift | 6 +- .../NewCategoriViewController.swift | 17 +- .../ScheduleViewController.swift | 10 +- .../SelectCategoriesViewController.swift | 42 +- .../TrackersViewController.swift | 92 ++- Tracker/ViewModel/CategoryViewModel.swift | 23 +- Tracker/ViewModel/TrackerViewModel.swift | 180 ++--- 29 files changed, 820 insertions(+), 669 deletions(-) create mode 100644 Tracker/Assets.xcassets/Colors/Color.colorset/Contents.json create mode 100644 Tracker/Assets.xcassets/Colors/textEventColor.colorset/Contents.json rename Tracker/Models/Enum/{UserDefaultKeys.swift => HelpersEnum.swift} (69%) diff --git a/Tracker.xcodeproj/project.pbxproj b/Tracker.xcodeproj/project.pbxproj index fae143d..133d2f3 100644 --- a/Tracker.xcodeproj/project.pbxproj +++ b/Tracker.xcodeproj/project.pbxproj @@ -36,7 +36,7 @@ 1A704D3F2B2B76C60035C94D /* EmojiCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A704D3E2B2B76C60035C94D /* EmojiCollectionViewCell.swift */; }; 1A760A9D2B29EDC900B0C1CC /* EditTrackerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A760A9C2B29EDC900B0C1CC /* EditTrackerViewModel.swift */; }; 1A79C42D2B39C3E4009CE43A /* StatisticViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A79C42C2B39C3E4009CE43A /* StatisticViewModel.swift */; }; - 1A79C42F2B39FBCC009CE43A /* UserDefaultKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A79C42E2B39FBCC009CE43A /* UserDefaultKeys.swift */; }; + 1A79C42F2B39FBCC009CE43A /* HelpersEnum.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A79C42E2B39FBCC009CE43A /* HelpersEnum.swift */; }; 1A80951A2B3964A000591911 /* StatisticView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A8095192B3964A000591911 /* StatisticView.swift */; }; 1A83156E2B2C786000097DE7 /* HandlerResultType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A83156D2B2C786000097DE7 /* HandlerResultType.swift */; }; 1A84F3792AC87F4E001DD42F /* GeometricParams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A84F3782AC87F4E001DD42F /* GeometricParams.swift */; }; @@ -123,7 +123,7 @@ 1A704D3E2B2B76C60035C94D /* EmojiCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiCollectionViewCell.swift; sourceTree = ""; }; 1A760A9C2B29EDC900B0C1CC /* EditTrackerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditTrackerViewModel.swift; sourceTree = ""; }; 1A79C42C2B39C3E4009CE43A /* StatisticViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatisticViewModel.swift; sourceTree = ""; }; - 1A79C42E2B39FBCC009CE43A /* UserDefaultKeys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultKeys.swift; sourceTree = ""; }; + 1A79C42E2B39FBCC009CE43A /* HelpersEnum.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HelpersEnum.swift; sourceTree = ""; }; 1A8095192B3964A000591911 /* StatisticView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatisticView.swift; sourceTree = ""; }; 1A83156D2B2C786000097DE7 /* HandlerResultType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandlerResultType.swift; sourceTree = ""; }; 1A84F3782AC87F4E001DD42F /* GeometricParams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeometricParams.swift; sourceTree = ""; }; @@ -458,7 +458,7 @@ 1AE27C2C2B0604A800C4A925 /* Errors.swift */, 1A6724122B2F108000719A9D /* FiltersState.swift */, 1AC642732B38442E0072E081 /* EventAnalytics.swift */, - 1A79C42E2B39FBCC009CE43A /* UserDefaultKeys.swift */, + 1A79C42E2B39FBCC009CE43A /* HelpersEnum.swift */, ); path = Enum; sourceTree = ""; @@ -728,7 +728,7 @@ 1AAC05C32ACC595800AA4B6B /* CreateTrackerViewController.swift in Sources */, 1A21DD952ADE5A6A00583A01 /* DaysValueTransformer.swift in Sources */, 1A2F9B652AC82FBF00914B60 /* EventSelectionViewController.swift in Sources */, - 1A79C42F2B39FBCC009CE43A /* UserDefaultKeys.swift in Sources */, + 1A79C42F2B39FBCC009CE43A /* HelpersEnum.swift in Sources */, 1AA5B2372AD91D1800CB38A2 /* SelectCategoriesViewController.swift in Sources */, 1A79C42D2B39C3E4009CE43A /* StatisticViewModel.swift in Sources */, 1A8A7FE82AC55D970038CB42 /* SceneDelegate.swift in Sources */, diff --git a/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index 89c2734..6f4a457 100644 --- a/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -674,8 +674,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/ScheduleViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "149" - endingLineNumber = "149" + startingLineNumber = "151" + endingLineNumber = "151" landmarkName = "tableView(_:cellForRowAt:)" landmarkType = "7"> @@ -690,8 +690,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/ScheduleViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "179" - endingLineNumber = "179" + startingLineNumber = "181" + endingLineNumber = "181" landmarkName = "addDayInListkDay(cell:flag:)" landmarkType = "7"> @@ -706,8 +706,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/ScheduleViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "181" - endingLineNumber = "181" + startingLineNumber = "183" + endingLineNumber = "183" landmarkName = "addDayInListkDay(cell:flag:)" landmarkType = "7"> @@ -754,8 +754,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/ScheduleViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "182" - endingLineNumber = "182" + startingLineNumber = "184" + endingLineNumber = "184" landmarkName = "addDayInListkDay(cell:flag:)" landmarkType = "7"> @@ -802,8 +802,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/ScheduleViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "108" - endingLineNumber = "108" + startingLineNumber = "110" + endingLineNumber = "110" landmarkName = "updateListWeekDay(flag:day:)" landmarkType = "7"> @@ -818,8 +818,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/ScheduleViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "110" - endingLineNumber = "110" + startingLineNumber = "112" + endingLineNumber = "112" landmarkName = "updateListWeekDay(flag:day:)" landmarkType = "7"> @@ -834,8 +834,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/ScheduleViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "116" - endingLineNumber = "116" + startingLineNumber = "118" + endingLineNumber = "118" landmarkName = "updateListWeekDay(flag:day:)" landmarkType = "7"> @@ -850,8 +850,8 @@ filePath = "Tracker/TabBarController/TrackersViews/SuplementaryView/HeaderReusableView.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "18" - endingLineNumber = "18" + startingLineNumber = "19" + endingLineNumber = "19" landmarkName = "label" landmarkType = "24"> @@ -866,8 +866,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "583" - endingLineNumber = "583" + startingLineNumber = "611" + endingLineNumber = "611" landmarkName = "collectionView(_:viewForSupplementaryElementOfKind:at:)" landmarkType = "7"> @@ -1064,8 +1064,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "581" - endingLineNumber = "581" + startingLineNumber = "609" + endingLineNumber = "609" landmarkName = "collectionView(_:viewForSupplementaryElementOfKind:at:)" landmarkType = "7"> @@ -1172,8 +1172,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "566" - endingLineNumber = "566" + startingLineNumber = "594" + endingLineNumber = "594" landmarkName = "collectionView(_:layout:insetForSectionAt:)" landmarkType = "7"> @@ -1188,8 +1188,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "559" - endingLineNumber = "559" + startingLineNumber = "587" + endingLineNumber = "587" landmarkName = "collectionView(_:layout:sizeForItemAt:)" landmarkType = "7"> @@ -1204,8 +1204,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "580" - endingLineNumber = "580" + startingLineNumber = "608" + endingLineNumber = "608" landmarkName = "collectionView(_:viewForSupplementaryElementOfKind:at:)" landmarkType = "7"> @@ -1312,8 +1312,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "292" - endingLineNumber = "292" + startingLineNumber = "300" + endingLineNumber = "300" landmarkName = "registerCellAndHeader(collectionView:)" landmarkType = "7"> @@ -1328,8 +1328,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "575" - endingLineNumber = "575" + startingLineNumber = "603" + endingLineNumber = "603" landmarkName = "collectionView(_:layout:referenceSizeForHeaderInSection:)" landmarkType = "7"> @@ -1439,8 +1439,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/EventSelectionViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "144" - endingLineNumber = "144" + startingLineNumber = "143" + endingLineNumber = "143" landmarkName = "trackerViewControllerDidCancel(_:)" landmarkType = "7"> @@ -1455,8 +1455,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "642" - endingLineNumber = "642" + startingLineNumber = "678" + endingLineNumber = "678" landmarkName = "eventSelectionViewControllerDidCancel(_:)" landmarkType = "7"> @@ -1519,8 +1519,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "601" - endingLineNumber = "601" + startingLineNumber = "634" + endingLineNumber = "634" landmarkName = "updateSearchResults(for:)" landmarkType = "7"> @@ -1693,8 +1693,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/EventSelectionViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "143" - endingLineNumber = "143" + startingLineNumber = "142" + endingLineNumber = "142" landmarkName = "trackerViewControllerDidCancel(_:)" landmarkType = "7"> @@ -1803,8 +1803,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "608" - endingLineNumber = "608" + startingLineNumber = "642" + endingLineNumber = "642" landmarkName = "updateSearchResults(for:)" landmarkType = "7"> @@ -1851,8 +1851,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "562" - endingLineNumber = "562" + startingLineNumber = "590" + endingLineNumber = "590" landmarkName = "collectionView(_:layout:sizeForItemAt:)" landmarkType = "7"> @@ -2169,8 +2169,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "568" - endingLineNumber = "568" + startingLineNumber = "599" + endingLineNumber = "599" landmarkName = "collectionView(_:viewForSupplementaryElementOfKind:at:)" landmarkType = "7"> @@ -2876,8 +2876,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "609" - endingLineNumber = "609" + startingLineNumber = "643" + endingLineNumber = "643" landmarkName = "updateSearchResults(for:)" landmarkType = "7"> @@ -2924,8 +2924,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "425" - endingLineNumber = "425" + startingLineNumber = "456" + endingLineNumber = "456" landmarkName = "tableView(_:cellForRowAt:)" landmarkType = "7"> @@ -2940,8 +2940,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/ScheduleViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "98" - endingLineNumber = "98" + startingLineNumber = "100" + endingLineNumber = "100" landmarkName = "didTapDoneButton()" landmarkType = "7"> @@ -3004,8 +3004,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/NewCategoriViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "176" - endingLineNumber = "176" + startingLineNumber = "183" + endingLineNumber = "183" landmarkName = "textFieldDidEndEditing(_:)" landmarkType = "7"> @@ -3847,8 +3847,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "512" - endingLineNumber = "512" + startingLineNumber = "530" + endingLineNumber = "530" landmarkName = "collectionView(_:cellForItemAt:)" landmarkType = "7"> @@ -3863,8 +3863,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "538" - endingLineNumber = "538" + startingLineNumber = "566" + endingLineNumber = "566" landmarkName = "collectionView(_:contextMenuConfigurationForItemsAt:point:)" landmarkType = "7"> @@ -3879,8 +3879,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "527" - endingLineNumber = "527" + startingLineNumber = "555" + endingLineNumber = "555" landmarkName = "collectionView(_:contextMenuConfigurationForItemsAt:point:)" landmarkType = "7"> @@ -4011,99 +4011,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -4460,8 +4319,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "638" - endingLineNumber = "638" + startingLineNumber = "674" + endingLineNumber = "674" landmarkName = "eventSelectionViewController(vc:nameCategories:tracker:)" landmarkType = "7"> @@ -4634,8 +4493,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "126" - endingLineNumber = "126" + startingLineNumber = "129" + endingLineNumber = "129" landmarkName = "addNewTracker(_:nameCategory:)" landmarkType = "7"> @@ -4650,8 +4509,8 @@ filePath = "Tracker/TabBarController/TrackersViews/Cell/TrackersCollectionViewCell.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "141" - endingLineNumber = "141" + startingLineNumber = "143" + endingLineNumber = "143" landmarkName = "didTapAddButton()" landmarkType = "7"> @@ -4666,10 +4525,72 @@ filePath = "Tracker/Domain/Store/TrackerRecordStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "127" - endingLineNumber = "127" - landmarkName = "updateTrackerRecord(_:)" + startingLineNumber = "158" + endingLineNumber = "158" + landmarkName = "deleteOrCreateTrackerRecord(id:date:)" landmarkType = "7"> + + + + + + + + + + @@ -4825,6 +4746,21 @@ endingLineNumber = "179" offsetFromSymbolStart = "148"> + + @@ -4876,22 +4812,6 @@ - - - - @@ -4918,8 +4838,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "190" - endingLineNumber = "190" + startingLineNumber = "205" + endingLineNumber = "205" landmarkName = "addPinnedCategory(_:pinnedCategory:)" landmarkType = "7"> @@ -5169,8 +5089,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "236" - endingLineNumber = "236" + startingLineNumber = "245" + endingLineNumber = "245" landmarkName = "bind()" landmarkType = "7"> @@ -5185,8 +5105,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "247" - endingLineNumber = "247" + startingLineNumber = "256" + endingLineNumber = "256" landmarkName = "bind()" landmarkType = "7"> @@ -5248,8 +5168,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "252" - endingLineNumber = "252" + startingLineNumber = "261" + endingLineNumber = "261" landmarkName = "bind()" landmarkType = "7"> @@ -5264,8 +5184,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "233" - endingLineNumber = "233" + startingLineNumber = "242" + endingLineNumber = "242" landmarkName = "bind()" landmarkType = "7"> @@ -5280,9 +5200,9 @@ filePath = "Tracker/ViewModel/CategoryViewModel.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "43" - endingLineNumber = "43" - landmarkName = "init(trackerCategoryStore:trackerStore:category:)" + startingLineNumber = "47" + endingLineNumber = "47" + landmarkName = "init(trackerCategoryStore:trackerStore:trackerRecordStore:category:)" landmarkType = "7"> @@ -5296,8 +5216,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/ScheduleViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "99" - endingLineNumber = "99" + startingLineNumber = "101" + endingLineNumber = "101" landmarkName = "didTapDoneButton()" landmarkType = "7"> @@ -5312,9 +5232,9 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "603" - endingLineNumber = "603" - landmarkName = "editTracker(vc:editTracker:nameCategory:indexPath:)" + startingLineNumber = "635" + endingLineNumber = "635" + landmarkName = "editTracker(vc:editTracker:count:nameCategory:indexPath:)" landmarkType = "7"> @@ -5328,8 +5248,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "546" - endingLineNumber = "546" + startingLineNumber = "577" + endingLineNumber = "577" landmarkName = "collectionView(_:layout:sizeForItemAt:)" landmarkType = "7"> @@ -5344,8 +5264,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "559" - endingLineNumber = "559" + startingLineNumber = "590" + endingLineNumber = "590" landmarkName = "collectionView(_:layout:referenceSizeForHeaderInSection:)" landmarkType = "7"> @@ -5360,8 +5280,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "570" - endingLineNumber = "570" + startingLineNumber = "601" + endingLineNumber = "601" landmarkName = "collectionView(_:viewForSupplementaryElementOfKind:at:)" landmarkType = "7"> @@ -5408,8 +5328,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "573" - endingLineNumber = "573" + startingLineNumber = "604" + endingLineNumber = "604" landmarkName = "collectionView(_:viewForSupplementaryElementOfKind:at:)" landmarkType = "7"> @@ -5456,8 +5376,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "518" - endingLineNumber = "518" + startingLineNumber = "549" + endingLineNumber = "549" landmarkName = "collectionView(_:didSelectItemAt:)" landmarkType = "7"> @@ -5472,8 +5392,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "525" - endingLineNumber = "525" + startingLineNumber = "556" + endingLineNumber = "556" landmarkName = "collectionView(_:didSelectItemAt:)" landmarkType = "7"> @@ -5488,8 +5408,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "493" - endingLineNumber = "493" + startingLineNumber = "524" + endingLineNumber = "524" landmarkName = "collectionView(_:cellForItemAt:)" landmarkType = "7"> @@ -5520,9 +5440,9 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "607" - endingLineNumber = "607" - landmarkName = "editTracker(vc:editTracker:nameCategory:indexPath:)" + startingLineNumber = "639" + endingLineNumber = "639" + landmarkName = "editTracker(vc:editTracker:count:nameCategory:indexPath:)" landmarkType = "7"> @@ -5599,8 +5519,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "538" - endingLineNumber = "538" + startingLineNumber = "569" + endingLineNumber = "569" landmarkName = "collectionView(_:didDeselectItemAt:)" landmarkType = "7"> @@ -5615,8 +5535,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "103" - endingLineNumber = "103" + startingLineNumber = "105" + endingLineNumber = "105" landmarkName = "searchTracker(id:)" landmarkType = "7"> @@ -5663,8 +5583,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "101" - endingLineNumber = "101" + startingLineNumber = "103" + endingLineNumber = "103" landmarkName = "searchTracker(id:)" landmarkType = "7"> @@ -5679,8 +5599,8 @@ filePath = "Tracker/ViewModel/TrackerViewModel.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "372" - endingLineNumber = "372" + startingLineNumber = "354" + endingLineNumber = "354" landmarkName = "storeCategory(trackerCategoryStore:)" landmarkType = "7"> @@ -5695,8 +5615,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "650" - endingLineNumber = "650" + startingLineNumber = "686" + endingLineNumber = "686" landmarkName = "trackerViewController(vc:nameCategory:tracker:)" landmarkType = "7"> @@ -5803,8 +5723,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "205" - endingLineNumber = "205" + startingLineNumber = "220" + endingLineNumber = "220" landmarkName = "controllerDidChangeContent(_:)" landmarkType = "7"> @@ -5896,8 +5816,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "276" - endingLineNumber = "276" + startingLineNumber = "285" + endingLineNumber = "285" landmarkName = "didTapGreateButton()" landmarkType = "7"> @@ -6184,8 +6104,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "152" - endingLineNumber = "152" + startingLineNumber = "155" + endingLineNumber = "155" landmarkName = "updateTracker(tracker:nameCategory:)" landmarkType = "7"> @@ -6277,8 +6197,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "140" - endingLineNumber = "140" + startingLineNumber = "143" + endingLineNumber = "143" landmarkName = "updateTracker(tracker:nameCategory:)" landmarkType = "7"> @@ -6355,8 +6275,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "291" - endingLineNumber = "291" + startingLineNumber = "300" + endingLineNumber = "300" landmarkName = "didTapGreateButton()" landmarkType = "7"> @@ -6403,8 +6323,8 @@ filePath = "Tracker/ViewModel/TrackerViewModel.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "255" - endingLineNumber = "255" + startingLineNumber = "251" + endingLineNumber = "251" landmarkName = "updateTracker(tracker:nameCategory:)" landmarkType = "7"> @@ -6419,8 +6339,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "136" - endingLineNumber = "136" + startingLineNumber = "139" + endingLineNumber = "139" landmarkName = "updateTracker(tracker:nameCategory:)" landmarkType = "7"> @@ -6435,8 +6355,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "283" - endingLineNumber = "283" + startingLineNumber = "292" + endingLineNumber = "292" landmarkName = "didTapGreateButton()" landmarkType = "7"> @@ -6451,8 +6371,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "295" - endingLineNumber = "295" + startingLineNumber = "304" + endingLineNumber = "304" landmarkName = "didTapGreateButton()" landmarkType = "7"> @@ -6467,10 +6387,42 @@ filePath = "Tracker/TabBarController/TrackersViews/Cell/CustomTableViewCell.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "68" - endingLineNumber = "68" + startingLineNumber = "74" + endingLineNumber = "74" landmarkName = "setupCornerRadius(cornerRadius:maskedCorners:)" landmarkType = "7"> + + + + + + @@ -6656,8 +6608,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "211" - endingLineNumber = "211" + startingLineNumber = "219" + endingLineNumber = "219" landmarkName = "bind()" landmarkType = "7"> @@ -6672,8 +6624,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "216" - endingLineNumber = "216" + startingLineNumber = "224" + endingLineNumber = "224" landmarkName = "bind()" landmarkType = "7"> @@ -6688,10 +6640,42 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "214" - endingLineNumber = "214" + startingLineNumber = "222" + endingLineNumber = "222" landmarkName = "bind()" landmarkType = "7"> + + + + + + - - - - - - - - @@ -6815,8 +6767,8 @@ filePath = "Tracker/TabBarController/TrackersViews/Cell/TrackersCollectionViewCell.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "154" - endingLineNumber = "154" + startingLineNumber = "156" + endingLineNumber = "156" landmarkName = "updateLableCountAndImageAddButton(_:)" landmarkType = "7"> @@ -6831,60 +6783,12 @@ filePath = "Tracker/TabBarController/TrackersViews/Cell/TrackersCollectionViewCell.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "157" - endingLineNumber = "157" + startingLineNumber = "159" + endingLineNumber = "159" landmarkName = "updateLableCountAndImageAddButton(_:)" landmarkType = "7"> - - - - - - - - - - @@ -6911,8 +6815,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "262" - endingLineNumber = "262" + startingLineNumber = "270" + endingLineNumber = "270" landmarkName = "filterState(state:)" landmarkType = "7"> @@ -6927,8 +6831,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "268" - endingLineNumber = "268" + startingLineNumber = "276" + endingLineNumber = "276" landmarkName = "filterState(state:)" landmarkType = "7"> @@ -6943,8 +6847,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "265" - endingLineNumber = "265" + startingLineNumber = "273" + endingLineNumber = "273" landmarkName = "filterState(state:)" landmarkType = "7"> @@ -6991,8 +6895,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "186" - endingLineNumber = "186" + startingLineNumber = "190" + endingLineNumber = "190" landmarkName = "bind()" landmarkType = "7"> @@ -7007,8 +6911,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/FiltersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "124" - endingLineNumber = "124" + startingLineNumber = "128" + endingLineNumber = "128" landmarkName = "tableView(_:didSelectRowAt:)" landmarkType = "7"> @@ -7023,8 +6927,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "32" - endingLineNumber = "32" + startingLineNumber = "35" + endingLineNumber = "35" landmarkName = "fetchedTrackerResultController" landmarkType = "24"> @@ -7039,8 +6943,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "34" - endingLineNumber = "34" + startingLineNumber = "36" + endingLineNumber = "36" landmarkName = "fetchedTrackerResultController" landmarkType = "24"> diff --git a/Tracker/Application/SceneDelegate.swift b/Tracker/Application/SceneDelegate.swift index 05594d4..3f89076 100644 --- a/Tracker/Application/SceneDelegate.swift +++ b/Tracker/Application/SceneDelegate.swift @@ -8,7 +8,7 @@ import UIKit class SceneDelegate: UIResponder, UIWindowSceneDelegate { - + var window: UIWindow? @UserDefaultsBacked(key: "is_onboarding") private var isOnboarding @@ -32,29 +32,29 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { window?.rootViewController = rootViewController isOnboarding = true } - + func sceneDidDisconnect(_ scene: UIScene) { // Called as the scene is being released by the system. // This occurs shortly after the scene enters the background, or when its session is discarded. // Release any resources associated with this scene that can be re-created the next time the scene connects. // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). } - + func sceneDidBecomeActive(_ scene: UIScene) { // Called when the scene has moved from an inactive state to an active state. // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. } - + func sceneWillResignActive(_ scene: UIScene) { // Called when the scene will move from an active state to an inactive state. // This may occur due to temporary interruptions (ex. an incoming phone call). } - + func sceneWillEnterForeground(_ scene: UIScene) { // Called as the scene transitions from the background to the foreground. // Use this method to undo the changes made on entering the background. } - + func sceneDidEnterBackground(_ scene: UIScene) { // Called as the scene transitions from the foreground to the background. // Use this method to save data, release shared resources, and store enough scene-specific state information diff --git a/Tracker/Assets.xcassets/Colors/Color.colorset/Contents.json b/Tracker/Assets.xcassets/Colors/Color.colorset/Contents.json new file mode 100644 index 0000000..22c4bb0 --- /dev/null +++ b/Tracker/Assets.xcassets/Colors/Color.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "1.000", + "green" : "1.000", + "red" : "1.000" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "1.000", + "green" : "1.000", + "red" : "1.000" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tracker/Assets.xcassets/Colors/textEventColor.colorset/Contents.json b/Tracker/Assets.xcassets/Colors/textEventColor.colorset/Contents.json new file mode 100644 index 0000000..c74053a --- /dev/null +++ b/Tracker/Assets.xcassets/Colors/textEventColor.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "1.000", + "green" : "1.000", + "red" : "1.000" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.133", + "green" : "0.106", + "red" : "0.102" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tracker/CoreData/Model.xcdatamodeld/Model.xcdatamodel/contents b/Tracker/CoreData/Model.xcdatamodeld/Model.xcdatamodel/contents index e379a24..6ed526e 100644 --- a/Tracker/CoreData/Model.xcdatamodeld/Model.xcdatamodel/contents +++ b/Tracker/CoreData/Model.xcdatamodeld/Model.xcdatamodel/contents @@ -22,6 +22,7 @@ + \ No newline at end of file diff --git a/Tracker/Domain/Store/TrackerCategoryStore.swift b/Tracker/Domain/Store/TrackerCategoryStore.swift index 3125ce2..8e1ba99 100644 --- a/Tracker/Domain/Store/TrackerCategoryStore.swift +++ b/Tracker/Domain/Store/TrackerCategoryStore.swift @@ -8,6 +8,7 @@ import Foundation import CoreData +//MARK: - TrackerCategoryStoreProtocol protocol TrackerCategoryStoreProtocol { func addCategory(_ nameCategory: String) -> Result func getListTrackerCategoryCoreData() -> [TrackerCategoryCoreData]? @@ -15,6 +16,7 @@ protocol TrackerCategoryStoreProtocol { func updateNameCategory(newNameCategory: String, oldNameCategory: String) -> Result } +//MARK: - TrackerCategoryStoreDelegate protocol TrackerCategoryStoreDelegate: AnyObject { func storeCategory(trackerCategoryStore: TrackerCategoryStoreProtocol) } @@ -93,7 +95,6 @@ extension TrackerCategoryStore: TrackerCategoryStoreProtocol { do { if let category = try searchTrackerCategoryCD(nameCategory: nameCategory) { context.delete(category) - category.trakers = nil return save() } } catch { diff --git a/Tracker/Domain/Store/TrackerRecordStore.swift b/Tracker/Domain/Store/TrackerRecordStore.swift index fa45773..eb5b458 100644 --- a/Tracker/Domain/Store/TrackerRecordStore.swift +++ b/Tracker/Domain/Store/TrackerRecordStore.swift @@ -8,13 +8,18 @@ import Foundation import CoreData +//MARK: - TrackerRecordStoreProtocol protocol TrackerRecordStoreProtocol { - func updateTrackerRecord(_ trackerRecord: TrackerRecord) -> Result + func deleteOrCreateTrackerRecord(id: UUID, date: Date) -> Result func getCountTrackerRecord(id: UUID) -> Result func treckersRecordsResult() -> Result, Error> + func getIsTrackerRecord(id: UUID, date: Date) throws -> Bool func getCountTrackerComplet() -> Int? + func deleteTrackerRecord(id: UUID) -> Result + func deleteTrackerRecords(trackers: [Tracker]) -> Result } +//MARK: - TrackerRecordStoreDelegate protocol TrackerRecordStoreDelegate: AnyObject { func trackerRecordStore(trackerRecordStore: TrackerRecordStoreProtocol) } @@ -24,7 +29,7 @@ final class TrackerRecordStore: NSObject { weak var delegate: TrackerRecordStoreDelegate? private let context: NSManagedObjectContext - + private lazy var fetchedTrackerRecordResultController: NSFetchedResultsController = { let request = TrackerRecordCoreData.fetchRequest() request.sortDescriptors = [NSSortDescriptor(keyPath: \TrackerRecordCoreData.date, ascending: true)] @@ -48,6 +53,7 @@ final class TrackerRecordStore: NSObject { } } +//MARK: - private extension private extension TrackerRecordStore { func save() -> Result { do { @@ -70,14 +76,14 @@ private extension TrackerRecordStore { return trackerRecord } - func updateExistingTrackerRecord(_ trackerRecordCoreData: TrackerRecordCoreData, trackerRecord: TrackerRecord) { - trackerRecordCoreData.trackerId = trackerRecord.id - trackerRecordCoreData.date = trackerRecord.date + func updateExistingTrackerRecord(_ trackerRecordCoreData: TrackerRecordCoreData, id: UUID, date: Date) { + trackerRecordCoreData.trackerId = id + trackerRecordCoreData.date = date } - func addNewTrackerRecord(_ trackerRecord: TrackerRecord) -> Result { + func addNewTrackerRecord(id: UUID, date: Date) -> Result { let trackerRecordCoreData = TrackerRecordCoreData(context: context) - updateExistingTrackerRecord(trackerRecordCoreData, trackerRecord: trackerRecord) + updateExistingTrackerRecord(trackerRecordCoreData, id: id, date: date) return save() } @@ -86,24 +92,26 @@ private extension TrackerRecordStore { return save() } - func searchTrackerRecordForDate(trackerRecord: TrackerRecord) throws -> TrackerRecordCoreData? { + func searchTrackerRecordForDate(id: UUID, date: Date) throws -> TrackerRecordCoreData? { let request = NSFetchRequest(entityName: "\(TrackerRecordCoreData.self)") request.returnsObjectsAsFaults = false request.predicate = NSPredicate(format: "%K == %@ AND %K == %@", #keyPath(TrackerRecordCoreData.date), - trackerRecord.date as CVarArg, + date as CVarArg, #keyPath(TrackerRecordCoreData.trackerId), - trackerRecord.id as CVarArg) - return try context.fetch(request).first + id as CVarArg) + let trackerRecord = try context.fetch(request) + + return trackerRecord.first } - func getCountTrackersRecord(id: UUID) throws -> Int? { + func getLisTrackersRecord(id: UUID) throws -> [TrackerRecordCoreData] { let request = NSFetchRequest(entityName: "\(TrackerRecordCoreData.self)") request.returnsObjectsAsFaults = false request.predicate = NSPredicate(format: "%K == %@", #keyPath(TrackerRecordCoreData.trackerId), id as CVarArg) - return try context.fetch(request).count + return try context.fetch(request) } } @@ -121,12 +129,35 @@ extension TrackerRecordStore: TrackerRecordStoreProtocol { } } - func updateTrackerRecord(_ trackerRecord: TrackerRecord) -> Result { + func deleteTrackerRecord(id: UUID) -> Result { do { - if let trackerRecord = try searchTrackerRecordForDate(trackerRecord: trackerRecord) { + let trackerRecord = try getLisTrackersRecord(id: id) + trackerRecord.forEach { context.delete($0) } + return save() + } + catch { + return .failure(error) + } + } + + func deleteTrackerRecords(trackers: [Tracker]) -> Result { + do { + try trackers.forEach { + let trackerRecords = try getLisTrackersRecord(id: $0.id) + trackerRecords.forEach { context.delete($0) } + } + } catch { + return .failure(error) + } + return save() + } + + func deleteOrCreateTrackerRecord(id: UUID, date: Date) -> Result { + do { + if let trackerRecord = try searchTrackerRecordForDate(id: id, date: date) { return deleteTrackerRecord(trackerRecord) } else { - return addNewTrackerRecord(trackerRecord) + return addNewTrackerRecord(id: id, date: date) } } catch { @@ -136,19 +167,25 @@ extension TrackerRecordStore: TrackerRecordStoreProtocol { func getCountTrackerRecord(id: UUID) -> Result { do { - if let countTrackers = try getCountTrackersRecord(id: id) { - return .success(countTrackers) - } + let countTrackers = try getLisTrackersRecord(id: id).count + return .success(countTrackers) } catch { return .failure(error) } - return .failure(StoreErrors.TrackrerRecordStoreError.loadTrackerRecord) } func getCountTrackerComplet() -> Int? { fetchedTrackerRecordResultController.fetchedObjects?.count } + func getIsTrackerRecord(id: UUID, date: Date) throws -> Bool { + do { + let tracker = try searchTrackerRecordForDate(id: id, date: date) + return tracker != nil + } catch { + throw error + } + } } //MARK: - NSFetchedResultsControllerDelegate diff --git a/Tracker/Domain/Store/TrackerStore.swift b/Tracker/Domain/Store/TrackerStore.swift index e9bcb2d..d261550 100644 --- a/Tracker/Domain/Store/TrackerStore.swift +++ b/Tracker/Domain/Store/TrackerStore.swift @@ -8,14 +8,17 @@ import Foundation import CoreData +//MARK: - TrackerStoreProtocol protocol TrackerStoreProtocol { func addNewTracker(_ tracker: Tracker, nameCategory: String) -> Result func getTrackers(_ objects: [TrackerCoreData]) -> Result<[Tracker], Error> func updateTracker(tracker: Tracker, nameCategory: String) -> Result func deleteTracker(_ id: UUID) -> Result + func deleteTrackers(trackers: [Tracker]) -> Result func addPinnedCategory(_ id: UUID, pinnedCategory: PinnCategoryCoreData) -> Result } +//MARK: - TrackerStoreDelegate protocol TrackerStoreDelegate: AnyObject { func updateTracker(_ trackerCoreData: TrackerStoreProtocol) } @@ -30,7 +33,6 @@ final class TrackerStore: NSObject { private lazy var fetchedTrackerResultController: NSFetchedResultsController = { let request = TrackerCoreData.fetchRequest() let sortName = NSSortDescriptor(keyPath: \TrackerCoreData.name, ascending: true) - let sortId = NSSortDescriptor(keyPath: \TrackerCoreData.id, ascending: true) request.sortDescriptors = [sortName] let fetchedResultController = NSFetchedResultsController(fetchRequest: request, managedObjectContext: context, @@ -52,7 +54,7 @@ final class TrackerStore: NSObject { } } -//MARK: - Private +//MARK: - Private extension private extension TrackerStore { func tracker(from trackerCoreData: TrackerCoreData) throws -> Tracker { guard let id = trackerCoreData.id else { @@ -102,7 +104,8 @@ private extension TrackerStore { guard let keyPath = (\TrackerCoreData.id)._kvcKeyPathString else { throw StoreErrors.TrackrerStoreError.getTrackerError } request.predicate = NSPredicate(format: "%K == %@", keyPath, id as CVarArg) - return try context.fetch(request).first + let trackerCD = try context.fetch(request) + return trackerCD.first } func searchCategory(name: String) throws -> TrackerCategoryCoreData? { @@ -174,6 +177,18 @@ extension TrackerStore: TrackerStoreProtocol { return save() } + func deleteTrackers(trackers: [Tracker]) -> Result { + do { try trackers .forEach { + if let tracker = try searchTracker(id: $0.id) { + context.delete(tracker) + } + } + return save() + } catch { + return .failure(error) + } + } + func getTrackers(_ objects: [TrackerCoreData]) -> Result<[Tracker], Error> { let _ = fetchedTrackerResultController do { @@ -183,13 +198,12 @@ extension TrackerStore: TrackerStoreProtocol { return .failure(error) } } - + func addPinnedCategory(_ id: UUID, pinnedCategory: PinnCategoryCoreData) -> Result { do { if let trackerCD = try searchTracker(id: id) { pinnedCategory.addToTracker(trackerCD) pinnedCategory.trackerId = id - return save() } } catch { @@ -199,6 +213,7 @@ extension TrackerStore: TrackerStoreProtocol { } } +//MARK: - NSFetchedResultsControllerDelegate extension TrackerStore: NSFetchedResultsControllerDelegate { func controllerDidChangeContent(_ controller: NSFetchedResultsController) { guard let delegate else { return } diff --git a/Tracker/Extension/Date.swift b/Tracker/Extension/Date.swift index 294b4b6..bf53bb3 100644 --- a/Tracker/Extension/Date.swift +++ b/Tracker/Extension/Date.swift @@ -2,13 +2,13 @@ // Date.swift // Tracker // -// Created by Марина Машук on 15.10.23. +// Created by Григорий Машук on 15.10.23. // import Foundation extension Date { - var ignoringTime: Date? { + var ignoringTime: Date { let dateComponents = Calendar.current.dateComponents([.day, .month, .year], from: self) return Calendar.current.date(from: dateComponents) ?? Date() } diff --git a/Tracker/Extension/UIColor.swift b/Tracker/Extension/UIColor.swift index a468d5c..c165cc5 100644 --- a/Tracker/Extension/UIColor.swift +++ b/Tracker/Extension/UIColor.swift @@ -41,4 +41,7 @@ extension UIColor { static var redGradient: UIColor {UIColor(named: "redGradient") ?? .red} static var greenGradient: UIColor {UIColor(named: "greenGradient") ?? .green} static var blueGradient: UIColor {UIColor(named: "blueGradient") ?? .blue} + + static var textEventColor: UIColor {UIColor(named: "textEventColor") ?? .gray} + static var separatorColor: UIColor {UIColor(named: "separatorColor") ?? .gray} } diff --git a/Tracker/Helpers/Colors.swift b/Tracker/Helpers/Colors.swift index 65b125e..a5a75f7 100644 --- a/Tracker/Helpers/Colors.swift +++ b/Tracker/Helpers/Colors.swift @@ -8,17 +8,26 @@ import UIKit final class Colors { - let viewBackground = UIColor.systemBackground + lazy var viewBackground = UIColor.systemBackground - let buttonDisabledColor = UIColor { (traits: UITraitCollection) -> UIColor in + lazy var buttonDisabledColor = UIColor { (traits: UITraitCollection) -> UIColor in if traits.userInterfaceStyle == .light { - return UIColor.lightGray + return UIColor.blackDay } else { - return UIColor(red: 0.8, green: 0.5, blue: 0.8, alpha: 1) + return UIColor.whiteDay +// return UIColor(red: 0.8, green: 0.5, blue: 0.8, alpha: 1) } } - let whiteBlackItemColor = UIColor { (traits: UITraitCollection) -> UIColor in + lazy var buttonEventColor = UIColor { (traits: UITraitCollection) -> UIColor in + if traits.userInterfaceStyle == .light { + return UIColor.blackDay + } else { + return UIColor.whiteDay + } + } + + lazy var whiteBlackItemColor = UIColor { (traits: UITraitCollection) -> UIColor in if traits.userInterfaceStyle == .light { return UIColor.black } else { diff --git a/Tracker/Models/Enum/UserDefaultKeys.swift b/Tracker/Models/Enum/HelpersEnum.swift similarity index 69% rename from Tracker/Models/Enum/UserDefaultKeys.swift rename to Tracker/Models/Enum/HelpersEnum.swift index ee5dcbe..fd79b69 100644 --- a/Tracker/Models/Enum/UserDefaultKeys.swift +++ b/Tracker/Models/Enum/HelpersEnum.swift @@ -12,3 +12,8 @@ enum UserDefaultKeys: String { case selectNameCategory case selectFilter } + +enum Fixed: String { + case fixedEng = "Fixed" + case fixedRu = "Закрепленные" +} diff --git a/Tracker/TabBarController/TabBarController.swift b/Tracker/TabBarController/TabBarController.swift index 40d69c7..bd60a30 100644 --- a/Tracker/TabBarController/TabBarController.swift +++ b/Tracker/TabBarController/TabBarController.swift @@ -23,7 +23,8 @@ final class TabBarController: UITabBarController { override func viewDidLoad() { super.viewDidLoad() - setupTabBar() + tabBar.layer.borderWidth = 1 + tabBar.clipsToBounds = true let trackerViewController = TrackersViewController(viewModel: TrackerViewModel()) let statisticsViewController = StatisticsViewController() @@ -74,9 +75,4 @@ private extension TabBarController { return vc } - - func setupTabBar() { - tabBar.layer.borderWidth = 1 - tabBar.clipsToBounds = true - } } diff --git a/Tracker/TabBarController/TrackersViews/Cell/CreateTrackerTableViewCell.swift b/Tracker/TabBarController/TrackersViews/Cell/CreateTrackerTableViewCell.swift index db975f8..cd44119 100644 --- a/Tracker/TabBarController/TrackersViews/Cell/CreateTrackerTableViewCell.swift +++ b/Tracker/TabBarController/TrackersViews/Cell/CreateTrackerTableViewCell.swift @@ -15,10 +15,11 @@ class CreateTrackerTableViewCell: UITableViewCell { static let choiceButtonSize = CGSize(width: 44, height: 44) } + private let colors = Colors() + private lazy var lableView: UILabel = { let lableView = UILabel() lableView.font = ConstantsCreateCell.lableFont - lableView.textColor = .blackDay return lableView }() @@ -51,6 +52,7 @@ class CreateTrackerTableViewCell: UITableViewCell { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) selectionStyle = .none + backgroundColor = colors.viewBackground setupUIElement() } @@ -58,6 +60,7 @@ class CreateTrackerTableViewCell: UITableViewCell { super.init(coder: coder) assertionFailure("init(coder:) has not been implemented") } + } extension CreateTrackerTableViewCell { diff --git a/Tracker/TabBarController/TrackersViews/Cell/CustomTableViewCell.swift b/Tracker/TabBarController/TrackersViews/Cell/CustomTableViewCell.swift index d6aa948..eafa9b8 100644 --- a/Tracker/TabBarController/TrackersViews/Cell/CustomTableViewCell.swift +++ b/Tracker/TabBarController/TrackersViews/Cell/CustomTableViewCell.swift @@ -15,10 +15,11 @@ final class CustomTableViewCell: UITableViewCell { static let lableFont = UIFont.systemFont(ofSize: 17, weight: .regular) } + private let colors = Colors() + private lazy var nameCategoriLableView: UILabel = { let nameCategoriLableView = UILabel() nameCategoriLableView.font = ConstantsGreateCell.lableFont - nameCategoriLableView.textColor = .blackDay return nameCategoriLableView }() @@ -37,6 +38,7 @@ final class CustomTableViewCell: UITableViewCell { super.init(style: style, reuseIdentifier: reuseIdentifier) selectionStyle = .none layer.masksToBounds = true + backgroundColor = colors.viewBackground setupLable() setupSelectedImage() } @@ -48,6 +50,10 @@ final class CustomTableViewCell: UITableViewCell { override func prepareForReuse() { super.prepareForReuse() layer.cornerRadius = 0 + layer.maskedCorners = [.layerMinXMinYCorner, + .layerMinXMaxYCorner, + .layerMaxXMinYCorner, + .layerMaxXMaxYCorner] } } diff --git a/Tracker/TabBarController/TrackersViews/Cell/StatisticView.swift b/Tracker/TabBarController/TrackersViews/Cell/StatisticView.swift index eae91b0..9f0a312 100644 --- a/Tracker/TabBarController/TrackersViews/Cell/StatisticView.swift +++ b/Tracker/TabBarController/TrackersViews/Cell/StatisticView.swift @@ -15,12 +15,11 @@ final class StatisticsView: UIView { static let borderWidth = CGFloat(1) } + private let colors = Colors() + private lazy var lableHeader: UILabel = { let lableHeader = UILabel() lableHeader.font = ConstantsStatistics.fontLabelHeader - lableHeader.textColor = .blackDay - lableHeader.translatesAutoresizingMaskIntoConstraints = false - lableHeader.backgroundColor = .clear return lableHeader }() @@ -28,23 +27,14 @@ final class StatisticsView: UIView { private lazy var secondaryTextLable: UILabel = { let secondaryTextLable = UILabel() secondaryTextLable.font = ConstantsStatistics.lableFont - secondaryTextLable.textColor = .blackDay - lableStackView.translatesAutoresizingMaskIntoConstraints = false return secondaryTextLable }() - private lazy var lableStackView: UIStackView = { - let lableStackView = UIStackView() - lableStackView.translatesAutoresizingMaskIntoConstraints = false - lableStackView.axis = .vertical - - return lableStackView - }() - override init(frame: CGRect) { super.init(frame: frame) - setupSteckView() + setupView() + backgroundColor = colors.viewBackground } required init?(coder: NSCoder) { @@ -53,17 +43,20 @@ final class StatisticsView: UIView { } extension StatisticsView { - private func setupSteckView() { - addSubview(lableStackView) + private func setupView() { [lableHeader, secondaryTextLable].forEach { $0.backgroundColor = .clear $0.translatesAutoresizingMaskIntoConstraints = false - lableStackView.addArrangedSubview($0) + addSubview($0) } + let indentation = CGFloat(12) + NSLayoutConstraint.activate([ - lableStackView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 12), - lableStackView.centerYAnchor.constraint(equalTo: centerYAnchor) + lableHeader.topAnchor.constraint(equalTo: topAnchor, constant: indentation), + lableHeader.leadingAnchor.constraint(equalTo: leadingAnchor, constant: indentation), + secondaryTextLable.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -indentation), + secondaryTextLable.leadingAnchor.constraint(equalTo: leadingAnchor, constant: indentation) ]) } diff --git a/Tracker/TabBarController/TrackersViews/Cell/TrackersCollectionViewCell.swift b/Tracker/TabBarController/TrackersViews/Cell/TrackersCollectionViewCell.swift index ab5ffd2..ae5383c 100644 --- a/Tracker/TabBarController/TrackersViews/Cell/TrackersCollectionViewCell.swift +++ b/Tracker/TabBarController/TrackersViews/Cell/TrackersCollectionViewCell.swift @@ -20,6 +20,8 @@ final class TrackersCollectionViewCell: UICollectionViewCell { static let isPinnedImage = "IsPinned" static let numberOfDays = NSLocalizedString("numberOfDays", tableName: "LocalizableDict", comment: "") + static let numberOfLines = 2 + static let cornerRadiusColorView = CGFloat(16) static let borderWidthColorView = CGFloat(1) @@ -67,7 +69,7 @@ final class TrackersCollectionViewCell: UICollectionViewCell { private lazy var nameTrackerLabel: UILabel = { let nameTrackerLabel = UILabel() - nameTrackerLabel.numberOfLines = 2 + nameTrackerLabel.numberOfLines = ConstantsTrackerCell.numberOfLines nameTrackerLabel.font = ConstantsTrackerCell.fontTextLable nameTrackerLabel.textColor = .white nameTrackerLabel.textAlignment = .justified @@ -242,18 +244,20 @@ extension TrackersCollectionViewCell { colorView.addSubview($0) } + let indentation = CGFloat(12) + NSLayoutConstraint.activate([ - emojiLable.topAnchor.constraint(equalTo: colorView.topAnchor, constant: 12), - emojiLable.leadingAnchor.constraint(equalTo: colorView.leadingAnchor, constant: 12), + emojiLable.topAnchor.constraint(equalTo: colorView.topAnchor, constant: indentation), + emojiLable.leadingAnchor.constraint(equalTo: colorView.leadingAnchor, constant: indentation), emojiLable.heightAnchor.constraint(equalToConstant: ConstantsTrackerCell.sizeLableView.height), emojiLable.widthAnchor.constraint(equalToConstant: ConstantsTrackerCell.sizeLableView.width), - nameTrackerLabel.leadingAnchor.constraint(equalTo: colorView.leadingAnchor, constant: 12), - nameTrackerLabel.trailingAnchor.constraint(equalTo: colorView.trailingAnchor, constant: -12), - nameTrackerLabel.bottomAnchor.constraint(equalTo: colorView.bottomAnchor, constant: -12), + nameTrackerLabel.leadingAnchor.constraint(equalTo: colorView.leadingAnchor, constant: indentation), + nameTrackerLabel.trailingAnchor.constraint(equalTo: colorView.trailingAnchor, constant: -indentation), + nameTrackerLabel.bottomAnchor.constraint(equalTo: colorView.bottomAnchor, constant: -indentation), - isPinnedImageView.topAnchor.constraint(equalTo: colorView.topAnchor, constant: 12), - isPinnedImageView.trailingAnchor.constraint(equalTo: colorView.trailingAnchor), + isPinnedImageView.topAnchor.constraint(equalTo: colorView.topAnchor, constant: indentation), + isPinnedImageView.trailingAnchor.constraint(equalTo: colorView.trailingAnchor, constant: -6), isPinnedImageView.heightAnchor.constraint(equalToConstant: ConstantsTrackerCell.sizeIsPinnedImage.height), isPinnedImageView.widthAnchor.constraint(equalToConstant: ConstantsTrackerCell.sizeIsPinnedImage.width) ]) diff --git a/Tracker/TabBarController/TrackersViews/StatisticViews/StatisticsViewController.swift b/Tracker/TabBarController/TrackersViews/StatisticViews/StatisticsViewController.swift index 618168d..1359da3 100644 --- a/Tracker/TabBarController/TrackersViews/StatisticViews/StatisticsViewController.swift +++ b/Tracker/TabBarController/TrackersViews/StatisticViews/StatisticsViewController.swift @@ -22,12 +22,12 @@ class StatisticsViewController: UIViewController { } private var viewModel: StatisticsViewModelProtocol? + private let colors = Colors() private lazy var labelHeader: UILabel = { let labelHeader = UILabel() labelHeader.text = ConstantsStatisticVC.headerStatisticText labelHeader.font = ConstantsStatisticVC.fontLabelHeader - labelHeader.textColor = .blackDay labelHeader.translatesAutoresizingMaskIntoConstraints = false labelHeader.backgroundColor = .clear @@ -60,11 +60,11 @@ class StatisticsViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() + view.backgroundColor = colors.viewBackground viewModel = StatisticsViewModel() showStabView(flag: viewModel?.getIsTracker()) bind() cardStatysticsView.setCount(count: viewModel?.getCountTrackerComplet() ?? 0) - view.backgroundColor = .white setupSubView() } diff --git a/Tracker/TabBarController/TrackersViews/SuplementaryView/HeaderReusableView.swift b/Tracker/TabBarController/TrackersViews/SuplementaryView/HeaderReusableView.swift index e0dd116..391a986 100644 --- a/Tracker/TabBarController/TrackersViews/SuplementaryView/HeaderReusableView.swift +++ b/Tracker/TabBarController/TrackersViews/SuplementaryView/HeaderReusableView.swift @@ -13,12 +13,12 @@ final class HeaderReusableView: UICollectionReusableView { static let fontLable = UIFont.boldSystemFont(ofSize: 19) static let numberOfLinesLable = 1 } + private let colors = Colors() - lazy var label: UILabel = { + private lazy var label: UILabel = { let label = UILabel() label.backgroundColor = .clear label.font = ConstantsHeader.fontLable - label.textColor = .blackDay label.numberOfLines = ConstantsHeader.numberOfLinesLable return label @@ -26,6 +26,7 @@ final class HeaderReusableView: UICollectionReusableView { override init(frame: CGRect) { super.init(frame: frame) + backgroundColor = colors.viewBackground setupLable() } @@ -35,8 +36,8 @@ final class HeaderReusableView: UICollectionReusableView { } //MARK: - SetupUI -private extension HeaderReusableView { - func setupLable() { +extension HeaderReusableView { + private func setupLable() { addSubview(label) label.translatesAutoresizingMaskIntoConstraints = false @@ -46,4 +47,8 @@ private extension HeaderReusableView { label.bottomAnchor.constraint(equalTo: bottomAnchor) ]) } + + func setTextLable(text: String) { + label.text = text + } } diff --git a/Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift b/Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift index 7838e36..f24a1c3 100644 --- a/Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift +++ b/Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift @@ -24,6 +24,7 @@ final class CreateTrackerViewController: UIViewController { private struct ConstantsCreateVc { static let newHabit = NSLocalizedString("newHabit", comment: "") static let newIrregular = NSLocalizedString("newIrregular", comment: "") + static let numberOfDays = NSLocalizedString("numberOfDays", tableName: "LocalizableDict", comment: "") static let placeholderTrackerTextField = NSLocalizedString("placeholderTrackerTextField", comment: "") static let textCancelButton = NSLocalizedString("textCancelButton", comment: "") static let textGreateButton = NSLocalizedString("textGreateButton", comment: "") @@ -36,6 +37,7 @@ final class CreateTrackerViewController: UIViewController { static let heightCollectionView = CGFloat(54) static let textFont = UIFont.systemFont(ofSize: 16, weight: .medium) + static let countСompletedTrackersFont = UIFont.boldSystemFont(ofSize: 32) static let createNameTextFieldFont = UIFont.systemFont(ofSize: 17, weight: .regular) static let spacingButtonSpacing = CGFloat(10) static let spacingContentStack = CGFloat(24) @@ -50,13 +52,11 @@ final class CreateTrackerViewController: UIViewController { weak var createTrackerDelegate: CreateTrackerViewControllerDelegate? private let viewModel: EditTrackerModelProtocol - private let handlerResultType = HandlerResultType() private let colors = Colors() private lazy var newHabitLabel: UILabel = { let newHabitLabel = UILabel() newHabitLabel.text = viewModel.isSchedul ? ConstantsCreateVc.newIrregular : ConstantsCreateVc.newHabit - newHabitLabel.textColor = .blackDay newHabitLabel.font = ConstantsCreateVc.textFont newHabitLabel.textAlignment = .center newHabitLabel.backgroundColor = .clear @@ -64,6 +64,14 @@ final class CreateTrackerViewController: UIViewController { return newHabitLabel }() + private lazy var countСompletedTrackers: UILabel = { + let countСompletedTrackers = UILabel() + countСompletedTrackers.font = ConstantsCreateVc.countСompletedTrackersFont + countСompletedTrackers.text = setTextCountLable(count: 0) + + return countСompletedTrackers + }() + private lazy var createNameTextField: UITextField = { let createNameTextField = UITextField() createNameTextField.placeholder = ConstantsCreateVc.placeholderTrackerTextField @@ -109,6 +117,7 @@ final class CreateTrackerViewController: UIViewController { selectionTableView.backgroundColor = .clear selectionTableView.isScrollEnabled = false selectionTableView.separatorStyle = viewModel.isSchedul ? .none : .singleLine + selectionTableView.separatorColor = .grayDay selectionTableView.separatorInset = ConstantsCreateVc.insertSeparatorTableView selectionTableView.register(CreateTrackerTableViewCell.self, forCellReuseIdentifier: "\(CreateTrackerTableViewCell.self)") @@ -169,8 +178,8 @@ final class CreateTrackerViewController: UIViewController { createButton.layer.cornerRadius = ConstantsCreateVc.cornerRadius createButton.layer.masksToBounds = true createButton.backgroundColor = .grayDay + createButton.setTitleColor(.whiteDay, for: .normal) createButton.setTitle(ConstantsCreateVc.textGreateButton, for: .normal) - createButton.titleLabel?.textColor = .redDay createButton.titleLabel?.font = ConstantsCreateVc.textFont createButton.isEnabled = false createButton.addTarget(self, action: #selector(didTapGreateButton) , for: .touchUpInside) @@ -221,7 +230,7 @@ final class CreateTrackerViewController: UIViewController { super.viewDidLoad() bind() setupUIElement() - view.backgroundColor = .white + view.backgroundColor = colors.viewBackground let tapGesture = UITapGestureRecognizer(target: self, action: #selector(hideKeyboard)) tapGesture.cancelsTouchesInView = false view.addGestureRecognizer(tapGesture) @@ -229,7 +238,7 @@ final class CreateTrackerViewController: UIViewController { } extension CreateTrackerViewController { - func bind() { + private func bind() { guard let viewModel = viewModel as? EditTrackerViewModel else { return } viewModel.$schedule.bind { [weak self] _ in guard let self else { return } @@ -260,7 +269,7 @@ extension CreateTrackerViewController { } } - func chengeActivButton() { + private func chengeActivButton() { let flag = self.viewModel.checkingForEmptiness() isActivCreateButton(flag: flag) } @@ -322,28 +331,47 @@ extension CreateTrackerViewController { view.endEditing(true) } - //MARK: - Логика private func isActivCreateButton(flag: Bool) { createButton.isEnabled = flag - createButton.backgroundColor = flag ? .blackDay : .grayDay + createButton.backgroundColor = flag ? colors.buttonDisabledColor : .grayDay + createButton.setTitleColor( flag ? .textEventColor : .whiteDay, for: .normal) } func reverseIsSchedul() { viewModel.reverseIsSchedul() } + func setTextCountLable(count: Int) -> String { + String.localizedStringWithFormat(ConstantsCreateVc.numberOfDays, + count) + } + func presentViewController(vc: UIViewController, modalStyle: UIModalPresentationStyle) { vc.modalPresentationStyle = modalStyle present(vc, animated: true) } + //MARK: - SetupUI func setupUIElement() { setupNewHabitLabel() + if updateTrackerDelegate != nil { + setupCountLabel() + } setupButtonStack() setupScrollView() setupStackView() } + func setupCountLabel() { + view.addSubview(countСompletedTrackers) + countСompletedTrackers.translatesAutoresizingMaskIntoConstraints = false + + NSLayoutConstraint.activate([ + countСompletedTrackers.topAnchor.constraint(equalTo: newHabitLabel.bottomAnchor, constant: 38), + countСompletedTrackers.centerXAnchor.constraint(equalTo: view.centerXAnchor) + ]) + } + func setupNewHabitLabel() { view.addSubview(newHabitLabel) newHabitLabel.translatesAutoresizingMaskIntoConstraints = false @@ -373,8 +401,10 @@ extension CreateTrackerViewController { view.addSubview(scrollView) scrollView.translatesAutoresizingMaskIntoConstraints = false + let scrollTopAnchor = updateTrackerDelegate == nil ? newHabitLabel.bottomAnchor : countСompletedTrackers.bottomAnchor + NSLayoutConstraint.activate([ - scrollView.topAnchor.constraint(equalTo: newHabitLabel.bottomAnchor, constant: 24), + scrollView.topAnchor.constraint(equalTo: scrollTopAnchor, constant: 24), scrollView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 16), scrollView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -16), scrollView.bottomAnchor.constraint(equalTo: buttonStack.topAnchor) @@ -405,11 +435,12 @@ extension CreateTrackerViewController { //MARK: - UITableViewDataSource extension CreateTrackerViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - viewModel.listSettings.count + return viewModel.listSettings.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - guard let cell = tableView.dequeueReusableCell(withIdentifier: "\(CreateTrackerTableViewCell.self)") as? CreateTrackerTableViewCell, + guard let cell = tableView.dequeueReusableCell(withIdentifier: "\(CreateTrackerTableViewCell.self)") as? + CreateTrackerTableViewCell, let viewModel = viewModel as? EditTrackerViewModel else { return UITableViewCell() } let secondarySchedulText = viewModel.jonedSchedule(schedule: viewModel.schedule, @@ -567,10 +598,10 @@ extension CreateTrackerViewController: UICollectionViewDelegateFlowLayout { switch kind { case UICollectionView.elementKindSectionHeader: if collectionView == colorCollectionView { - supplementary.label.text = Header.color.name + supplementary.setTextLable(text: Header.color.name) } if collectionView == emojiCollectionView { - supplementary.label.text = Header.emoji.name + supplementary.setTextLable(text: Header.emoji.name) } default: return UICollectionReusableView() @@ -596,9 +627,10 @@ extension CreateTrackerViewController: SelectCategoriesViewControllerDelegate { //MARK: - TrackersViewControllerDelegate extension CreateTrackerViewController: TrackersViewControllerDelegate { - func editTracker(vc: TrackersViewController, editTracker: Tracker, nameCategory: String, indexPath: IndexPath) { + func editTracker(vc: TrackersViewController, editTracker: Tracker, count: Int, nameCategory: String, indexPath: IndexPath) { let colorRow = viewModel.getColorRow(color: editTracker.color) let emojiRow = viewModel.getEmojiRow(emoji: editTracker.emoji) + countСompletedTrackers.text = setTextCountLable(count: count) guard let emojiCell = emojiCollectionView.cellForItem(at: IndexPath(row: emojiRow, section: 0)) as? EmojiCollectionViewCell, let colorCell = colorCollectionView.cellForItem(at: IndexPath(row: colorRow, section: 0)) as? ColorCollectionViewCell diff --git a/Tracker/TabBarController/TrackersViews/ViewControllers/EventSelectionViewController.swift b/Tracker/TabBarController/TrackersViews/ViewControllers/EventSelectionViewController.swift index 0075d36..2be7168 100644 --- a/Tracker/TabBarController/TrackersViews/ViewControllers/EventSelectionViewController.swift +++ b/Tracker/TabBarController/TrackersViews/ViewControllers/EventSelectionViewController.swift @@ -19,17 +19,16 @@ final class EventSelectionViewController: UIViewController { static let texGreatetLabelName = NSLocalizedString("texGreatetLabelName", comment: "") static let textFont = UIFont.systemFont(ofSize: 16, weight: .medium) static let cornerRadius = CGFloat(16) - static let lableGreateImage = UIColor(named: "blackDay") } weak var delegate: EventSelectionViewControllerDelegate? + private let colors = Colors() private lazy var labelCreate: UILabel = { let labelCreate = UILabel() labelCreate.text = ConstantsEventVc.texGreatetLabelName labelCreate.font = ConstantsEventVc.textFont labelCreate.backgroundColor = .clear - labelCreate.textColor = ConstantsEventVc.lableGreateImage labelCreate.translatesAutoresizingMaskIntoConstraints = false return labelCreate @@ -60,7 +59,7 @@ final class EventSelectionViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - view.backgroundColor = .white + view.backgroundColor = colors.viewBackground setupLableGreate() setupStackView() } @@ -94,8 +93,8 @@ private extension EventSelectionViewController { func setupButton(text: NameEvent, font: UIFont, cornerRadius: CGFloat? = nil) -> UIButton { let button = UIButton() button.setTitle(text.name, for: .normal) - button.titleLabel?.textColor = UIColor.whiteDay - button.backgroundColor = UIColor.blackDay + button.backgroundColor = colors.buttonEventColor + button.setTitleColor(.textEventColor, for: .normal) button.titleLabel?.font = font button.layer.cornerRadius = cornerRadius ?? CGFloat(0) button.layer.masksToBounds = cornerRadius == nil ? false : true diff --git a/Tracker/TabBarController/TrackersViews/ViewControllers/FiltersViewController.swift b/Tracker/TabBarController/TrackersViews/ViewControllers/FiltersViewController.swift index 4271496..fc02b4a 100644 --- a/Tracker/TabBarController/TrackersViews/ViewControllers/FiltersViewController.swift +++ b/Tracker/TabBarController/TrackersViews/ViewControllers/FiltersViewController.swift @@ -22,6 +22,7 @@ final class FiltersViewController: UIViewController { weak var delegate: FiltersViewControllerDelegate? private let viewModel: FiltersViewModelProtocol + private let colors = Colors() private lazy var filtersLable: UILabel = { let filtersLable = UILabel() @@ -40,6 +41,7 @@ final class FiltersViewController: UIViewController { filterTableView.delegate = self filterTableView.backgroundColor = .clear filterTableView.separatorStyle = .singleLine + filterTableView.separatorColor = .separatorColor filterTableView.register(CustomTableViewCell.self, forCellReuseIdentifier: "\(CustomTableViewCell.self)") filterTableView.separatorInset = ConstantsFilters.insertSeparatorTableView @@ -48,7 +50,7 @@ final class FiltersViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - view.backgroundColor = .white + view.backgroundColor = colors.viewBackground setupCategoryLabel() setupTableView() } @@ -88,6 +90,7 @@ private extension FiltersViewController { } } +//MARK: - UITableViewDataSource extension FiltersViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { viewModel.filtersState.count @@ -117,6 +120,7 @@ extension FiltersViewController: UITableViewDataSource { } } +//MARK: - UITableViewDelegate extension FiltersViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { guard let delegate else { return } diff --git a/Tracker/TabBarController/TrackersViews/ViewControllers/NewCategoriViewController.swift b/Tracker/TabBarController/TrackersViews/ViewControllers/NewCategoriViewController.swift index bcc4dea..c80777d 100644 --- a/Tracker/TabBarController/TrackersViews/ViewControllers/NewCategoriViewController.swift +++ b/Tracker/TabBarController/TrackersViews/ViewControllers/NewCategoriViewController.swift @@ -32,11 +32,11 @@ class NewCategoriViewController: UIViewController { weak var updateCategoryrDelegate: UpdateCategoriViewControllerDelegate? private var viewModel: NewCategoryViewModelProtocol? + private let colors = Colors() private lazy var newCategoriLabel: UILabel = { let newCategoriLabel = UILabel() newCategoriLabel.text = ConstantsNewCatVc.newcategoriLabelText - newCategoriLabel.textColor = .blackDay newCategoriLabel.font = ConstantsNewCatVc.font newCategoriLabel.textAlignment = .center newCategoriLabel.backgroundColor = .clear @@ -47,7 +47,7 @@ class NewCategoriViewController: UIViewController { private lazy var readyButton: UIButton = { let readyButton = UIButton() readyButton.setTitle(ConstantsNewCatVc.buttonName, for: .normal) - readyButton.titleLabel?.textColor = UIColor.whiteDay + readyButton.setTitleColor(.textEventColor, for: .normal) readyButton.isEnabled = false readyButton.backgroundColor = .grayDay readyButton.titleLabel?.font = ConstantsNewCatVc.font @@ -92,7 +92,7 @@ class NewCategoriViewController: UIViewController { let tapGesture = UITapGestureRecognizer(target: self, action: #selector(hideKeyboard)) tapGesture.cancelsTouchesInView = false view.addGestureRecognizer(tapGesture) - view.backgroundColor = .white + view.backgroundColor = colors.viewBackground setupUIElement() } } @@ -103,6 +103,8 @@ private extension NewCategoriViewController { viewModel.$newNameCategory.bind { [weak self] text in guard let self else { return } chengeHiddenButton(flag: viewModel.getNewNameCategory().isEmpty) + readyButton.backgroundColor = !text.isEmpty ? colors.buttonDisabledColor : .grayDay + readyButton.setTitleColor( !text.isEmpty ? .textEventColor : .whiteDay, for: .normal) } } @@ -111,10 +113,13 @@ private extension NewCategoriViewController { func didTapNewСategoriButton() { guard let viewModel else { return } if let createCategorydelegate { - createCategorydelegate.didNewCategoryName(self, nameCategory: viewModel.getNewNameCategory()) + createCategorydelegate.didNewCategoryName(self, + nameCategory: viewModel.getNewNameCategory()) } if let updateCategoryrDelegate { - updateCategoryrDelegate.didUpdateCategoryName(self, newNameCategory: viewModel.getNewNameCategory(), oldNameCategory: viewModel.getOldNameCategory()) + updateCategoryrDelegate.didUpdateCategoryName(self, + newNameCategory: viewModel.getNewNameCategory(), + oldNameCategory: viewModel.getOldNameCategory()) } dismiss(animated: true) } @@ -127,6 +132,8 @@ private extension NewCategoriViewController { func chengeHiddenButton(flag: Bool) { readyButton.isEnabled = !flag readyButton.backgroundColor = !flag ? .blackDay : .grayDay + readyButton.backgroundColor = flag ? colors.buttonDisabledColor : .grayDay + readyButton.setTitleColor( flag ? .textEventColor : .whiteDay, for: .normal) } //MARK: - SetupUI diff --git a/Tracker/TabBarController/TrackersViews/ViewControllers/ScheduleViewController.swift b/Tracker/TabBarController/TrackersViews/ViewControllers/ScheduleViewController.swift index ecee5ed..a78ebf2 100644 --- a/Tracker/TabBarController/TrackersViews/ViewControllers/ScheduleViewController.swift +++ b/Tracker/TabBarController/TrackersViews/ViewControllers/ScheduleViewController.swift @@ -26,12 +26,13 @@ final class ScheduleViewController: UIViewController { weak var delegate: ScheduleViewControllerDelegate? private let viewModel: SheduleViewModelProtocol + private let colors = Colors() private lazy var scheduleLable: UILabel = { let scheduleLable = UILabel() scheduleLable.text = ConstantsShedulVc.scheduleLableText scheduleLable.font = ConstantsShedulVc.scheduleLableFont - scheduleLable.textColor = .blackDay + scheduleLable.textColor = colors.buttonEventColor scheduleLable.backgroundColor = .clear scheduleLable.textAlignment = .center @@ -45,6 +46,7 @@ final class ScheduleViewController: UIViewController { weekDayTableView.allowsSelection = false weekDayTableView.backgroundColor = .clear weekDayTableView.separatorInset = ConstantsShedulVc.separatorInsetTableView + weekDayTableView.separatorColor = .separatorColor weekDayTableView.register(WeekDayTableViewCell.self, forCellReuseIdentifier: "\(WeekDayTableViewCell.self)") return weekDayTableView @@ -55,9 +57,9 @@ final class ScheduleViewController: UIViewController { doneButton.addTarget(self, action: #selector(didTapDoneButton), for: .touchUpInside) doneButton.layer.cornerRadius = ConstantsShedulVc.cornerRadiusUIElement doneButton.layer.masksToBounds = true - doneButton.backgroundColor = .blackDay + doneButton.backgroundColor = colors.buttonDisabledColor doneButton.setTitle(ConstantsShedulVc.doneButtonText, for: .normal) - doneButton.titleLabel?.textColor = .whiteDay + doneButton.setTitleColor(.textEventColor, for: .normal) return doneButton }() @@ -82,7 +84,7 @@ final class ScheduleViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - view.backgroundColor = .whiteDay + view.backgroundColor = colors.viewBackground setupContentSteck() } } diff --git a/Tracker/TabBarController/TrackersViews/ViewControllers/SelectCategoriesViewController.swift b/Tracker/TabBarController/TrackersViews/ViewControllers/SelectCategoriesViewController.swift index d6a7d63..75f00f1 100644 --- a/Tracker/TabBarController/TrackersViews/ViewControllers/SelectCategoriesViewController.swift +++ b/Tracker/TabBarController/TrackersViews/ViewControllers/SelectCategoriesViewController.swift @@ -12,6 +12,7 @@ protocol SelectCategoriesViewControllerDelegate: AnyObject { func categoriesViewController(vc: UIViewController, nameCategory: String) } +//MARK: - SelectCategoryEditViewControllerDelegate protocol SelectCategoryEditViewControllerDelegate: AnyObject { func editCategoriesViewController(vc: UIViewController, oldNameCategory: String) } @@ -20,7 +21,6 @@ protocol SelectCategoryEditViewControllerDelegate: AnyObject { final class SelectCategoriesViewController: UIViewController { private struct ConstantsCreateCatVc { static let imageViewImageStab = "Star" - static let alertActionErrorTitle = "Ok" static let categoryLabelText = NSLocalizedString("categoriLabelText", comment: "") static let categoryAddButtonText = NSLocalizedString("categoriAddButtonText", comment: "") @@ -30,6 +30,7 @@ final class SelectCategoriesViewController: UIViewController { static let textFixed = NSLocalizedString("textFixed", comment: "") static let lableTextStabNumberOfLines = 2 + static let one = 1 static let selectionCategoryTableViewRowHeight = CGFloat(75) static let cornerRadius = CGFloat(16) static let categoryLabelFont = UIFont.systemFont(ofSize: 16, weight: .medium) @@ -43,6 +44,7 @@ final class SelectCategoriesViewController: UIViewController { private var viewModel: CategoryViewModelProtocol private let handler = HandlerResultType() + private let colors = Colors() private lazy var categoryLabel: UILabel = { let categoryLabel = UILabel() @@ -88,6 +90,7 @@ final class SelectCategoriesViewController: UIViewController { selectionCategoryTableView.delegate = self selectionCategoryTableView.backgroundColor = .clear selectionCategoryTableView.separatorStyle = .singleLine + selectionCategoryTableView.separatorColor = .separatorColor selectionCategoryTableView.register(CustomTableViewCell.self, forCellReuseIdentifier: "\(CustomTableViewCell.self)") selectionCategoryTableView.separatorInset = ConstantsCreateCatVc.insertSeparatorTableView @@ -97,8 +100,8 @@ final class SelectCategoriesViewController: UIViewController { private lazy var createCategoryButton: UIButton = { let createCategoryButton = UIButton() createCategoryButton.setTitle(ConstantsCreateCatVc.categoryAddButtonText, for: .normal) - createCategoryButton.titleLabel?.textColor = .whiteDay - createCategoryButton.backgroundColor = .blackDay + createCategoryButton.setTitleColor(.textEventColor, for: .normal) + createCategoryButton.backgroundColor = colors.buttonDisabledColor createCategoryButton.titleLabel?.font = ConstantsCreateCatVc.lableFont createCategoryButton.layer.cornerRadius = ConstantsCreateCatVc.cornerRadius createCategoryButton.layer.masksToBounds = true @@ -123,7 +126,7 @@ final class SelectCategoriesViewController: UIViewController { guard let viewModel = viewModel as? CategoryViewModel else { return } setupUIElement() bind() - view.backgroundColor = .whiteDay + view.backgroundColor = colors.viewBackground handler.resultTypeHandler(viewModel.сategoryExcludingFixed()) { [weak self] cat in guard let self else { return } self.showStabView(flag: !cat.isEmpty) @@ -155,7 +158,7 @@ extension SelectCategoriesViewController { handler.resultTypeHandler(viewModel.сategoryExcludingFixed()) { cat in self.showStabView(flag: !cat.isEmpty) } - selectionCategoryTableView.reloadData() + self.selectionCategoryTableView.reloadData() } } @@ -173,13 +176,19 @@ extension SelectCategoriesViewController { } } - private func deleteAction(text: String, category: TrackerCategory) -> UIAction { + private func deleteAction(text: String, category: TrackerCategory, trackers: [Tracker]) -> UIAction { UIAction(title: text, attributes: .destructive) { [weak self] _ in guard let self else { return } - handler.resultTypeHandler(viewModel.deleteCategory(nameCategory: category.nameCategory)) {} + self.handler.resultTypeHandler(self.viewModel.deleteTrackerRecords(trackers: trackers)) {} + self.handler.resultTypeHandler(self.viewModel.deleteTrackers(trackers: category.arrayTrackers)) {} + self.handler.resultTypeHandler(self.viewModel.deleteCategory(nameCategory: category.nameCategory)) { + self.handler.resultTypeHandler(self.viewModel.deleteTrackers(trackers: category.arrayTrackers)) { + self.handler.resultTypeHandler(self.viewModel.deleteTrackerRecords(trackers: trackers)) {} + } + } } } - + //MARK: - SetupUI private func setupUIElement() { setupCategoryButton() @@ -259,27 +268,32 @@ extension SelectCategoriesViewController: UITableViewDataSource { let model = CustomCellModel(text: text, color: .backgroundNight) cell.config(model: model) - if category.count > 1 { + if category.count > ConstantsCreateCatVc.one { cell.separatorInset = ConstantsCreateCatVc.insertSeparatorTableView } - if indexPath.row == .zero && category.count == 1 { + if indexPath.row == .zero && category.count == ConstantsCreateCatVc.one { cell.setupCornerRadius(cornerRadius: ConstantsCreateCatVc.cornerRadius, maskedCorners: nil) cell.separatorInset = UIEdgeInsets(top: .zero, left: view.bounds.width, bottom: .zero, right: .zero) } - if indexPath.row == .zero && category.count > 1 { + if indexPath.row == .zero && category.count > ConstantsCreateCatVc.one { cell.setupCornerRadius(cornerRadius: ConstantsCreateCatVc.cornerRadius, maskedCorners: [.layerMaxXMinYCorner, .layerMinXMinYCorner]) } - if indexPath.row == category.count - 1 && category.count > 1 { + if indexPath.row == category.count - ConstantsCreateCatVc.one && + category.count > ConstantsCreateCatVc.one { cell.setupCornerRadius(cornerRadius: ConstantsCreateCatVc.cornerRadius, maskedCorners: [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]) cell.separatorInset = UIEdgeInsets(top: .zero, left: view.bounds.width, bottom: .zero, right: .zero) } + if category.count == ConstantsCreateCatVc.one { + cell.layer.cornerRadius = 16 + } + return cell } } @@ -308,7 +322,7 @@ extension SelectCategoriesViewController: UITableViewDelegate { else { return UIMenu()} return UIMenu(children: [ editAction(text: ConstantsCreateCatVc.textEdit, category: category), - deleteAction(text: ConstantsCreateCatVc.textDelete, category: category) + deleteAction(text: ConstantsCreateCatVc.textDelete, category: category, trackers: category.arrayTrackers) ]) }) } @@ -323,7 +337,7 @@ extension SelectCategoriesViewController: NewCategoriViewControllerDelegate { if let _ = category.filter({ $0.nameCategory == nameFirstUppercased }).first { return } - if nameFirstUppercased == ConstantsCreateCatVc.textFixed { + if nameFirstUppercased == Fixed.fixedRu.rawValue || nameFirstUppercased == Fixed.fixedEng.rawValue { return } handler.resultTypeHandler(viewModel.addCategory(nameCategory: nameFirstUppercased)) {} diff --git a/Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift b/Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift index 3623830..2727b49 100644 --- a/Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift +++ b/Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift @@ -7,8 +7,9 @@ import UIKit +//MARK: - TrackersViewControllerDelegate protocol TrackersViewControllerDelegate: AnyObject { - func editTracker(vc: TrackersViewController, editTracker: Tracker, nameCategory: String, indexPath: IndexPath) + func editTracker(vc: TrackersViewController, editTracker: Tracker, count: Int, nameCategory: String, indexPath: IndexPath) } //MARK: - TrackersViewController @@ -96,7 +97,6 @@ final class TrackersViewController: UIViewController { let lableHeader = UILabel() lableHeader.text = ConstantsTrackerVc.headerText lableHeader.font = ConstantsTrackerVc.fontLabelHeader - lableHeader.textColor = .blackDay lableHeader.translatesAutoresizingMaskIntoConstraints = false lableHeader.backgroundColor = .clear @@ -105,6 +105,7 @@ final class TrackersViewController: UIViewController { private lazy var trackerCollectionView: TrackerCollectionView = { let trackerCollectionView = greateTrackerCollectionView() + trackerCollectionView.alwaysBounceVertical = true trackerCollectionView.delegate = self trackerCollectionView.dataSource = self trackerCollectionView.backgroundColor = .clear @@ -161,7 +162,6 @@ final class TrackersViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - AnalyticsService.shared.open() view.backgroundColor = colors.viewBackground setupUIElement() viewModel = TrackerViewModel() @@ -176,6 +176,10 @@ final class TrackersViewController: UIViewController { viewModel.allTrackersByDate(date: datePicker.date) } } + + override func viewWillAppear(_ animated: Bool) { + AnalyticsService.shared.open() + } } private extension TrackersViewController { @@ -191,11 +195,15 @@ private extension TrackersViewController { self.trackerCollectionView.reloadItems(at: [indexPath]) } + viewModel.$filterListTrackersWeekDay.bind { [weak self] category in + guard let self else { return } + self.buttonFilter.isHidden = category.isEmpty + } + viewModel.$visibleCategory.bind { [weak self] result in guard let self, let cat = handler.resultTypeHandlerGetValue(result) else { return } - self.buttonFilter.isHidden = cat.isEmpty self.showStabView(flag: !cat.isEmpty) self.trackerCollectionView.reloadData() } @@ -211,10 +219,10 @@ private extension TrackersViewController { viewModel.allTrackersByDate(date: datePicker.date) setColorButtonFilter(state: viewModel.getFilterState(), button: buttonFilter) case .completed: - viewModel.getCompleted(date: datePicker.date) + viewModel.getCompleted(date: datePicker.date, flag: true) setColorButtonFilter(state: viewModel.getFilterState(), button: buttonFilter) case .notCompleted: - viewModel.getNotCompleted(date: datePicker.date) + viewModel.getNotCompleted(date: datePicker.date, flag: false) setColorButtonFilter(state: viewModel.getFilterState(), button: buttonFilter) } } @@ -262,10 +270,10 @@ private extension TrackersViewController { viewModel.allTrackersByDate(date: datePicker.date) setColorButtonFilter(state: viewModel.getFilterState(), button: buttonFilter) case .completed: - viewModel.getCompleted(date: datePicker.date) + viewModel.getCompleted(date: datePicker.date, flag: true) setColorButtonFilter(state: viewModel.getFilterState(), button: buttonFilter) case .notCompleted: - viewModel.getNotCompleted(date: datePicker.date) + viewModel.getNotCompleted(date: datePicker.date, flag: false) setColorButtonFilter(state: viewModel.getFilterState(), button: buttonFilter) } } @@ -302,14 +310,16 @@ private extension TrackersViewController { let cat = self.handler.resultTypeHandlerGetValue(viewModel.getCategory()) else { return } if category.isPinned { - handler.resultTypeHandler(viewModel.addPinnedCategory(id: tracker.id, nameCategory: category.nameCategory)) {} - self.handler.resultTypeHandler(viewModel.deleteTracker(tracker.id)) {} + handler.resultTypeHandler(viewModel.addPinnedCategory(id: tracker.id, nameCategory: category.nameCategory)) { + self.handler.resultTypeHandler(viewModel.deleteTracker(tracker.id)) {} + } let filterNameCategory = cat.filter { $0.nameCategory == ConstantsTrackerVc.textFixed } if let _ = filterNameCategory.first?.nameCategory { self.handler.resultTypeHandler(viewModel.addNewTracker(tracker, nameCategory: ConstantsTrackerVc.textFixed)) {} } else { - self.handler.resultTypeHandler(viewModel.addCategory(nameCategory: ConstantsTrackerVc.textFixed)) {} - self.handler.resultTypeHandler(viewModel.addNewTracker(tracker, nameCategory: ConstantsTrackerVc.textFixed)) {} + self.handler.resultTypeHandler(viewModel.addCategory(nameCategory: ConstantsTrackerVc.textFixed)) { + self.handler.resultTypeHandler(viewModel.addNewTracker(tracker, nameCategory: ConstantsTrackerVc.textFixed)) {} + } } } else { if let nameCategory = handler.resultTypeHandlerGetValue(viewModel.deleteAndGetPinnedCategory(id: tracker.id)) { @@ -318,8 +328,9 @@ private extension TrackersViewController { self.handler.resultTypeHandler(viewModel.addNewTracker(tracker, nameCategory: category.nameCategory)) {} } else { guard let nameCategory else { return } - self.handler.resultTypeHandler(viewModel.addCategory(nameCategory: nameCategory)) {} - self.handler.resultTypeHandler(viewModel.addNewTracker(tracker, nameCategory: nameCategory)) {} + self.handler.resultTypeHandler(viewModel.addCategory(nameCategory: nameCategory)) { + self.handler.resultTypeHandler(viewModel.addNewTracker(tracker, nameCategory: nameCategory)) {} + } } } } @@ -332,17 +343,22 @@ private extension TrackersViewController { indexPath: IndexPath) -> UIAction { UIAction(title: text) { [weak self] _ in - guard let self else { return } - let viewModel = EditTrackerViewModel() - let createTrackerVC = CreateTrackerViewController(viewModel: viewModel, updateTrackerDelegate: self) + guard let self, + let viewModel + else { return } + let editViewModel = EditTrackerViewModel() + let createTrackerVC = CreateTrackerViewController(viewModel: editViewModel, updateTrackerDelegate: self) delegate = createTrackerVC - if tracker.schedule.count != viewModel.regular.count { + if tracker.schedule.count != editViewModel.regular.count { createTrackerVC.reverseIsSchedul() } createTrackerVC.modalPresentationStyle = .formSheet present(createTrackerVC, animated: true) { + let count = self.handler.resultTypeHandlerGetValue(viewModel.getCountTrackerCompleted(id: tracker.id)) + guard let count else { return } self.delegate?.editTracker(vc: self, editTracker: tracker, + count: count, nameCategory: category.nameCategory, indexPath: indexPath) } @@ -358,7 +374,10 @@ private extension TrackersViewController { preferredStyle: .actionSheet) let deleteAction = UIAlertAction(title: ConstantsTrackerVc.textDelete, style: .destructive) { _ in - self.handler.resultTypeHandler(viewModel.deleteTracker(tracker.id)) {} + + self.handler.resultTypeHandler(viewModel.deleteTracker(tracker.id)) { + self.handler.resultTypeHandler(viewModel.deleteTrackersRecord(id: tracker.id)) {} + } AnalyticsService.shared.delete() alert.dismiss(animated: true) } @@ -379,7 +398,6 @@ private extension TrackersViewController { setupStabView() setupNavBarItem() setupButtonFilter() - } func setupNavBarItem() { @@ -471,7 +489,7 @@ extension TrackersViewController: UICollectionViewDataSource { else { return .zero } return itemsCount } - + func numberOfSections(in collectionView: UICollectionView) -> Int { guard let viewModel = viewModel as? TrackerViewModel, let sectionCount = handler.resultTypeHandlerGetValue(viewModel.visibleCategory)?.count @@ -523,6 +541,16 @@ extension TrackersViewController: UICollectionViewDelegate { let previewTarget = UIPreviewTarget(container: self.trackerCollectionView, center: cell.center) return .init(view: cell.getView(), parameters: UIPreviewParameters(), target: previewTarget) } + + func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { + guard let viewModel, + let visibleCategories = handler.resultTypeHandlerGetValue(viewModel.visibleCategory) + else { return } + + if visibleCategories.count - 1 == indexPath.row { + buttonFilter.isHidden = false + } + } func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemsAt indexPaths: [IndexPath], @@ -584,7 +612,12 @@ extension TrackersViewController: UICollectionViewDelegateFlowLayout { let viewModel = viewModel as? TrackerViewModel, let visibleCategories = handler.resultTypeHandlerGetValue(viewModel.visibleCategory) else { return UICollectionReusableView() } - supplementary.label.text = visibleCategories[indexPath.section].nameCategory + let nameCategory = visibleCategories[indexPath.section].nameCategory + if nameCategory == Fixed.fixedRu.rawValue || nameCategory == Fixed.fixedEng.rawValue { + supplementary.setTextLable(text: ConstantsTrackerVc.textFixed) + return supplementary + } + supplementary.setTextLable(text: nameCategory) return supplementary default: return UICollectionReusableView() @@ -599,19 +632,22 @@ extension TrackersViewController: UISearchResultsUpdating { let viewModel else { return } if !word.isEmpty { - guard let seachCategory = handler.resultTypeHandlerGetValue(viewModel.filterListTrackersName(word: word)) else { return } + guard let seachCategory = handler.resultTypeHandlerGetValue(viewModel.filterListTrackersName(word: word)) + else { return } viewModel.getShowListTrackerSearchForName(seachCategory) return } - if word.isEmpty { - viewModel.getShowListTrackersForDay(date: datePicker.date) - chengeStab(text: ConstantsTrackerVc.labelNothingFoundText, nameImage: ConstantsTrackerVc.imageNothingFound) + if word.isEmpty { + let _ = viewModel.getShowListTrackersForDay(date: datePicker.date) + chengeStab(text: ConstantsTrackerVc.labelNothingFoundText, + nameImage: ConstantsTrackerVc.imageNothingFound) } if !searchController.isActive { - viewModel.getShowListTrackersForDay(date: datePicker.date) - chengeStab(text: ConstantsTrackerVc.labelStabText, nameImage: ConstantsTrackerVc.imageStar) + let _ = viewModel.getShowListTrackersForDay(date: datePicker.date) + chengeStab(text: ConstantsTrackerVc.labelStabText, + nameImage: ConstantsTrackerVc.imageStar) } } } diff --git a/Tracker/ViewModel/CategoryViewModel.swift b/Tracker/ViewModel/CategoryViewModel.swift index fd6bf3d..2dd67cd 100644 --- a/Tracker/ViewModel/CategoryViewModel.swift +++ b/Tracker/ViewModel/CategoryViewModel.swift @@ -7,6 +7,7 @@ import Foundation +//MARK: - CategoryViewModelProtocol protocol CategoryViewModelProtocol { func isCategorySelected(at index: Int) -> Result func getCategory() -> Result<[TrackerCategory], Error> @@ -15,24 +16,27 @@ protocol CategoryViewModelProtocol { func createNameCategory(at index: Int) -> Result func updateNameCategory(newNameCategory: String, oldNameCaetegory: String) -> Result func deleteCategory(nameCategory: String) -> Result + func deleteTrackers(trackers: [Tracker]) -> Result + func deleteTrackerRecords(trackers: [Tracker]) -> Result func selectСategory(at index: Int) -> Result } //MARK: - ViewModel final class CategoryViewModel { - private let textFixed = NSLocalizedString("textFixed", comment: "") - @Observable> private(set) var category: Result<[TrackerCategory], Error> @UserDefaultsBacked(key: UserDefaultKeys.selectNameCategory.rawValue) private(set) var selectNameCategory: String? private let trackerCategoryStore: TrackerCategoryStoreProtocol private let trackerStore: TrackerStoreProtocol + private let trackerRecordStore: TrackerRecordStoreProtocol convenience init() { let trackerCategoryStore = TrackerCategoryStore() let trackerStore = TrackerStore() + let trackerRecordStore = TrackerRecordStore() self.init(trackerCategoryStore: trackerCategoryStore, trackerStore: trackerStore, + trackerRecordStore: trackerRecordStore, category: .success([]) ) trackerCategoryStore.delegate = self @@ -42,11 +46,13 @@ final class CategoryViewModel { init(trackerCategoryStore: TrackerCategoryStoreProtocol, trackerStore: TrackerStoreProtocol, + trackerRecordStore: TrackerRecordStoreProtocol, category: Result<[TrackerCategory], Error> ) { self.trackerCategoryStore = trackerCategoryStore self.trackerStore = trackerStore + self.trackerRecordStore = trackerRecordStore self.category = category } } @@ -103,7 +109,10 @@ extension CategoryViewModel: CategoryViewModelProtocol { func сategoryExcludingFixed() -> Result<[TrackerCategory], Error> { switch category { case .success(let category): - let filterCat = category.filter { $0.nameCategory != textFixed } + let filterCat = category.filter { + $0.nameCategory != Fixed.fixedRu.rawValue && + $0.nameCategory != Fixed.fixedEng.rawValue + } return .success(filterCat) case .failure(let error): return .failure(error) @@ -124,6 +133,14 @@ extension CategoryViewModel: CategoryViewModelProtocol { trackerCategoryStore.deleteCategory(nameCategory: nameCategory) } + func deleteTrackers(trackers: [Tracker]) -> Result { + trackerStore.deleteTrackers(trackers: trackers) + } + + func deleteTrackerRecords(trackers: [Tracker]) -> Result { + trackerRecordStore.deleteTrackerRecords(trackers: trackers) + } + func selectСategory(at index: Int) -> Result { switch category { case .success(let category): diff --git a/Tracker/ViewModel/TrackerViewModel.swift b/Tracker/ViewModel/TrackerViewModel.swift index 56f0a70..9e2188f 100644 --- a/Tracker/ViewModel/TrackerViewModel.swift +++ b/Tracker/ViewModel/TrackerViewModel.swift @@ -7,46 +7,45 @@ import Foundation +//MARK: - TrackerViewModelProtocol protocol TrackerViewModelProtocol { var category: Result<[TrackerCategory], Error> { get } var visibleCategory: Result<[TrackerCategory], Error> { get } - func getCategory() -> Result<[TrackerCategory], Error> - func deleteTracker(_ id: UUID) -> Result + func getCategory() -> Result<[TrackerCategory], Error> func addCategory(nameCategory: String) -> Result - func deleteCategory(nameCategory: String) -> Result - func treckersRecordsResult() -> Result, Error> - func loadTrackerRecord(id: UUID) -> Result - func updateCompletedTrackers(tracker: Tracker, date: Date) -> Result + func getCountTrackerCompleted(id: UUID) -> Result + func deleteTrackersRecord(id: UUID) -> Result func addNewTracker(_ tracker: Tracker, nameCategory: String) -> Result func updateTracker(tracker: Tracker, nameCategory: String) -> Result + func deleteTracker(_ id: UUID) -> Result func getShowListTrackersForDay(date: Date) func getShowListTrackerSearchForName(_ searchCategory: [TrackerCategory]) func filterListTrackersName(word: String) -> Result<[TrackerCategory], Error> - func setIndexPath(_ indexPath: IndexPath) - func addPinnedCategory(id: UUID, nameCategory: String) -> Result func deleteAndGetPinnedCategory(id: UUID) -> Result func allTrackersByDate(date: Date) - func getToDayTrackers(date: Date) - func getNotCompleted(date: Date) - func getCompleted(date: Date) + func getNotCompleted(date: Date, flag: Bool) + func getCompleted(date: Date, flag: Bool) func setFilterState(state: FiltersState) func getFilterState() -> FiltersState + + func getIsCategoryForDay() -> Bool } -//MARK: - ViewModel +//MARK: - TrackerViewModel final class TrackerViewModel { private let textFixed = NSLocalizedString("textFixed", comment: "") @Observable> private(set) var category: Result<[TrackerCategory], Error> - @Observable private(set) var indexPath: IndexPath @Observable> private(set) var visibleCategory: Result<[TrackerCategory], Error> + @Observable<[TrackerCategory]> private(set) var filterListTrackersWeekDay: [TrackerCategory] + @Observable private(set) var indexPath: IndexPath @Observableprivate(set) var filterState: FiltersState @UserDefaultsBacked(key: UserDefaultKeys.selectNameCategory.rawValue) private var selectNameCategory: String? @UserDefaultsBacked(key: UserDefaultKeys.selectFilter.rawValue) private var selectFilter: String? @@ -71,6 +70,7 @@ final class TrackerViewModel { category: .success([]), completedTrackers: .success(Set()), visibleCategory: .success([]), + filterListTrackersWeekDay: [], indexPath: IndexPath(), filterState: .allTrackers) trackerCategoryStore.delegate = self @@ -92,6 +92,7 @@ final class TrackerViewModel { category: Result<[TrackerCategory], Error>, completedTrackers: Result, Error>, visibleCategory: Result<[TrackerCategory], Error>, + filterListTrackersWeekDay: [TrackerCategory], indexPath: IndexPath, filterState: FiltersState) { self.trackerCategoryStore = trackerCategoryStore @@ -101,6 +102,7 @@ final class TrackerViewModel { self.category = category self.completedTrackers = completedTrackers self.visibleCategory = visibleCategory + self.filterListTrackersWeekDay = filterListTrackersWeekDay self.indexPath = indexPath self.filterState = filterState } @@ -124,6 +126,30 @@ private extension TrackerViewModel { } } + func setVisibleCategory(date: Date, flag: Bool) { + switch category { + case .success(let category): + var listCategories: [TrackerCategory] = [] + let filterCategory = filterListTrackersWeekDay(trackerCategory: category, date: date) + filterCategory.forEach { + do { + let cat = try trackerCompletedOrNotByCategory(category: $0, + date: date.ignoringTime, + flag: flag, + filter: trackerRecordStore.getIsTrackerRecord(id:date:)) + if !cat.arrayTrackers.isEmpty { + listCategories.append(cat) + } + } catch { + visibleCategory = .failure(error) + } + } + visibleCategory = .success(listCategories) + case .failure(let error): + visibleCategory = .failure(error) + } + } + func comparisonWeekDays(date: Date, day: WeekDay) -> Bool { FormatDate.shared.greateWeekDayInt(date: date) == day.rawValue } @@ -150,79 +176,49 @@ private extension TrackerViewModel { listCategories.append(trackerCat) } } + filterListTrackersWeekDay = listCategories return listCategories } - func filterListTrackersCompleted(trackerCategory: [TrackerCategory], date: Date) { - var listCategories: [TrackerCategory] = [] - for cat in .zero.. Result { + return trackerRecordStore.deleteOrCreateTrackerRecord(id: id, date: date.ignoringTime) } - func updateTrackerRecord(_ trackerRecord: TrackerRecord) -> Result { - return trackerRecordStore.updateTrackerRecord(trackerRecord) + func trackerCompletedOrNotByCategory(category: TrackerCategory, + date: Date, + flag: Bool, + filter: (UUID, Date) throws -> Bool) throws -> TrackerCategory { + let trackers = category.arrayTrackers + do { + let trackers = try trackers.filter { try flag == filter($0.id, date) } + let cat = TrackerCategory(nameCategory: category.nameCategory, + arrayTrackers: trackers, + isPinned: category.isPinned) + return cat + } catch { + throw error + } } } //MARK: - TrackerViewModelProtocol extension TrackerViewModel: TrackerViewModelProtocol { - func deleteCategory(nameCategory: String) -> Result { - trackerCategoryStore.deleteCategory(nameCategory: nameCategory) - } - func deleteTracker(_ id: UUID) -> Result { trackerStore.deleteTracker(id) } + func deleteTrackersRecord(id: UUID) -> Result { + trackerRecordStore.deleteTrackerRecord(id: id) + } + func addCategory(nameCategory: String) -> Result { trackerCategoryStore.addCategory(nameCategory) } @@ -284,16 +280,7 @@ extension TrackerViewModel: TrackerViewModelProtocol { } func updateCompletedTrackers(tracker: Tracker, date: Date) -> Result { - switch completedTrackers { - case .success(let compTrack): - if let recordTracker = compTrack.first(where: { $0.id == tracker.id && - Calendar.current.isDate($0.date, inSameDayAs: date) }) { - return updateTrackerRecord(recordTracker) - } - return updateTrackerRecord(TrackerRecord(id: tracker.id, date: date)) - case .failure(let error): - return .failure(error) - } + return deleteOrCreateTrackerRecord(id: tracker.id, date: date) } func getShowListTrackerSearchForName(_ searchCategory: [TrackerCategory]) { @@ -303,7 +290,8 @@ extension TrackerViewModel: TrackerViewModelProtocol { func getIsComplited(tracker: Tracker, date: Date) -> Result{ switch completedTrackers { case .success(let compTrack): - return .success(compTrack.contains (where: { $0.id == tracker.id && Calendar.current.isDate(date, equalTo: $0.date, toGranularity: .day)})) + return .success(compTrack.contains (where: { $0.id == tracker.id && + Calendar.current.isDate(date, equalTo: $0.date, toGranularity: .day)})) case .failure(let error): return .failure(error) } @@ -331,26 +319,12 @@ extension TrackerViewModel: TrackerViewModelProtocol { getShowListTrackersForDay(date: date) } - func getToDayTrackers(date: Date) { - getShowListTrackersForDay(date: date) + func getNotCompleted(date: Date, flag: Bool) { + filterListTrackersNotCompleted(date: date, flag: flag) } - func getNotCompleted(date: Date) { - switch category { - case .success(let cat): - filterListTrackersNotCompleted(trackerCategory: cat, date: date) - case .failure(let error): - visibleCategory = .failure(error) - } - } - - func getCompleted(date: Date) { - switch category { - case .success(let cat): - filterListTrackersCompleted(trackerCategory: cat, date: date) - case .failure(let error): - visibleCategory = .failure(error) - } + func getCompleted(date: Date, flag: Bool) { + filterListTrackersCompleted(date: date, flag: flag) } func setFilterState(state: FiltersState) { @@ -364,6 +338,14 @@ extension TrackerViewModel: TrackerViewModelProtocol { func getSelectFilter() -> String? { selectFilter } + + func getCountTrackerCompleted(id: UUID) -> Result { + trackerRecordStore.getCountTrackerRecord(id: id) + } + + func getIsCategoryForDay() -> Bool { + filterListTrackersWeekDay.isEmpty + } } //MARK: - TrackerCategoryStoreDelegate From 121bcb30e470dc659dcdaaeef690a7586c811096 Mon Sep 17 00:00:00 2001 From: Grifon Date: Sat, 6 Jan 2024 16:23:53 +0300 Subject: [PATCH 12/24] refactoring --- Tracker.xcodeproj/project.pbxproj | 84 +++-- .../xcdebugger/Breakpoints_v2.xcbkptlist | 227 ++++++------ .../AnalyticsService/AnalyticsService.swift | 25 +- Tracker/Application/AppDelegate.swift | 13 +- Tracker/Application/SceneDelegate.swift | 7 +- .../Model.xcdatamodel/contents | 2 - .../Domain/Helpers/DaysValueTransformer.swift | 14 +- .../Domain/Store/PinnedCategoryStore.swift | 16 +- .../Domain/Store/TrackerCategoryStore.swift | 36 +- Tracker/Domain/Store/TrackerRecordStore.swift | 58 ++- Tracker/Domain/Store/TrackerStore.swift | 56 ++- Tracker/Extension/Date.swift | 3 +- Tracker/Extension/UITextField.swift | 8 +- Tracker/Helpers/Colors.swift | 1 - Tracker/Helpers/HandlerResultType.swift | 29 +- Tracker/Models/Enum/Errors.swift | 58 ++- Tracker/Models/Enum/StatisticsEvent.swift | 31 ++ ...elpersEnum.swift => UserDefaultKeys.swift} | 1 + Tracker/Models/GeometricParams.swift | 6 +- Tracker/Models/PinnTrackerModel.swift | 3 +- Tracker/Models/TrackerCategoryModel.swift | 15 +- Tracker/Models/TrackerModel.swift | 9 +- .../Resources/en.lproj/Localizable.strings | 23 ++ .../Resources/ru.lproj/Localizable.strings | 23 ++ .../OnboardingPageViewController.swift | 39 ++- .../OnboardingViewController.swift | 21 +- .../TabBarController.swift | 23 +- .../CreateTrackerViewController.swift | 251 +++++++++---- .../EventSelectionViewController.swift | 66 +++- .../FiltersViewController.swift | 60 +++- .../NewCategoriViewController.swift | 68 ++-- .../ScheduleViewController.swift | 64 ++-- .../SelectCategoriesViewController.swift | 186 +++++++--- .../StatisticsViewController.swift | 90 +++-- .../TrackersViewController.swift | 331 ++++++++++++------ Tracker/ViewModel/CategoryViewModel.swift | 58 +-- Tracker/ViewModel/EditTrackerViewModel.swift | 58 ++- Tracker/ViewModel/FiltersViewModel.swift | 4 +- Tracker/ViewModel/ShedulViewModel.swift | 3 +- Tracker/ViewModel/StatisticViewModel.swift | 9 +- Tracker/ViewModel/TrackerViewModel.swift | 204 ++++++----- .../ColorCollectionViewCell.swift | 19 +- .../EmojiCollectionViewCell.swift | 19 +- .../TrackersCollectionViewCell.swift | 78 +++-- .../CustomViews}/HeaderReusableView.swift | 15 +- .../CustomViews}/StatisticView.swift | 28 +- .../CustomViews}/TrackerCollectionView.swift | 8 +- .../CreateTrackerTableViewCell.swift | 31 +- .../TableViewCell}/CustomTableViewCell.swift | 30 +- .../TableViewCell}/WeekDayTableViewCell.swift | 29 +- TrackerTests/TrackerTests.swift | 12 +- .../testTrackersViewControllerDark.1.png | Bin 0 -> 97098 bytes .../testTrackersViewControllerLight.1.png | Bin 0 -> 85463 bytes .../TrackerTests/testViewController.1.png | Bin 197401 -> 97098 bytes .../TrackerTests/testViewController.2.png | Bin 0 -> 85463 bytes 55 files changed, 1704 insertions(+), 848 deletions(-) create mode 100644 Tracker/Models/Enum/StatisticsEvent.swift rename Tracker/Models/Enum/{HelpersEnum.swift => UserDefaultKeys.swift} (93%) rename Tracker/{Onboarding => ViewControllers/OnboardingViewControllers}/OnboardingPageViewController.swift (65%) rename Tracker/{Onboarding => ViewControllers/OnboardingViewControllers}/OnboardingViewController.swift (92%) rename Tracker/{TabBarController => ViewControllers}/TabBarController.swift (83%) rename Tracker/{TabBarController/TrackersViews => ViewControllers}/ViewControllers/CreateTrackerViewController.swift (76%) rename Tracker/{TabBarController/TrackersViews => ViewControllers}/ViewControllers/EventSelectionViewController.swift (69%) rename Tracker/{TabBarController/TrackersViews => ViewControllers}/ViewControllers/FiltersViewController.swift (68%) rename Tracker/{TabBarController/TrackersViews => ViewControllers}/ViewControllers/NewCategoriViewController.swift (82%) rename Tracker/{TabBarController/TrackersViews => ViewControllers}/ViewControllers/ScheduleViewController.swift (79%) rename Tracker/{TabBarController/TrackersViews => ViewControllers}/ViewControllers/SelectCategoriesViewController.swift (68%) rename Tracker/{TabBarController/TrackersViews/StatisticViews => ViewControllers/ViewControllers}/StatisticsViewController.swift (74%) rename Tracker/{TabBarController/TrackersViews => ViewControllers}/ViewControllers/TrackersViewController.swift (73%) rename Tracker/{TabBarController/TrackersViews/Cell => Views/CollectionViewCell}/ColorCollectionViewCell.swift (90%) rename Tracker/{TabBarController/TrackersViews/Cell => Views/CollectionViewCell}/EmojiCollectionViewCell.swift (87%) rename Tracker/{TabBarController/TrackersViews/Cell => Views/CollectionViewCell}/TrackersCollectionViewCell.swift (85%) rename Tracker/{TabBarController/TrackersViews/SuplementaryView => Views/CustomViews}/HeaderReusableView.swift (85%) rename Tracker/{TabBarController/TrackersViews/Cell => Views/CustomViews}/StatisticView.swift (76%) rename Tracker/{TabBarController/TrackersViews/CollectionView => Views/CustomViews}/TrackerCollectionView.swift (71%) rename Tracker/{TabBarController/TrackersViews/Cell => Views/TableViewCell}/CreateTrackerTableViewCell.swift (87%) rename Tracker/{TabBarController/TrackersViews/Cell => Views/TableViewCell}/CustomTableViewCell.swift (85%) rename Tracker/{TabBarController/TrackersViews/Cell => Views/TableViewCell}/WeekDayTableViewCell.swift (74%) create mode 100644 TrackerTests/__Snapshots__/TrackerTests/testTrackersViewControllerDark.1.png create mode 100644 TrackerTests/__Snapshots__/TrackerTests/testTrackersViewControllerLight.1.png create mode 100644 TrackerTests/__Snapshots__/TrackerTests/testViewController.2.png diff --git a/Tracker.xcodeproj/project.pbxproj b/Tracker.xcodeproj/project.pbxproj index 133d2f3..8163826 100644 --- a/Tracker.xcodeproj/project.pbxproj +++ b/Tracker.xcodeproj/project.pbxproj @@ -36,7 +36,7 @@ 1A704D3F2B2B76C60035C94D /* EmojiCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A704D3E2B2B76C60035C94D /* EmojiCollectionViewCell.swift */; }; 1A760A9D2B29EDC900B0C1CC /* EditTrackerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A760A9C2B29EDC900B0C1CC /* EditTrackerViewModel.swift */; }; 1A79C42D2B39C3E4009CE43A /* StatisticViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A79C42C2B39C3E4009CE43A /* StatisticViewModel.swift */; }; - 1A79C42F2B39FBCC009CE43A /* HelpersEnum.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A79C42E2B39FBCC009CE43A /* HelpersEnum.swift */; }; + 1A79C42F2B39FBCC009CE43A /* UserDefaultKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A79C42E2B39FBCC009CE43A /* UserDefaultKeys.swift */; }; 1A80951A2B3964A000591911 /* StatisticView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A8095192B3964A000591911 /* StatisticView.swift */; }; 1A83156E2B2C786000097DE7 /* HandlerResultType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A83156D2B2C786000097DE7 /* HandlerResultType.swift */; }; 1A84F3792AC87F4E001DD42F /* GeometricParams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A84F3782AC87F4E001DD42F /* GeometricParams.swift */; }; @@ -49,6 +49,7 @@ 1A8A7FF22AC55D980038CB42 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1A8A7FF02AC55D980038CB42 /* LaunchScreen.storyboard */; }; 1A8ED50D2AEFEA1900BA4EE4 /* OnboardingPageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A8ED50C2AEFEA1900BA4EE4 /* OnboardingPageViewController.swift */; }; 1A8ED50F2AEFEB5A00BA4EE4 /* OnboardingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A8ED50E2AEFEB5A00BA4EE4 /* OnboardingViewController.swift */; }; + 1A92B6872B488439006C18C2 /* StatisticsEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A92B6862B488439006C18C2 /* StatisticsEvent.swift */; }; 1A959AF42AE24C44001C0EAE /* UIColorMarshalling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A959AF32AE24C44001C0EAE /* UIColorMarshalling.swift */; }; 1A9AC5AF2AF373F000A9213D /* Observable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A9AC5AE2AF373F000A9213D /* Observable.swift */; }; 1A9AC5B42AF37AED00A9213D /* TrackerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A9AC5B32AF37AED00A9213D /* TrackerViewModel.swift */; }; @@ -123,7 +124,7 @@ 1A704D3E2B2B76C60035C94D /* EmojiCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiCollectionViewCell.swift; sourceTree = ""; }; 1A760A9C2B29EDC900B0C1CC /* EditTrackerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditTrackerViewModel.swift; sourceTree = ""; }; 1A79C42C2B39C3E4009CE43A /* StatisticViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatisticViewModel.swift; sourceTree = ""; }; - 1A79C42E2B39FBCC009CE43A /* HelpersEnum.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HelpersEnum.swift; sourceTree = ""; }; + 1A79C42E2B39FBCC009CE43A /* UserDefaultKeys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultKeys.swift; sourceTree = ""; }; 1A8095192B3964A000591911 /* StatisticView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatisticView.swift; sourceTree = ""; }; 1A83156D2B2C786000097DE7 /* HandlerResultType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandlerResultType.swift; sourceTree = ""; }; 1A84F3782AC87F4E001DD42F /* GeometricParams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeometricParams.swift; sourceTree = ""; }; @@ -138,6 +139,7 @@ 1A8A7FF32AC55D980038CB42 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 1A8ED50C2AEFEA1900BA4EE4 /* OnboardingPageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingPageViewController.swift; sourceTree = ""; }; 1A8ED50E2AEFEB5A00BA4EE4 /* OnboardingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingViewController.swift; sourceTree = ""; }; + 1A92B6862B488439006C18C2 /* StatisticsEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatisticsEvent.swift; sourceTree = ""; }; 1A959AF32AE24C44001C0EAE /* UIColorMarshalling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIColorMarshalling.swift; sourceTree = ""; }; 1A9AC5AE2AF373F000A9213D /* Observable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Observable.swift; sourceTree = ""; }; 1A9AC5B32AF37AED00A9213D /* TrackerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerViewModel.swift; sourceTree = ""; }; @@ -248,18 +250,21 @@ path = Helpers; sourceTree = ""; }; - 1A3ED44D2ACDE0B40006EBBB /* CollectionView */ = { + 1A3ED44D2ACDE0B40006EBBB /* CustomViews */ = { isa = PBXGroup; children = ( + 1ABB3D2B2AC9AAB40037B601 /* HeaderReusableView.swift */, + 1A8095192B3964A000591911 /* StatisticView.swift */, 1ADD3D732ACBDBB1005F639F /* TrackerCollectionView.swift */, ); - path = CollectionView; + path = CustomViews; sourceTree = ""; }; 1A3ED44E2ACDE1020006EBBB /* ViewControllers */ = { isa = PBXGroup; children = ( 1A598BB72AC6FAC5009B166D /* TrackersViewController.swift */, + 1A8A7FE92AC55D970038CB42 /* StatisticsViewController.swift */, 1AAC05C22ACC595800AA4B6B /* CreateTrackerViewController.swift */, 1A3ED44B2ACDDE4A0006EBBB /* ScheduleViewController.swift */, 1AA5B2362AD91D1800CB38A2 /* SelectCategoriesViewController.swift */, @@ -281,25 +286,24 @@ path = Store; sourceTree = ""; }; - 1A598BB42AC6F0A9009B166D /* TabBarController */ = { + 1A598BB42AC6F0A9009B166D /* ViewControllers */ = { isa = PBXGroup; children = ( 1A598BB52AC6F352009B166D /* TabBarController.swift */, - 1A598BB92AC6FB97009B166D /* TrackersViews */, + 1A8ED50B2AEFE9AA00BA4EE4 /* OnboardingViewControllers */, + 1A3ED44E2ACDE1020006EBBB /* ViewControllers */, ); - path = TabBarController; + path = ViewControllers; sourceTree = ""; }; - 1A598BB92AC6FB97009B166D /* TrackersViews */ = { + 1A598BB92AC6FB97009B166D /* Views */ = { isa = PBXGroup; children = ( - 1A3ED44E2ACDE1020006EBBB /* ViewControllers */, - 1A3ED44D2ACDE0B40006EBBB /* CollectionView */, - 1AAC05C12ACC56EC00AA4B6B /* SuplementaryView */, - 1A8A576D2ACD603200C060FA /* StatisticViews */, - 1AAC05C02ACC56A500AA4B6B /* Cell */, + 1A3ED44D2ACDE0B40006EBBB /* CustomViews */, + 1A92B6852B487A55006C18C2 /* CollectionViewCell */, + 1AAC05C02ACC56A500AA4B6B /* TableViewCell */, ); - path = TrackersViews; + path = Views; sourceTree = ""; }; 1A84F3772AC87F28001DD42F /* Models */ = { @@ -317,14 +321,6 @@ path = Models; sourceTree = ""; }; - 1A8A576D2ACD603200C060FA /* StatisticViews */ = { - isa = PBXGroup; - children = ( - 1A8A7FE92AC55D970038CB42 /* StatisticsViewController.swift */, - ); - path = StatisticViews; - sourceTree = ""; - }; 1A8A7FD92AC55D960038CB42 = { isa = PBXGroup; children = ( @@ -349,17 +345,17 @@ 1A8A7FE42AC55D970038CB42 /* Tracker */ = { isa = PBXGroup; children = ( - 1AC642702B383AB60072E081 /* AnalyticsService */, + 1A598BB42AC6F0A9009B166D /* ViewControllers */, + 1A598BB92AC6FB97009B166D /* Views */, 1A9AC5B52AF3854A00A9213D /* ViewModel */, - 1A8ED50B2AEFE9AA00BA4EE4 /* Onboarding */, 1A2FC2482AED1E05000156FE /* Domain */, 1A21DD932ADE5A0700583A01 /* CoreData */, - 1A598BB42AC6F0A9009B166D /* TabBarController */, 1A84F3772AC87F28001DD42F /* Models */, 1A3477CB2AD18D2100EDEAED /* Helpers */, 1A2980BB2AC86499007FE528 /* Extension */, 1AAD15662AF14E2D0080EB72 /* PropertyWrappers */, 1AA7BFC52B00E41A00C678FF /* Resources */, + 1AC642702B383AB60072E081 /* AnalyticsService */, 1AF783BD2ACD9D6C0002BB54 /* Application */, 1A8A7FEE2AC55D980038CB42 /* Assets.xcassets */, 1A8A7FF02AC55D980038CB42 /* LaunchScreen.storyboard */, @@ -368,13 +364,23 @@ path = Tracker; sourceTree = ""; }; - 1A8ED50B2AEFE9AA00BA4EE4 /* Onboarding */ = { + 1A8ED50B2AEFE9AA00BA4EE4 /* OnboardingViewControllers */ = { isa = PBXGroup; children = ( 1A8ED50C2AEFEA1900BA4EE4 /* OnboardingPageViewController.swift */, 1A8ED50E2AEFEB5A00BA4EE4 /* OnboardingViewController.swift */, ); - path = Onboarding; + path = OnboardingViewControllers; + sourceTree = ""; + }; + 1A92B6852B487A55006C18C2 /* CollectionViewCell */ = { + isa = PBXGroup; + children = ( + 1A1410692ADD5185001D6DE6 /* ColorCollectionViewCell.swift */, + 1A704D3E2B2B76C60035C94D /* EmojiCollectionViewCell.swift */, + 1AB78AA22AC750A900904AB2 /* TrackersCollectionViewCell.swift */, + ); + path = CollectionViewCell; sourceTree = ""; }; 1A9AC5B52AF3854A00A9213D /* ViewModel */ = { @@ -401,26 +407,14 @@ path = Resources; sourceTree = ""; }; - 1AAC05C02ACC56A500AA4B6B /* Cell */ = { + 1AAC05C02ACC56A500AA4B6B /* TableViewCell */ = { isa = PBXGroup; children = ( 1A8A576B2ACD5F4D00C060FA /* CreateTrackerTableViewCell.swift */, - 1AB78AA22AC750A900904AB2 /* TrackersCollectionViewCell.swift */, - 1A3ED4512ACDEA3E0006EBBB /* WeekDayTableViewCell.swift */, 1A1E94B42AD952EF00FE9A0C /* CustomTableViewCell.swift */, - 1A1410692ADD5185001D6DE6 /* ColorCollectionViewCell.swift */, - 1A704D3E2B2B76C60035C94D /* EmojiCollectionViewCell.swift */, - 1A8095192B3964A000591911 /* StatisticView.swift */, - ); - path = Cell; - sourceTree = ""; - }; - 1AAC05C12ACC56EC00AA4B6B /* SuplementaryView */ = { - isa = PBXGroup; - children = ( - 1ABB3D2B2AC9AAB40037B601 /* HeaderReusableView.swift */, + 1A3ED4512ACDEA3E0006EBBB /* WeekDayTableViewCell.swift */, ); - path = SuplementaryView; + path = TableViewCell; sourceTree = ""; }; 1AAD15662AF14E2D0080EB72 /* PropertyWrappers */ = { @@ -458,7 +452,8 @@ 1AE27C2C2B0604A800C4A925 /* Errors.swift */, 1A6724122B2F108000719A9D /* FiltersState.swift */, 1AC642732B38442E0072E081 /* EventAnalytics.swift */, - 1A79C42E2B39FBCC009CE43A /* HelpersEnum.swift */, + 1A79C42E2B39FBCC009CE43A /* UserDefaultKeys.swift */, + 1A92B6862B488439006C18C2 /* StatisticsEvent.swift */, ); path = Enum; sourceTree = ""; @@ -695,6 +690,7 @@ 1A80951A2B3964A000591911 /* StatisticView.swift in Sources */, 1A8A7FE62AC55D970038CB42 /* AppDelegate.swift in Sources */, 1A3B83AA2AD56323003C3897 /* NameEvent.swift in Sources */, + 1A92B6872B488439006C18C2 /* StatisticsEvent.swift in Sources */, 1A45A6782ADE8E9E0063F08C /* TrackerStore.swift in Sources */, 1AE27C2D2B0604A800C4A925 /* Errors.swift in Sources */, 1AE989302ACAF14B00392315 /* TrackerCategoryModel.swift in Sources */, @@ -728,7 +724,7 @@ 1AAC05C32ACC595800AA4B6B /* CreateTrackerViewController.swift in Sources */, 1A21DD952ADE5A6A00583A01 /* DaysValueTransformer.swift in Sources */, 1A2F9B652AC82FBF00914B60 /* EventSelectionViewController.swift in Sources */, - 1A79C42F2B39FBCC009CE43A /* HelpersEnum.swift in Sources */, + 1A79C42F2B39FBCC009CE43A /* UserDefaultKeys.swift in Sources */, 1AA5B2372AD91D1800CB38A2 /* SelectCategoriesViewController.swift in Sources */, 1A79C42D2B39C3E4009CE43A /* StatisticViewModel.swift in Sources */, 1A8A7FE82AC55D970038CB42 /* SceneDelegate.swift in Sources */, diff --git a/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index 6f4a457..14aed4a 100644 --- a/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -866,8 +866,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "611" - endingLineNumber = "611" + startingLineNumber = "642" + endingLineNumber = "642" landmarkName = "collectionView(_:viewForSupplementaryElementOfKind:at:)" landmarkType = "7"> @@ -1064,8 +1064,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "609" - endingLineNumber = "609" + startingLineNumber = "640" + endingLineNumber = "640" landmarkName = "collectionView(_:viewForSupplementaryElementOfKind:at:)" landmarkType = "7"> @@ -1172,8 +1172,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "594" - endingLineNumber = "594" + startingLineNumber = "625" + endingLineNumber = "625" landmarkName = "collectionView(_:layout:insetForSectionAt:)" landmarkType = "7"> @@ -1188,8 +1188,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "587" - endingLineNumber = "587" + startingLineNumber = "618" + endingLineNumber = "618" landmarkName = "collectionView(_:layout:sizeForItemAt:)" landmarkType = "7"> @@ -1204,8 +1204,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "608" - endingLineNumber = "608" + startingLineNumber = "639" + endingLineNumber = "639" landmarkName = "collectionView(_:viewForSupplementaryElementOfKind:at:)" landmarkType = "7"> @@ -1328,8 +1328,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "603" - endingLineNumber = "603" + startingLineNumber = "634" + endingLineNumber = "634" landmarkName = "collectionView(_:layout:referenceSizeForHeaderInSection:)" landmarkType = "7"> @@ -1455,8 +1455,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "678" - endingLineNumber = "678" + startingLineNumber = "709" + endingLineNumber = "709" landmarkName = "eventSelectionViewControllerDidCancel(_:)" landmarkType = "7"> @@ -1519,8 +1519,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "634" - endingLineNumber = "634" + startingLineNumber = "665" + endingLineNumber = "665" landmarkName = "updateSearchResults(for:)" landmarkType = "7"> @@ -1803,8 +1803,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "642" - endingLineNumber = "642" + startingLineNumber = "673" + endingLineNumber = "673" landmarkName = "updateSearchResults(for:)" landmarkType = "7"> @@ -1851,8 +1851,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "590" - endingLineNumber = "590" + startingLineNumber = "621" + endingLineNumber = "621" landmarkName = "collectionView(_:layout:sizeForItemAt:)" landmarkType = "7"> @@ -2169,8 +2169,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "599" - endingLineNumber = "599" + startingLineNumber = "605" + endingLineNumber = "605" landmarkName = "collectionView(_:viewForSupplementaryElementOfKind:at:)" landmarkType = "7"> @@ -2876,8 +2876,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "643" - endingLineNumber = "643" + startingLineNumber = "674" + endingLineNumber = "674" landmarkName = "updateSearchResults(for:)" landmarkType = "7"> @@ -2924,8 +2924,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "456" - endingLineNumber = "456" + startingLineNumber = "458" + endingLineNumber = "458" landmarkName = "tableView(_:cellForRowAt:)" landmarkType = "7"> @@ -3847,8 +3847,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "530" - endingLineNumber = "530" + startingLineNumber = "531" + endingLineNumber = "531" landmarkName = "collectionView(_:cellForItemAt:)" landmarkType = "7"> @@ -3863,8 +3863,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "566" - endingLineNumber = "566" + startingLineNumber = "597" + endingLineNumber = "597" landmarkName = "collectionView(_:contextMenuConfigurationForItemsAt:point:)" landmarkType = "7"> @@ -3879,8 +3879,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "555" - endingLineNumber = "555" + startingLineNumber = "586" + endingLineNumber = "586" landmarkName = "collectionView(_:contextMenuConfigurationForItemsAt:point:)" landmarkType = "7"> @@ -4145,8 +4145,8 @@ filePath = "Tracker/Domain/Store/TrackerCategoryStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "40" - endingLineNumber = "40" + startingLineNumber = "43" + endingLineNumber = "43" landmarkName = "fetchedCategoryResultController" landmarkType = "24"> @@ -4319,8 +4319,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "674" - endingLineNumber = "674" + startingLineNumber = "705" + endingLineNumber = "705" landmarkName = "eventSelectionViewController(vc:nameCategories:tracker:)" landmarkType = "7"> @@ -4493,8 +4493,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "129" - endingLineNumber = "129" + startingLineNumber = "142" + endingLineNumber = "142" landmarkName = "addNewTracker(_:nameCategory:)" landmarkType = "7"> @@ -4525,8 +4525,8 @@ filePath = "Tracker/Domain/Store/TrackerRecordStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "158" - endingLineNumber = "158" + startingLineNumber = "177" + endingLineNumber = "177" landmarkName = "deleteOrCreateTrackerRecord(id:date:)" landmarkType = "7"> @@ -4590,6 +4590,21 @@ endingLineNumber = "160" offsetFromSymbolStart = "160"> + + @@ -4603,8 +4618,8 @@ filePath = "Tracker/Domain/Store/TrackerRecordStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "87" - endingLineNumber = "87" + startingLineNumber = "97" + endingLineNumber = "97" landmarkName = "addNewTrackerRecord(id:date:)" landmarkType = "7"> @@ -4822,8 +4837,8 @@ filePath = "Tracker/Domain/Store/TrackerCategoryStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "123" - endingLineNumber = "123" + startingLineNumber = "137" + endingLineNumber = "137" landmarkName = "controllerDidChangeContent(_:)" landmarkType = "7"> @@ -4838,8 +4853,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "205" - endingLineNumber = "205" + startingLineNumber = "226" + endingLineNumber = "226" landmarkName = "addPinnedCategory(_:pinnedCategory:)" landmarkType = "7"> @@ -5105,8 +5120,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "256" - endingLineNumber = "256" + startingLineNumber = "257" + endingLineNumber = "257" landmarkName = "bind()" landmarkType = "7"> @@ -5168,8 +5183,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "261" - endingLineNumber = "261" + startingLineNumber = "262" + endingLineNumber = "262" landmarkName = "bind()" landmarkType = "7"> @@ -5200,8 +5215,8 @@ filePath = "Tracker/ViewModel/CategoryViewModel.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "47" - endingLineNumber = "47" + startingLineNumber = "51" + endingLineNumber = "51" landmarkName = "init(trackerCategoryStore:trackerStore:trackerRecordStore:category:)" landmarkType = "7"> @@ -5232,8 +5247,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "635" - endingLineNumber = "635" + startingLineNumber = "641" + endingLineNumber = "641" landmarkName = "editTracker(vc:editTracker:count:nameCategory:indexPath:)" landmarkType = "7"> @@ -5248,8 +5263,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "577" - endingLineNumber = "577" + startingLineNumber = "583" + endingLineNumber = "583" landmarkName = "collectionView(_:layout:sizeForItemAt:)" landmarkType = "7"> @@ -5264,8 +5279,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "590" - endingLineNumber = "590" + startingLineNumber = "596" + endingLineNumber = "596" landmarkName = "collectionView(_:layout:referenceSizeForHeaderInSection:)" landmarkType = "7"> @@ -5280,8 +5295,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "601" - endingLineNumber = "601" + startingLineNumber = "607" + endingLineNumber = "607" landmarkName = "collectionView(_:viewForSupplementaryElementOfKind:at:)" landmarkType = "7"> @@ -5328,8 +5343,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "604" - endingLineNumber = "604" + startingLineNumber = "610" + endingLineNumber = "610" landmarkName = "collectionView(_:viewForSupplementaryElementOfKind:at:)" landmarkType = "7"> @@ -5376,8 +5391,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "549" - endingLineNumber = "549" + startingLineNumber = "555" + endingLineNumber = "555" landmarkName = "collectionView(_:didSelectItemAt:)" landmarkType = "7"> @@ -5392,8 +5407,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "556" - endingLineNumber = "556" + startingLineNumber = "562" + endingLineNumber = "562" landmarkName = "collectionView(_:didSelectItemAt:)" landmarkType = "7"> @@ -5408,8 +5423,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "524" - endingLineNumber = "524" + startingLineNumber = "530" + endingLineNumber = "530" landmarkName = "collectionView(_:cellForItemAt:)" landmarkType = "7"> @@ -5440,8 +5455,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "639" - endingLineNumber = "639" + startingLineNumber = "645" + endingLineNumber = "645" landmarkName = "editTracker(vc:editTracker:count:nameCategory:indexPath:)" landmarkType = "7"> @@ -5503,8 +5518,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "564" - endingLineNumber = "564" + startingLineNumber = "570" + endingLineNumber = "570" landmarkName = "collectionView(_:didDeselectItemAt:)" landmarkType = "7"> @@ -5519,8 +5534,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "569" - endingLineNumber = "569" + startingLineNumber = "575" + endingLineNumber = "575" landmarkName = "collectionView(_:didDeselectItemAt:)" landmarkType = "7"> @@ -5535,8 +5550,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "105" - endingLineNumber = "105" + startingLineNumber = "114" + endingLineNumber = "114" landmarkName = "searchTracker(id:)" landmarkType = "7"> @@ -5583,8 +5598,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "103" - endingLineNumber = "103" + startingLineNumber = "112" + endingLineNumber = "112" landmarkName = "searchTracker(id:)" landmarkType = "7"> @@ -5599,8 +5614,8 @@ filePath = "Tracker/ViewModel/TrackerViewModel.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "354" - endingLineNumber = "354" + startingLineNumber = "391" + endingLineNumber = "391" landmarkName = "storeCategory(trackerCategoryStore:)" landmarkType = "7"> @@ -5615,8 +5630,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "686" - endingLineNumber = "686" + startingLineNumber = "717" + endingLineNumber = "717" landmarkName = "trackerViewController(vc:nameCategory:tracker:)" landmarkType = "7"> @@ -5723,8 +5738,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "220" - endingLineNumber = "220" + startingLineNumber = "242" + endingLineNumber = "242" landmarkName = "controllerDidChangeContent(_:)" landmarkType = "7"> @@ -5816,8 +5831,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "285" - endingLineNumber = "285" + startingLineNumber = "287" + endingLineNumber = "287" landmarkName = "didTapGreateButton()" landmarkType = "7"> @@ -6104,8 +6119,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "155" - endingLineNumber = "155" + startingLineNumber = "171" + endingLineNumber = "171" landmarkName = "updateTracker(tracker:nameCategory:)" landmarkType = "7"> @@ -6197,8 +6212,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "143" - endingLineNumber = "143" + startingLineNumber = "158" + endingLineNumber = "158" landmarkName = "updateTracker(tracker:nameCategory:)" landmarkType = "7"> @@ -6275,8 +6290,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "300" - endingLineNumber = "300" + startingLineNumber = "302" + endingLineNumber = "302" landmarkName = "didTapGreateButton()" landmarkType = "7"> @@ -6323,8 +6338,8 @@ filePath = "Tracker/ViewModel/TrackerViewModel.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "251" - endingLineNumber = "251" + startingLineNumber = "266" + endingLineNumber = "266" landmarkName = "updateTracker(tracker:nameCategory:)" landmarkType = "7"> @@ -6339,8 +6354,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "139" - endingLineNumber = "139" + startingLineNumber = "154" + endingLineNumber = "154" landmarkName = "updateTracker(tracker:nameCategory:)" landmarkType = "7"> @@ -6355,8 +6370,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "292" - endingLineNumber = "292" + startingLineNumber = "294" + endingLineNumber = "294" landmarkName = "didTapGreateButton()" landmarkType = "7"> @@ -6371,8 +6386,8 @@ filePath = "Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "304" - endingLineNumber = "304" + startingLineNumber = "306" + endingLineNumber = "306" landmarkName = "didTapGreateButton()" landmarkType = "7"> @@ -6688,10 +6703,10 @@ filePath = "Tracker/ViewModel/TrackerViewModel.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "155" - endingLineNumber = "155" - landmarkName = "comparisonWeekDays(date:day:)" - landmarkType = "7"> + startingLineNumber = "168" + endingLineNumber = "168" + landmarkName = "TrackerViewModel" + landmarkType = "21"> diff --git a/Tracker/AnalyticsService/AnalyticsService.swift b/Tracker/AnalyticsService/AnalyticsService.swift index 183686e..645b2f2 100644 --- a/Tracker/AnalyticsService/AnalyticsService.swift +++ b/Tracker/AnalyticsService/AnalyticsService.swift @@ -21,54 +21,63 @@ protocol AnalyticsServiceProtocol { struct AnalyticsService { static let shared = AnalyticsService() private let handler: (MetricEvent) -> Void = { field in - YMMYandexMetrica.reportEvent(field.event.rawValue, parameters: field.params, onFailure: { error in + YMMYandexMetrica.reportEvent(field.event.rawValue, + parameters: field.params, + onFailure: { error in print("REPORT ERROR: %@", error.localizedDescription) }) } } extension AnalyticsService: AnalyticsServiceProtocol { - func open() { + func open() + { let params = [EventFields.screen.rawValue: Screen.Main.rawValue] let metricEvent = MetricEvent(event: .open, params: params) handler(metricEvent) } - func close() { + func close() + { let params = [EventFields.screen.rawValue: Screen.Main.rawValue] let metricEvent = MetricEvent(event: .close, params: params) handler(metricEvent) } - func addTracker() { + func addTracker() + { let params = [EventFields.screen.rawValue: Screen.Main.rawValue, EventFields.item.rawValue: ItemClick.add_track.rawValue] let metricEvent = MetricEvent(event: .click, params: params) handler(metricEvent) } - func track() { + func track() + { let params = [EventFields.screen.rawValue: Screen.Main.rawValue, EventFields.item.rawValue: ItemClick.track.rawValue] let metricEvent = MetricEvent(event: .click, params: params) handler(metricEvent) } - func edit() { + func edit() + { let params = [EventFields.screen.rawValue: Screen.Main.rawValue, EventFields.item.rawValue: ItemClick.edit.rawValue] let metricEvent = MetricEvent(event: .click, params: params) handler(metricEvent) } - func delete() { + func delete() + { let params = [EventFields.screen.rawValue: Screen.Main.rawValue, EventFields.item.rawValue: ItemClick.delete.rawValue] let metricEvent = MetricEvent(event: .click, params: params) handler(metricEvent) } - func filter() { + func filter() + { let params = [EventFields.screen.rawValue: Screen.Main.rawValue, EventFields.item.rawValue: ItemClick.filter.rawValue] let metricEvent = MetricEvent(event: .click, params: params) diff --git a/Tracker/Application/AppDelegate.swift b/Tracker/Application/AppDelegate.swift index cd30e59..e31d71d 100644 --- a/Tracker/Application/AppDelegate.swift +++ b/Tracker/Application/AppDelegate.swift @@ -18,7 +18,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate { (UIApplication.shared.delegate as! AppDelegate).persistentContainer } - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + func application(_ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool + { DaysValueTransformer.register() guard let configuration = YMMYandexMetricaConfiguration(apiKey: apiKeyYMM) else { return true } YMMYandexMetrica.activate(with: configuration) @@ -27,13 +29,18 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // MARK: UISceneSession Lifecycle - func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { + func application(_ application: UIApplication, + configurationForConnecting connectingSceneSession: UISceneSession, + options: UIScene.ConnectionOptions) -> UISceneConfiguration + { // Called when a new scene session is being created. // Use this method to select a configuration to create the new scene with. return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) } - func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { + func application(_ application: UIApplication, + didDiscardSceneSessions sceneSessions: Set) + { // Called when the user discards a scene session. // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. // Use this method to release any resources that were specific to the discarded scenes, as they will not return. diff --git a/Tracker/Application/SceneDelegate.swift b/Tracker/Application/SceneDelegate.swift index 3f89076..0c615cc 100644 --- a/Tracker/Application/SceneDelegate.swift +++ b/Tracker/Application/SceneDelegate.swift @@ -11,9 +11,12 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? - @UserDefaultsBacked(key: "is_onboarding") private var isOnboarding + @UserDefaultsBacked(key: UserDefaultKeys.isOnboarding.rawValue) private var isOnboarding - func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + func scene(_ scene: UIScene, + willConnectTo session: UISceneSession, + options connectionOptions: UIScene.ConnectionOptions) + { guard let scene = (scene as? UIWindowScene) else { return } window = UIWindow(windowScene: scene) let tabBarViewController = TabBarController() diff --git a/Tracker/CoreData/Model.xcdatamodeld/Model.xcdatamodel/contents b/Tracker/CoreData/Model.xcdatamodeld/Model.xcdatamodel/contents index 6ed526e..ee0d0e1 100644 --- a/Tracker/CoreData/Model.xcdatamodeld/Model.xcdatamodel/contents +++ b/Tracker/CoreData/Model.xcdatamodeld/Model.xcdatamodel/contents @@ -18,11 +18,9 @@ - - \ No newline at end of file diff --git a/Tracker/Domain/Helpers/DaysValueTransformer.swift b/Tracker/Domain/Helpers/DaysValueTransformer.swift index e7af15e..52f53b0 100644 --- a/Tracker/Domain/Helpers/DaysValueTransformer.swift +++ b/Tracker/Domain/Helpers/DaysValueTransformer.swift @@ -12,16 +12,22 @@ final class DaysValueTransformer: ValueTransformer { override class func transformedValueClass() -> AnyClass { NSData.self } override class func allowsReverseTransformation() -> Bool { true } - static func register() { - ValueTransformer.setValueTransformer(DaysValueTransformer(), forName: NSValueTransformerName(rawValue: String(describing: DaysValueTransformer.self))) + static func register() + { + ValueTransformer.setValueTransformer(DaysValueTransformer(), + forName: NSValueTransformerName(rawValue: + String(describing: + DaysValueTransformer.self))) } - override func transformedValue(_ value: Any?) -> Any? { + override func transformedValue(_ value: Any?) -> Any? + { guard let days = value as? [WeekDay] else { return nil } return try? JSONEncoder().encode(days) } - override func reverseTransformedValue(_ value: Any?) -> Any? { + override func reverseTransformedValue(_ value: Any?) -> Any? + { guard let data = value as? NSData else { return nil } return try? JSONDecoder().decode([WeekDay].self, from: data as Data) } diff --git a/Tracker/Domain/Store/PinnedCategoryStore.swift b/Tracker/Domain/Store/PinnedCategoryStore.swift index 706cb43..025a4c6 100644 --- a/Tracker/Domain/Store/PinnedCategoryStore.swift +++ b/Tracker/Domain/Store/PinnedCategoryStore.swift @@ -34,7 +34,8 @@ final class PinnedCategoryStore: NSObject { } private extension PinnedCategoryStore { - func save() -> Result { + func save() -> Result + { do { try context.save() return .success(()) @@ -43,7 +44,8 @@ private extension PinnedCategoryStore { } } - func searchCategoryById(id: UUID) throws -> PinnCategoryCoreData? { + func searchCategoryById(id: UUID) throws -> PinnCategoryCoreData? + { let request = NSFetchRequest(entityName: "\(PinnCategoryCoreData.self)") request.returnsObjectsAsFaults = false guard let keyPath = (\PinnCategoryCoreData.trackerId)._kvcKeyPathString @@ -53,8 +55,10 @@ private extension PinnedCategoryStore { } } +//MARK: - PinnedCategoryStoreProtocol extension PinnedCategoryStore: PinnedCategoryStoreProtocol { - func addPinnedCategory(_ nameCategory: String) -> Result { + func addPinnedCategory(_ nameCategory: String) -> Result + { let pinnCategoryCoreData = PinnCategoryCoreData(context: context) pinnCategoryCoreData.nameCategory = nameCategory do { @@ -64,12 +68,12 @@ extension PinnedCategoryStore: PinnedCategoryStoreProtocol { catch { return .failure(error) } } - func deleteAndGetPinnedCategory(_ id: UUID) -> Result { + func deleteAndGetPinnedCategory(_ id: UUID) -> Result + { do { if let pinnCategoryCoreData = try searchCategoryById(id: id) { let nameCategory = pinnCategoryCoreData.nameCategory - pinnCategoryCoreData.nameCategory = nil - pinnCategoryCoreData.trackerId = nil + context.delete(pinnCategoryCoreData) let _ = save() return .success(nameCategory) } diff --git a/Tracker/Domain/Store/TrackerCategoryStore.swift b/Tracker/Domain/Store/TrackerCategoryStore.swift index 8e1ba99..b408c92 100644 --- a/Tracker/Domain/Store/TrackerCategoryStore.swift +++ b/Tracker/Domain/Store/TrackerCategoryStore.swift @@ -25,7 +25,10 @@ protocol TrackerCategoryStoreDelegate: AnyObject { final class TrackerCategoryStore: NSObject { private let textFixed = NSLocalizedString("textFixed", comment: "") weak var delegate: TrackerCategoryStoreDelegate? - @UserDefaultsBacked(key: UserDefaultKeys.isTracker.rawValue) private(set) var isTracker: Bool? + + @UserDefaultsBacked(key: UserDefaultKeys.isTracker.rawValue) + private(set) var isTracker: Bool? + private let context: NSManagedObjectContext private lazy var fetchedCategoryResultController: NSFetchedResultsController = { @@ -43,7 +46,8 @@ final class TrackerCategoryStore: NSObject { return fetchedResultController }() - convenience override init() { + convenience override init() + { let context = AppDelegate.container.viewContext self.init(context: context) } @@ -54,7 +58,8 @@ final class TrackerCategoryStore: NSObject { } private extension TrackerCategoryStore { - func save() -> Result { + func save() -> Result + { do { try context.save() return .success(()) @@ -63,14 +68,17 @@ private extension TrackerCategoryStore { } } - func searchTrackerCategoryCD(nameCategory: String) throws -> TrackerCategoryCoreData? { + func searchTrackerCategoryCD(nameCategory: String) throws -> TrackerCategoryCoreData? + { let request = NSFetchRequest(entityName: "\(TrackerCategoryCoreData.self)") request.returnsObjectsAsFaults = false - request.predicate = NSPredicate(format: "%K == %@", #keyPath(TrackerCategoryCoreData.nameCategory), nameCategory as CVarArg) + request.predicate = NSPredicate(format: "%K == %@", #keyPath(TrackerCategoryCoreData.nameCategory), + nameCategory as CVarArg) return try context.fetch(request).first } - func update(trackerCategoryCD: TrackerCategoryCoreData, newNameCategory: String) -> Result { + func update(trackerCategoryCD: TrackerCategoryCoreData, newNameCategory: String) -> Result + { trackerCategoryCD.nameCategory = newNameCategory return save() } @@ -78,20 +86,23 @@ private extension TrackerCategoryStore { //MARK: - TrackerCategoryStoreProtocol extension TrackerCategoryStore: TrackerCategoryStoreProtocol { - func addCategory(_ nameCategory: String) -> Result { + func addCategory(_ nameCategory: String) -> Result + { let categoryCoreData = TrackerCategoryCoreData(context: context) categoryCoreData.nameCategory = nameCategory categoryCoreData.isPinned = nameCategory == textFixed ? false : true return save() } - func getListTrackerCategoryCoreData() -> [TrackerCategoryCoreData]? { + func getListTrackerCategoryCoreData() -> [TrackerCategoryCoreData]? + { let trackCatCD = fetchedCategoryResultController.fetchedObjects isTracker = trackCatCD?.first { $0.trakers?.count ?? 0 > 0 } == nil return trackCatCD } - func deleteCategory(nameCategory: String) -> Result { + func deleteCategory(nameCategory: String) -> Result + { do { if let category = try searchTrackerCategoryCD(nameCategory: nameCategory) { context.delete(category) @@ -103,7 +114,9 @@ extension TrackerCategoryStore: TrackerCategoryStoreProtocol { return save() } - func updateNameCategory(newNameCategory: String, oldNameCategory: String) -> Result { + func updateNameCategory(newNameCategory: String, + oldNameCategory: String) -> Result + { do { if let trackCatCD = try searchTrackerCategoryCD(nameCategory: oldNameCategory) { return update(trackerCategoryCD: trackCatCD, newNameCategory: newNameCategory) @@ -117,7 +130,8 @@ extension TrackerCategoryStore: TrackerCategoryStoreProtocol { //MARK: - NSFetchedResultsControllerDelegate extension TrackerCategoryStore: NSFetchedResultsControllerDelegate { - func controllerDidChangeContent(_ controller: NSFetchedResultsController) { + func controllerDidChangeContent(_ controller: NSFetchedResultsController) + { guard let delegate else { return } delegate.storeCategory(trackerCategoryStore: self) diff --git a/Tracker/Domain/Store/TrackerRecordStore.swift b/Tracker/Domain/Store/TrackerRecordStore.swift index eb5b458..14b31c1 100644 --- a/Tracker/Domain/Store/TrackerRecordStore.swift +++ b/Tracker/Domain/Store/TrackerRecordStore.swift @@ -21,13 +21,15 @@ protocol TrackerRecordStoreProtocol { //MARK: - TrackerRecordStoreDelegate protocol TrackerRecordStoreDelegate: AnyObject { - func trackerRecordStore(trackerRecordStore: TrackerRecordStoreProtocol) + func trackerRecordStore(trackerRecordStore: TrackerRecordStoreProtocol, + flag: Bool) } //MARK: - TrackerRecordStore final class TrackerRecordStore: NSObject { weak var delegate: TrackerRecordStoreDelegate? + private var flag: Bool = false private let context: NSManagedObjectContext private lazy var fetchedTrackerRecordResultController: NSFetchedResultsController = { @@ -43,7 +45,8 @@ final class TrackerRecordStore: NSObject { return fetchedResultController }() - convenience override init() { + convenience override init() + { let context = AppDelegate.container.viewContext self.init(context: context) } @@ -55,7 +58,8 @@ final class TrackerRecordStore: NSObject { //MARK: - private extension private extension TrackerRecordStore { - func save() -> Result { + func save() -> Result + { do { try context.save() return .success(()) @@ -64,7 +68,8 @@ private extension TrackerRecordStore { } } - func trackerRecord(from trackerRecordCoreDate: TrackerRecordCoreData) throws -> TrackerRecord { + func trackerRecord(from trackerRecordCoreDate: TrackerRecordCoreData) throws -> TrackerRecord + { guard let id = trackerRecordCoreDate.trackerId else { throw StoreErrors.TrackrerRecordStoreError.decodingErrorInvalidId } @@ -76,18 +81,24 @@ private extension TrackerRecordStore { return trackerRecord } - func updateExistingTrackerRecord(_ trackerRecordCoreData: TrackerRecordCoreData, id: UUID, date: Date) { + func updateExistingTrackerRecord(_ trackerRecordCoreData: TrackerRecordCoreData, + id: UUID, + date: Date) + { trackerRecordCoreData.trackerId = id trackerRecordCoreData.date = date } - func addNewTrackerRecord(id: UUID, date: Date) -> Result { + func addNewTrackerRecord(id: UUID, date: Date) -> Result + { let trackerRecordCoreData = TrackerRecordCoreData(context: context) - updateExistingTrackerRecord(trackerRecordCoreData, id: id, date: date) + updateExistingTrackerRecord(trackerRecordCoreData, id: id, + date: date) return save() } - func deleteTrackerRecord(_ trackerRecordCoreData: TrackerRecordCoreData) -> Result { + func deleteTrackerRecord(_ trackerRecordCoreData: TrackerRecordCoreData) -> Result + { context.delete(trackerRecordCoreData) return save() } @@ -117,7 +128,8 @@ private extension TrackerRecordStore { //MARK: - TrackerRecordStoreProtocol extension TrackerRecordStore: TrackerRecordStoreProtocol { - func treckersRecordsResult() -> Result, Error> { + func treckersRecordsResult() -> Result, Error> + { let trackerRecordsCoreData = fetchedTrackerRecordResultController.fetchedObjects guard let trackerRecordsCoreData else { return .failure(StoreErrors.TrackrerRecordStoreError.loadTrackerRecord) } @@ -129,18 +141,21 @@ extension TrackerRecordStore: TrackerRecordStoreProtocol { } } - func deleteTrackerRecord(id: UUID) -> Result { + func deleteTrackerRecord(id: UUID) -> Result + { do { let trackerRecord = try getLisTrackersRecord(id: id) trackerRecord.forEach { context.delete($0) } - return save() } catch { return .failure(error) } + flag = false + return save() } - func deleteTrackerRecords(trackers: [Tracker]) -> Result { + func deleteTrackerRecords(trackers: [Tracker]) -> Result + { do { try trackers.forEach { let trackerRecords = try getLisTrackersRecord(id: $0.id) @@ -149,12 +164,16 @@ extension TrackerRecordStore: TrackerRecordStoreProtocol { } catch { return .failure(error) } + flag = false return save() } - func deleteOrCreateTrackerRecord(id: UUID, date: Date) -> Result { + func deleteOrCreateTrackerRecord(id: UUID, date: Date) -> Result + { + flag = true do { - if let trackerRecord = try searchTrackerRecordForDate(id: id, date: date) { + if let trackerRecord = try searchTrackerRecordForDate(id: id, + date: date) { return deleteTrackerRecord(trackerRecord) } else { return addNewTrackerRecord(id: id, date: date) @@ -165,7 +184,8 @@ extension TrackerRecordStore: TrackerRecordStoreProtocol { } } - func getCountTrackerRecord(id: UUID) -> Result { + func getCountTrackerRecord(id: UUID) -> Result + { do { let countTrackers = try getLisTrackersRecord(id: id).count return .success(countTrackers) @@ -178,7 +198,8 @@ extension TrackerRecordStore: TrackerRecordStoreProtocol { fetchedTrackerRecordResultController.fetchedObjects?.count } - func getIsTrackerRecord(id: UUID, date: Date) throws -> Bool { + func getIsTrackerRecord(id: UUID, date: Date) throws -> Bool + { do { let tracker = try searchTrackerRecordForDate(id: id, date: date) return tracker != nil @@ -190,8 +211,9 @@ extension TrackerRecordStore: TrackerRecordStoreProtocol { //MARK: - NSFetchedResultsControllerDelegate extension TrackerRecordStore: NSFetchedResultsControllerDelegate { - func controllerDidChangeContent(_ controller: NSFetchedResultsController) { + func controllerDidChangeContent(_ controller: NSFetchedResultsController) + { guard let delegate else { return } - delegate.trackerRecordStore(trackerRecordStore: self) + delegate.trackerRecordStore(trackerRecordStore: self, flag: flag) } } diff --git a/Tracker/Domain/Store/TrackerStore.swift b/Tracker/Domain/Store/TrackerStore.swift index d261550..8125fee 100644 --- a/Tracker/Domain/Store/TrackerStore.swift +++ b/Tracker/Domain/Store/TrackerStore.swift @@ -44,7 +44,8 @@ final class TrackerStore: NSObject { return fetchedResultController }() - convenience override init() { + convenience override init() + { let context = AppDelegate.container.viewContext self.init(context: context) } @@ -56,7 +57,8 @@ final class TrackerStore: NSObject { //MARK: - Private extension private extension TrackerStore { - func tracker(from trackerCoreData: TrackerCoreData) throws -> Tracker { + func tracker(from trackerCoreData: TrackerCoreData) throws -> Tracker + { guard let id = trackerCoreData.id else { assertionFailure("not found ID") throw StoreErrors.TrackrerStoreError.decodingErrorInvalidId @@ -78,10 +80,15 @@ private extension TrackerStore { throw StoreErrors.TrackrerStoreError.decodingErrorInvalidSchedul } - return Tracker(id: id, name: name, color: colorMarshalling.color(from: color), emoji: emoji, schedule: schedul) + return Tracker(id: id, + name: name, + color: colorMarshalling.color(from: color), + emoji: emoji, + schedule: schedul) } - func updateExistingTrackerRecord(trackerCoreData: TrackerCoreData, tracker: Tracker) { + func updateExistingTrackerRecord(trackerCoreData: TrackerCoreData, tracker: Tracker) + { trackerCoreData.id = tracker.id trackerCoreData.colorHex = colorMarshalling.hexString(from: tracker.color) trackerCoreData.emoji = tracker.emoji @@ -89,7 +96,8 @@ private extension TrackerStore { trackerCoreData.schedule = tracker.schedule as NSObject } - func save() -> Result { + func save() -> Result + { do { try context.save() return .success(()) @@ -98,7 +106,8 @@ private extension TrackerStore { } } - func searchTracker(id: UUID) throws -> TrackerCoreData? { + func searchTracker(id: UUID) throws -> TrackerCoreData? + { let request = NSFetchRequest(entityName: "\(TrackerCoreData.self)") request.returnsObjectsAsFaults = false guard let keyPath = (\TrackerCoreData.id)._kvcKeyPathString @@ -108,7 +117,8 @@ private extension TrackerStore { return trackerCD.first } - func searchCategory(name: String) throws -> TrackerCategoryCoreData? { + func searchCategory(name: String) throws -> TrackerCategoryCoreData? + { let request = NSFetchRequest(entityName: "\(TrackerCategoryCoreData.self)") request.returnsObjectsAsFaults = false request.predicate = NSPredicate(format: "%K == %@", @@ -120,11 +130,14 @@ private extension TrackerStore { //MARK: - TrackerStoreProtocol extension TrackerStore: TrackerStoreProtocol { - func addNewTracker(_ tracker: Tracker, nameCategory: String) -> Result { + func addNewTracker(_ tracker: Tracker, + nameCategory: String) -> Result + { do { if let category = try searchCategory(name: nameCategory) { let trackerCoreData = TrackerCoreData(context: context) - updateExistingTrackerRecord(trackerCoreData: trackerCoreData, tracker: tracker) + updateExistingTrackerRecord(trackerCoreData: trackerCoreData, + tracker: tracker) category.addToTrakers(trackerCoreData) return save() } @@ -134,13 +147,16 @@ extension TrackerStore: TrackerStoreProtocol { return save() } - func updateTracker(tracker: Tracker, nameCategory: String) -> Result { + func updateTracker(tracker: Tracker, + nameCategory: String) -> Result + { do { if let categoryCD = try searchCategory(name: nameCategory) { do { if let trackerCD = try searchTracker(id: tracker.id) { trackerCD.category = categoryCD - updateExistingTrackerRecord(trackerCoreData: trackerCD, tracker: tracker) + updateExistingTrackerRecord(trackerCoreData: trackerCD, + tracker: tracker) return save() } } catch { @@ -152,7 +168,8 @@ extension TrackerStore: TrackerStoreProtocol { do { if let trackerCD = try searchTracker(id: tracker.id) { trackerCD.category = catCD - updateExistingTrackerRecord(trackerCoreData: trackerCD, tracker: tracker) + updateExistingTrackerRecord(trackerCoreData: trackerCD, + tracker: tracker) return save() } } catch { @@ -165,7 +182,8 @@ extension TrackerStore: TrackerStoreProtocol { return save() } - func deleteTracker(_ id: UUID) -> Result { + func deleteTracker(_ id: UUID) -> Result + { do { if let trackerCoreData = try searchTracker(id: id) { context.delete(trackerCoreData) @@ -177,7 +195,8 @@ extension TrackerStore: TrackerStoreProtocol { return save() } - func deleteTrackers(trackers: [Tracker]) -> Result { + func deleteTrackers(trackers: [Tracker]) -> Result + { do { try trackers .forEach { if let tracker = try searchTracker(id: $0.id) { context.delete(tracker) @@ -189,7 +208,8 @@ extension TrackerStore: TrackerStoreProtocol { } } - func getTrackers(_ objects: [TrackerCoreData]) -> Result<[Tracker], Error> { + func getTrackers(_ objects: [TrackerCoreData]) -> Result<[Tracker], Error> + { let _ = fetchedTrackerResultController do { let trackers = try objects.map({ try tracker(from: $0) }).sorted { $0.name < $1.name } @@ -199,7 +219,8 @@ extension TrackerStore: TrackerStoreProtocol { } } - func addPinnedCategory(_ id: UUID, pinnedCategory: PinnCategoryCoreData) -> Result { + func addPinnedCategory(_ id: UUID, pinnedCategory: PinnCategoryCoreData) -> Result + { do { if let trackerCD = try searchTracker(id: id) { pinnedCategory.addToTracker(trackerCD) @@ -215,7 +236,8 @@ extension TrackerStore: TrackerStoreProtocol { //MARK: - NSFetchedResultsControllerDelegate extension TrackerStore: NSFetchedResultsControllerDelegate { - func controllerDidChangeContent(_ controller: NSFetchedResultsController) { + func controllerDidChangeContent(_ controller: NSFetchedResultsController) + { guard let delegate else { return } delegate.updateTracker(self) } diff --git a/Tracker/Extension/Date.swift b/Tracker/Extension/Date.swift index bf53bb3..088d2bf 100644 --- a/Tracker/Extension/Date.swift +++ b/Tracker/Extension/Date.swift @@ -9,7 +9,8 @@ import Foundation extension Date { var ignoringTime: Date { - let dateComponents = Calendar.current.dateComponents([.day, .month, .year], from: self) + let dateComponents = Calendar.current.dateComponents([.day, .month, .year], + from: self) return Calendar.current.date(from: dateComponents) ?? Date() } } diff --git a/Tracker/Extension/UITextField.swift b/Tracker/Extension/UITextField.swift index 418863e..c585fb9 100644 --- a/Tracker/Extension/UITextField.swift +++ b/Tracker/Extension/UITextField.swift @@ -8,8 +8,12 @@ import UIKit extension UITextField { - func indent(size:CGFloat) { - self.leftView = UIView(frame: CGRect(x: self.frame.minX, y: self.frame.minY, width: size, height: self.frame.height)) + func indent(size:CGFloat) + { + self.leftView = UIView(frame: CGRect(x: self.frame.minX, + y: self.frame.minY, + width: size, + height: self.frame.height)) self.leftViewMode = .always } } diff --git a/Tracker/Helpers/Colors.swift b/Tracker/Helpers/Colors.swift index a5a75f7..d8694e3 100644 --- a/Tracker/Helpers/Colors.swift +++ b/Tracker/Helpers/Colors.swift @@ -15,7 +15,6 @@ final class Colors { return UIColor.blackDay } else { return UIColor.whiteDay -// return UIColor(red: 0.8, green: 0.5, blue: 0.8, alpha: 1) } } diff --git a/Tracker/Helpers/HandlerResultType.swift b/Tracker/Helpers/HandlerResultType.swift index c571e0b..b55acf6 100644 --- a/Tracker/Helpers/HandlerResultType.swift +++ b/Tracker/Helpers/HandlerResultType.swift @@ -8,30 +8,41 @@ import UIKit final class HandlerResultType { - func resultTypeHandler(_ listCategory: Result, handler: (Value) -> Void) { + func resultTypeHandler(_ listCategory: Result, vc: UIViewController, + handler: (Value) -> Void) + { switch listCategory { case .success(let newValue): handler(newValue) case .failure(let error): - showMessageErrorAlert(message: error.localizedDescription) + showMessageErrorAlert(message: error.localizedDescription, + viewController: vc) } } - func resultTypeHandlerGetValue(_ value: Result) -> Value? { + func resultTypeHandlerGetValue(_ value: Result, + vc: UIViewController) -> Value? + { switch value { case .success(let newValue): return newValue case .failure(let error): - showMessageErrorAlert(message: error.localizedDescription) + showMessageErrorAlert(message: error.localizedDescription, + viewController: vc) return nil } } - private func showMessageErrorAlert(message: String) { - let alert = UIAlertController(title: nil, message: message, preferredStyle: .alert) - let action = UIAlertAction(title: "Ok", style: .cancel) { _ in + private func showMessageErrorAlert(message: String, viewController: UIViewController) + { + let alert = UIAlertController(title: nil, + message: message, + preferredStyle: .alert) + viewController.present(alert, animated: true) + + DispatchQueue.main.asyncAfter(deadline: .now() + 3, execute: { alert.dismiss(animated: true) - } - alert.addAction(action) + }) } } diff --git a/Tracker/Models/Enum/Errors.swift b/Tracker/Models/Enum/Errors.swift index a18efcd..68e629a 100644 --- a/Tracker/Models/Enum/Errors.swift +++ b/Tracker/Models/Enum/Errors.swift @@ -8,8 +8,19 @@ import Foundation enum StoreErrors { enum TrackrerCategoryStoreError: Error { - case decodingErrorInvalidNameCategori + case decodingErrorInvalidNameCategory case getCategoryCoreDataError + + var localizedDescription: String { + var localizedDescription = "" + switch self { + case .decodingErrorInvalidNameCategory: + localizedDescription = NSLocalizedString("decodingErrorInvalidNameCategory", comment: "") + case .getCategoryCoreDataError: + localizedDescription = NSLocalizedString("getCategoryCoreDataError", comment: "") + } + return localizedDescription + } } enum TrackrerStoreError: Error { @@ -19,21 +30,56 @@ enum StoreErrors { case decodingErrorInvalidColor case decodingErrorInvalidEmoji case decodingErrorInvalidSchedul + + var localizedDescription: String { + var localizedDescription = "" + switch self { + case .getTrackerError: + localizedDescription = NSLocalizedString("getTrackerError", comment: "") + case .decodingErrorInvalidName: + localizedDescription = NSLocalizedString("decodingErrorInvalidName", comment: "") + case .decodingErrorInvalidId: + localizedDescription = NSLocalizedString("decodingErrorInvalidId", comment: "") + case .decodingErrorInvalidColor: + localizedDescription = NSLocalizedString("decodingErrorInvalidColor", comment: "") + case .decodingErrorInvalidEmoji: + localizedDescription = NSLocalizedString("decodingErrorInvalidEmoji", comment: "") + case .decodingErrorInvalidSchedul: + localizedDescription = NSLocalizedString("decodingErrorInvalidSchedul", comment: "") + } + + return localizedDescription + } } enum NSSetError: Error { case transformationErrorInvalid + + var localizedDescription: String { + NSLocalizedString("transformationErrorInvalid", comment: "") + } } enum TrackrerRecordStoreError: Error { case decodingErrorInvalidId case decodingErrorInvalidDate - case deleteError case loadTrackerRecord case getTrackerRecord - } - - enum PinnedCategoryStoreError: Error { - case decodingErrorInvalidName + + var localizedDescription: String { + var localizedDescription = "" + switch self { + case .decodingErrorInvalidId: + localizedDescription = NSLocalizedString("decodingErrorInvalidId", comment: "") + case .decodingErrorInvalidDate: + localizedDescription = NSLocalizedString("decodingErrorInvalidDate", comment: "") + case .loadTrackerRecord: + localizedDescription = NSLocalizedString("loadTrackerRecord", comment: "") + case .getTrackerRecord: + localizedDescription = NSLocalizedString("getTrackerRecord", comment: "") + } + + return localizedDescription + } } } diff --git a/Tracker/Models/Enum/StatisticsEvent.swift b/Tracker/Models/Enum/StatisticsEvent.swift new file mode 100644 index 0000000..2326846 --- /dev/null +++ b/Tracker/Models/Enum/StatisticsEvent.swift @@ -0,0 +1,31 @@ +// +// StatisticsEvent.swift +// Tracker +// +// Created by Григорий Машук on 5.01.24. +// + +import Foundation + +enum StatisticsEvent: String { + case bestPeriod + case perfectDays + case trackersCompleted + case averageValue + + var statisticName: String { + var statisticName = "" + switch self { + case .bestPeriod: + statisticName = NSLocalizedString(self.rawValue, comment: "") + case .perfectDays: + statisticName = NSLocalizedString(self.rawValue, comment: "") + case .trackersCompleted: + statisticName = NSLocalizedString(self.rawValue, comment: "") + case .averageValue: + statisticName = NSLocalizedString(self.rawValue, comment: "") + } + + return statisticName + } +} diff --git a/Tracker/Models/Enum/HelpersEnum.swift b/Tracker/Models/Enum/UserDefaultKeys.swift similarity index 93% rename from Tracker/Models/Enum/HelpersEnum.swift rename to Tracker/Models/Enum/UserDefaultKeys.swift index fd79b69..6861a6e 100644 --- a/Tracker/Models/Enum/HelpersEnum.swift +++ b/Tracker/Models/Enum/UserDefaultKeys.swift @@ -11,6 +11,7 @@ enum UserDefaultKeys: String { case isTracker case selectNameCategory case selectFilter + case isOnboarding } enum Fixed: String { diff --git a/Tracker/Models/GeometricParams.swift b/Tracker/Models/GeometricParams.swift index 4e242df..3114df4 100644 --- a/Tracker/Models/GeometricParams.swift +++ b/Tracker/Models/GeometricParams.swift @@ -15,7 +15,11 @@ struct GeometricParams { // Параметр вычисляется уже при создании, что экономит время на вычислениях при отрисовке коллекции. let paddingWidth: CGFloat - init(cellCount: Int, leftInset: CGFloat, rightInset: CGFloat, cellSpacing: CGFloat) { + init(cellCount: Int, + leftInset: CGFloat, + rightInset: CGFloat, + cellSpacing: CGFloat) + { self.cellCount = cellCount self.leftInset = leftInset self.rightInset = rightInset diff --git a/Tracker/Models/PinnTrackerModel.swift b/Tracker/Models/PinnTrackerModel.swift index 93871e6..c4459e8 100644 --- a/Tracker/Models/PinnTrackerModel.swift +++ b/Tracker/Models/PinnTrackerModel.swift @@ -13,7 +13,8 @@ struct PinnTracker { } extension PinnTracker: Equatable { - static func == (lhs: PinnTracker, rhs: PinnTracker) -> Bool { + static func == (lhs: PinnTracker, + rhs: PinnTracker) -> Bool { lhs.id == rhs.id } } diff --git a/Tracker/Models/TrackerCategoryModel.swift b/Tracker/Models/TrackerCategoryModel.swift index 5387cb3..472a685 100644 --- a/Tracker/Models/TrackerCategoryModel.swift +++ b/Tracker/Models/TrackerCategoryModel.swift @@ -14,12 +14,13 @@ struct TrackerCategory { } extension TrackerCategory: Hashable { - static func == (lhs: TrackerCategory, rhs: TrackerCategory) -> Bool { - return lhs.nameCategory == rhs.nameCategory - } - - func hash(into hasher: inout Hasher) { - hasher.combine(nameCategory) - } + static func == (lhs: TrackerCategory, + rhs: TrackerCategory) -> Bool { + lhs.nameCategory == rhs.nameCategory + } + + func hash(into hasher: inout Hasher) { + hasher.combine(nameCategory) + } } diff --git a/Tracker/Models/TrackerModel.swift b/Tracker/Models/TrackerModel.swift index 705df17..f998e1d 100644 --- a/Tracker/Models/TrackerModel.swift +++ b/Tracker/Models/TrackerModel.swift @@ -27,12 +27,5 @@ struct Tracker { self.schedule = schedule } } - -extension Tracker: Equatable { - static func == (lhs: Tracker, rhs: Tracker) -> Bool { - lhs.id == rhs.id - } -} - -extension Tracker: Hashable {} + diff --git a/Tracker/Resources/en.lproj/Localizable.strings b/Tracker/Resources/en.lproj/Localizable.strings index 31208ac..d71f0fc 100644 --- a/Tracker/Resources/en.lproj/Localizable.strings +++ b/Tracker/Resources/en.lproj/Localizable.strings @@ -61,6 +61,7 @@ "categoriLabelText" = "Category"; "categoriAddButtonText" = "Add a categorycategory"; "labelStabText" = "Habits and events can be combined in meaning"; +"deleteCategoryMessage" = "Are you sure you don't need this category?"; //MARK: - NewCategoriViewController "newcategoriLabelText" = "New category"; @@ -94,3 +95,25 @@ "friday" = "Fri"; "saturday" = "Sat"; "sunday" = "Sun"; + +//MARK: - ErrorLocalizedDescription +"decodingErrorInvalidNameCategory" = "Error decoding the category name"; +"getCategoryCoreDataError" = "Error getting a category from the database"; + +"getTrackerError" = "Error getting the tracker from the database"; +"decodingErrorInvalidName" = "Error decoding the tracker name"; +"decodingErrorInvalidId" = "Id decoding error"; +"decodingErrorInvalidColor" = "Error decoding the background color of the tracker"; +"decodingErrorInvalidEmoji" = "Error decoding the Emoji tracker"; +"decodingErrorInvalidSchedul" = "Tracker schedule decoding error"; + +"transformationErrorInvalid" = "Transformation error"; +"decodingErrorInvalidDate" = "Date decoding error"; +"loadTrackerRecord" = "Error loading completed trackers"; +"getTrackerRecord" = "Error receiving the tracker"; + +//MARK: - StatisticsEvent +"bestPeriod" = "Best period" +"perfectDays" = "Perfect days" +"trackersCompleted" = "Trackers completed" +"averageValue" = "Average Value" diff --git a/Tracker/Resources/ru.lproj/Localizable.strings b/Tracker/Resources/ru.lproj/Localizable.strings index 3d88f24..7e0e4fd 100644 --- a/Tracker/Resources/ru.lproj/Localizable.strings +++ b/Tracker/Resources/ru.lproj/Localizable.strings @@ -60,6 +60,7 @@ "categoriLabelText" = "Категория"; "categoriAddButtonText" = "Добавить категорию"; "labelStabText" = "Привычки и события можно обЪединить по смыслу"; +"deleteCategoryMessage" = "Эта категория точно не нужна?"; //MARK: - NewCategoriViewController "newcategoriLabelText" = "Новая категория"; @@ -93,3 +94,25 @@ "friday" = "Пт"; "saturday" = "Сб"; "sunday" = "Вс"; + +//MARK: - ErrorLocalizedDescription +"decodingErrorInvalidNameCategory" = "Ошибка декодирования названия категории"; +"getCategoryCoreDataError" = "Ошибка получения категории из базы данных"; + +"getTrackerError" = "Ошибка получения трекера из базы данных"; +"decodingErrorInvalidName" = "Ошибка декодирования названия трекера"; +"decodingErrorInvalidId" = "Ошибка декодирования Id"; +"decodingErrorInvalidColor" = "Ошибка декодирования цвета фона трекера"; +"decodingErrorInvalidEmoji" = "Ошибка декодирования Emoji трекера"; +"decodingErrorInvalidSchedul" = "Ошибка декодирования расписания трекера"; + +"transformationErrorInvalid" = "Ошибка трансформации"; +"decodingErrorInvalidDate" = "Ошибка декодирования даты"; +"loadTrackerRecord" = "Ошибка загрузки выполненных трекеров"; +"getTrackerRecord" = "Ошибка получения трекера"; + +//MARK: - StatisticsEvent +"bestPeriod" = "Лучший период" +"perfectDays" = "Идеальные дни" +"trackersCompleted" = "Трекеров завершено" +"averageValue" = "Среднее значение" diff --git a/Tracker/Onboarding/OnboardingPageViewController.swift b/Tracker/ViewControllers/OnboardingViewControllers/OnboardingPageViewController.swift similarity index 65% rename from Tracker/Onboarding/OnboardingPageViewController.swift rename to Tracker/ViewControllers/OnboardingViewControllers/OnboardingPageViewController.swift index 2528312..af76dd6 100644 --- a/Tracker/Onboarding/OnboardingPageViewController.swift +++ b/Tracker/ViewControllers/OnboardingViewControllers/OnboardingPageViewController.swift @@ -9,26 +9,28 @@ import UIKit //MARK: - OnboardingPageViewController final class OnboardingPageViewController: UIPageViewController { - private struct ConstantsOnboardingPageViewController { + private struct ConstantsOnboarding { static let textLableTwoVC = NSLocalizedString("textLableTwoVC", comment: "") static let imageNameTwoVC = "twoPage" static let numberOfPages = 2 static let currentPage = 0 + static let pageIndicatorTintColor = UIColor.blackDay.withAlphaComponent(0.3) } private var currentIndex: Int = 1 private lazy var pageControl: UIPageControl = { let pageControl = UIPageControl() - pageControl.numberOfPages = ConstantsOnboardingPageViewController.numberOfPages - pageControl.currentPage = ConstantsOnboardingPageViewController.currentPage + pageControl.numberOfPages = ConstantsOnboarding.numberOfPages + pageControl.currentPage = ConstantsOnboarding.currentPage pageControl.currentPageIndicatorTintColor = .blackDay - pageControl.pageIndicatorTintColor = .blackDay.withAlphaComponent(0.3) + pageControl.pageIndicatorTintColor = ConstantsOnboarding.pageIndicatorTintColor return pageControl }() - - override func viewDidLoad() { + + override func viewDidLoad() + { super.viewDidLoad() dataSource = self delegate = self @@ -37,30 +39,36 @@ final class OnboardingPageViewController: UIPageViewController { } private extension OnboardingPageViewController { - func setupPageControl() { + func setupPageControl() + { view.addSubview(pageControl) pageControl.translatesAutoresizingMaskIntoConstraints = false pageControl.backgroundColor = .clear NSLayoutConstraint.activate([ pageControl.centerXAnchor.constraint(equalTo: view.centerXAnchor), - pageControl.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -134) + pageControl.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, + constant: -134) ]) } } //MARK: - UIPageViewControllerDataSource extension OnboardingPageViewController: UIPageViewControllerDataSource { - func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { + func pageViewController(_ pageViewController: UIPageViewController, + viewControllerBefore viewController: UIViewController) -> UIViewController? + { return nil } - func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { + func pageViewController(_ pageViewController: UIPageViewController, + viewControllerAfter viewController: UIViewController) -> UIViewController? + { let twoViewControllers = OnboardingViewController() - let onboardingModelTwoVC = Onboarding(imageName: ConstantsOnboardingPageViewController.imageNameTwoVC, - textLable: ConstantsOnboardingPageViewController.textLableTwoVC) + let onboardingModelTwoVC = Onboarding(imageName: ConstantsOnboarding.imageNameTwoVC, + textLable: ConstantsOnboarding.textLableTwoVC) twoViewControllers.config(model: onboardingModelTwoVC) - if currentIndex == ConstantsOnboardingPageViewController.numberOfPages { + if currentIndex == ConstantsOnboarding.numberOfPages { return nil } currentIndex += 1 @@ -71,7 +79,10 @@ extension OnboardingPageViewController: UIPageViewControllerDataSource { //MARK: - UIPageViewControllerDelegate extension OnboardingPageViewController: UIPageViewControllerDelegate { - func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { + func pageViewController(_ pageViewController: UIPageViewController, + didFinishAnimating finished: Bool, + previousViewControllers: [UIViewController], + transitionCompleted completed: Bool) { pageControl.currentPage = pageControl.currentPage == 1 ? 0 : 1 } } diff --git a/Tracker/Onboarding/OnboardingViewController.swift b/Tracker/ViewControllers/OnboardingViewControllers/OnboardingViewController.swift similarity index 92% rename from Tracker/Onboarding/OnboardingViewController.swift rename to Tracker/ViewControllers/OnboardingViewControllers/OnboardingViewController.swift index 7af1cb8..c4df263 100644 --- a/Tracker/Onboarding/OnboardingViewController.swift +++ b/Tracker/ViewControllers/OnboardingViewControllers/OnboardingViewController.swift @@ -43,7 +43,8 @@ final class OnboardingViewController: UIViewController { return startButton }() - override func viewDidLoad() { + override func viewDidLoad() + { super.viewDidLoad() view.backgroundColor = .clear setupSubViews() @@ -52,7 +53,8 @@ final class OnboardingViewController: UIViewController { extension OnboardingViewController { //MARK: - SetupUI - private func setupSubViews() { + private func setupSubViews() + { [imageView, onboardingLableView].forEach { view.addSubview($0) $0.translatesAutoresizingMaskIntoConstraints = false @@ -63,7 +65,8 @@ extension OnboardingViewController { setupOnboardingLableView() } - private func setupContentImageView() { + private func setupContentImageView() + { NSLayoutConstraint.activate ([ imageView.topAnchor.constraint(equalTo: view.topAnchor, constant: -1), imageView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 1), @@ -72,7 +75,8 @@ extension OnboardingViewController { ]) } - private func setupOnboardingLableView() { + private func setupOnboardingLableView() + { NSLayoutConstraint.activate([ onboardingLableView.bottomAnchor.constraint(equalTo: startButton.topAnchor, constant: -160), onboardingLableView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16), @@ -80,7 +84,8 @@ extension OnboardingViewController { ]) } - private func setupStartButton() { + private func setupStartButton() + { view.addSubview(startButton) startButton.translatesAutoresizingMaskIntoConstraints = false startButton.backgroundColor = .blackDay @@ -96,14 +101,16 @@ extension OnboardingViewController { } //MARK: - Configure - func config(model: Onboarding) { + func config(model: Onboarding) + { guard let image = UIImage(named: model.imageName) else { return } imageView.image = image onboardingLableView.text = model.textLable } @objc - private func showViewController() { + private func showViewController() + { guard let window = UIApplication.shared.windows.first else { return } let vc = TabBarController() window.rootViewController = vc diff --git a/Tracker/TabBarController/TabBarController.swift b/Tracker/ViewControllers/TabBarController.swift similarity index 83% rename from Tracker/TabBarController/TabBarController.swift rename to Tracker/ViewControllers/TabBarController.swift index bd60a30..b15a028 100644 --- a/Tracker/TabBarController/TabBarController.swift +++ b/Tracker/ViewControllers/TabBarController.swift @@ -21,7 +21,8 @@ final class TabBarController: UITabBarController { private let colors = Colors() - override func viewDidLoad() { + override func viewDidLoad() + { super.viewDidLoad() tabBar.layer.borderWidth = 1 tabBar.clipsToBounds = true @@ -44,17 +45,19 @@ final class TabBarController: UITabBarController { ] } - override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) + { super.traitCollectionDidChange(previousTraitCollection) if #available(iOS 13.0, *), - // Проверяем только изменение цветовой схемы - traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) { - if traitCollection.userInterfaceStyle == .dark { - tabBar.layer.borderColor = UIColor.black.cgColor - } else { - tabBar.layer.borderColor = UIColor.grayDay.cgColor - } - } + // Проверяем только изменение цветовой схемы + traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) + { + if traitCollection.userInterfaceStyle == .dark { + tabBar.layer.borderColor = UIColor.black.cgColor + } else { + tabBar.layer.borderColor = UIColor.grayDay.cgColor + } + } } deinit { diff --git a/Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift b/Tracker/ViewControllers/ViewControllers/CreateTrackerViewController.swift similarity index 76% rename from Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift rename to Tracker/ViewControllers/ViewControllers/CreateTrackerViewController.swift index f24a1c3..abeafb9 100644 --- a/Tracker/TabBarController/TrackersViews/ViewControllers/CreateTrackerViewController.swift +++ b/Tracker/ViewControllers/ViewControllers/CreateTrackerViewController.swift @@ -116,10 +116,8 @@ final class CreateTrackerViewController: UIViewController { selectionTableView.delegate = self selectionTableView.backgroundColor = .clear selectionTableView.isScrollEnabled = false - selectionTableView.separatorStyle = viewModel.isSchedul ? .none : .singleLine - selectionTableView.separatorColor = .grayDay - selectionTableView.separatorInset = ConstantsCreateVc.insertSeparatorTableView - selectionTableView.register(CreateTrackerTableViewCell.self, forCellReuseIdentifier: "\(CreateTrackerTableViewCell.self)") + selectionTableView.register(CreateTrackerTableViewCell.self, + forCellReuseIdentifier: "\(CreateTrackerTableViewCell.self)") return selectionTableView }() @@ -127,11 +125,16 @@ final class CreateTrackerViewController: UIViewController { private lazy var emojiCollectionView: TrackerCollectionView = { let layout = UICollectionViewFlowLayout() let params = GeometricParams(cellCount: 7, leftInset: 6, rightInset: 6, cellSpacing: 2) - let emojiCollectionView = TrackerCollectionView(frame: .zero, collectionViewLayout: layout, params: params) + let emojiCollectionView = TrackerCollectionView(frame: .zero, + collectionViewLayout: layout, + params: params) emojiCollectionView.dataSource = self emojiCollectionView.delegate = self - emojiCollectionView.register(EmojiCollectionViewCell.self, forCellWithReuseIdentifier: "\(EmojiCollectionViewCell.self)") - emojiCollectionView.register(HeaderReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "\(HeaderReusableView.self)") + emojiCollectionView.register(EmojiCollectionViewCell.self, + forCellWithReuseIdentifier: "\(EmojiCollectionViewCell.self)") + emojiCollectionView.register(HeaderReusableView.self, + forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, + withReuseIdentifier: "\(HeaderReusableView.self)") emojiCollectionView.allowsMultipleSelection = false emojiCollectionView.backgroundColor = .clear emojiCollectionView.isScrollEnabled = false @@ -143,10 +146,13 @@ final class CreateTrackerViewController: UIViewController { private lazy var colorCollectionView: TrackerCollectionView = { let layout = UICollectionViewFlowLayout() let params = GeometricParams(cellCount: 6, leftInset: 6, rightInset: 6, cellSpacing: 2) - let colorCollectionView = TrackerCollectionView(frame: .zero, collectionViewLayout: layout, params: params) + let colorCollectionView = TrackerCollectionView(frame: .zero, + collectionViewLayout: layout, + params: params) colorCollectionView.dataSource = self colorCollectionView.delegate = self - colorCollectionView.register(ColorCollectionViewCell.self, forCellWithReuseIdentifier: "\(ColorCollectionViewCell.self)") + colorCollectionView.register(ColorCollectionViewCell.self, + forCellWithReuseIdentifier: "\(ColorCollectionViewCell.self)") colorCollectionView.register(HeaderReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "\(HeaderReusableView.self)") @@ -222,11 +228,13 @@ final class CreateTrackerViewController: UIViewController { super.init(nibName: nil, bundle: nil) } - required init?(coder: NSCoder) { + required init?(coder: NSCoder) + { fatalError("init(coder:) has not been implemented") } - override func viewDidLoad() { + override func viewDidLoad() + { super.viewDidLoad() bind() setupUIElement() @@ -238,12 +246,13 @@ final class CreateTrackerViewController: UIViewController { } extension CreateTrackerViewController { - private func bind() { + private func bind() + { guard let viewModel = viewModel as? EditTrackerViewModel else { return } viewModel.$schedule.bind { [weak self] _ in guard let self else { return } self.chengeActivButton() - selectionTableView.reloadRows(at: [IndexPath(row: 1, section: 0)], with: .automatic) + selectionTableView.reloadData() } viewModel.$color.bind { [weak self] _ in @@ -259,7 +268,7 @@ extension CreateTrackerViewController { viewModel.$nameNewCategory.bind { [weak self] _ in guard let self else { return } self.chengeActivButton() - selectionTableView.reloadRows(at: [IndexPath(row: 0, section: 0)], with: .automatic) + selectionTableView.reloadData() } viewModel.$nameTracker.bind { [weak self] nameTracker in @@ -269,19 +278,22 @@ extension CreateTrackerViewController { } } - private func chengeActivButton() { + private func chengeActivButton() + { let flag = self.viewModel.checkingForEmptiness() isActivCreateButton(flag: flag) } //MARK: - Обработка событий @objc - private func didTapCancelButton() { + private func didTapCancelButton() + { dismiss(animated: true) } @objc - private func didTapGreateButton() { + private func didTapGreateButton() + { guard let viewModel = viewModel as? EditTrackerViewModel, let color = viewModel.color else { return } @@ -312,7 +324,8 @@ extension CreateTrackerViewController { } @objc - private func textFieldDidChange(_ textField: UITextField) { + private func textFieldDidChange(_ textField: UITextField) + { guard let text = textField.text, let viewModel = viewModel as? EditTrackerViewModel else { return } @@ -327,32 +340,38 @@ extension CreateTrackerViewController { } @objc - private func hideKeyboard() { + private func hideKeyboard() + { view.endEditing(true) } - private func isActivCreateButton(flag: Bool) { + private func isActivCreateButton(flag: Bool) + { createButton.isEnabled = flag createButton.backgroundColor = flag ? colors.buttonDisabledColor : .grayDay createButton.setTitleColor( flag ? .textEventColor : .whiteDay, for: .normal) } - func reverseIsSchedul() { + func reverseIsSchedul() + { viewModel.reverseIsSchedul() } - func setTextCountLable(count: Int) -> String { + func setTextCountLable(count: Int) -> String + { String.localizedStringWithFormat(ConstantsCreateVc.numberOfDays, count) } - func presentViewController(vc: UIViewController, modalStyle: UIModalPresentationStyle) { + func presentViewController(vc: UIViewController, modalStyle: UIModalPresentationStyle) + { vc.modalPresentationStyle = modalStyle present(vc, animated: true) } //MARK: - SetupUI - func setupUIElement() { + func setupUIElement() + { setupNewHabitLabel() if updateTrackerDelegate != nil { setupCountLabel() @@ -362,27 +381,32 @@ extension CreateTrackerViewController { setupStackView() } - func setupCountLabel() { + func setupCountLabel() + { view.addSubview(countСompletedTrackers) countСompletedTrackers.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ - countСompletedTrackers.topAnchor.constraint(equalTo: newHabitLabel.bottomAnchor, constant: 38), + countСompletedTrackers.topAnchor.constraint(equalTo: newHabitLabel.bottomAnchor, + constant: 38), countСompletedTrackers.centerXAnchor.constraint(equalTo: view.centerXAnchor) ]) } - func setupNewHabitLabel() { + func setupNewHabitLabel() + { view.addSubview(newHabitLabel) newHabitLabel.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ - newHabitLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 24), + newHabitLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, + constant: 24), newHabitLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor) ]) } - func setupButtonStack() { + func setupButtonStack() + { view.addSubview(buttonStack) [cancelButton, createButton].forEach { $0.translatesAutoresizingMaskIntoConstraints = false @@ -391,27 +415,37 @@ extension CreateTrackerViewController { NSLayoutConstraint.activate([ buttonStack.heightAnchor.constraint(equalToConstant: 60), - buttonStack.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -10), - buttonStack.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20), - buttonStack.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20) + buttonStack.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, + constant: -10), + buttonStack.leadingAnchor.constraint(equalTo: view.leadingAnchor, + constant: 20), + buttonStack.trailingAnchor.constraint(equalTo: view.trailingAnchor, + constant: -20) ]) } - func setupScrollView() { + func setupScrollView() + { view.addSubview(scrollView) scrollView.translatesAutoresizingMaskIntoConstraints = false - let scrollTopAnchor = updateTrackerDelegate == nil ? newHabitLabel.bottomAnchor : countСompletedTrackers.bottomAnchor + let scrollTopAnchor = updateTrackerDelegate == nil ? + newHabitLabel.bottomAnchor : + countСompletedTrackers.bottomAnchor NSLayoutConstraint.activate([ - scrollView.topAnchor.constraint(equalTo: scrollTopAnchor, constant: 24), - scrollView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 16), - scrollView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -16), + scrollView.topAnchor.constraint(equalTo: scrollTopAnchor, + constant: 24), + scrollView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, + constant: 16), + scrollView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, + constant: -16), scrollView.bottomAnchor.constraint(equalTo: buttonStack.topAnchor) ]) } - func setupStackView() { + func setupStackView() + { scrollView.addSubview(contentStackView) [createNameTextField, characterRestrictionsView, selectionTableView, emojiCollectionView, colorCollectionView].forEach { contentStackView.addArrangedSubview($0) @@ -434,11 +468,15 @@ extension CreateTrackerViewController { //MARK: - UITableViewDataSource extension CreateTrackerViewController: UITableViewDataSource { - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + func tableView(_ tableView: UITableView, + numberOfRowsInSection section: Int) -> Int + { return viewModel.listSettings.count } - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + func tableView(_ tableView: UITableView, + cellForRowAt indexPath: IndexPath) -> UITableViewCell + { guard let cell = tableView.dequeueReusableCell(withIdentifier: "\(CreateTrackerTableViewCell.self)") as? CreateTrackerTableViewCell, let viewModel = viewModel as? EditTrackerViewModel @@ -453,7 +491,10 @@ extension CreateTrackerViewController: UITableViewDataSource { case .schedule: cell.configCell(choice: listSettings) cell.configSecondaryLableShedule(secondaryText: secondarySchedulText) - tableView.separatorInset = UIEdgeInsets(top: 0, left: view.bounds.width, bottom: 0, right: 0) + tableView.separatorInset = UIEdgeInsets(top: 0, + left: view.bounds.width, + bottom: 0, + right: 0) } if viewModel.isSchedul { @@ -462,29 +503,41 @@ extension CreateTrackerViewController: UITableViewDataSource { if !viewModel.isSchedul && indexPath.row == 1 { cell.setupCornerRadius(cornerRadius: ConstantsCreateVc.cornerRadius, - maskedCorners: [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]) + maskedCorners: [.layerMinXMaxYCorner, + .layerMaxXMaxYCorner]) } if !viewModel.isSchedul && indexPath.row == 0 { cell.setupCornerRadius(cornerRadius: ConstantsCreateVc.cornerRadius, - maskedCorners: [.layerMinXMinYCorner, .layerMaxXMinYCorner]) + maskedCorners: [.layerMinXMinYCorner, + .layerMaxXMinYCorner]) } + tableView.separatorStyle = viewModel.isSchedul ? .none : .singleLine + tableView.separatorColor = .grayDay + tableView.separatorInset = ConstantsCreateVc.insertSeparatorTableView + return cell } } //MARK: - UITableViewDelegate extension CreateTrackerViewController: UITableViewDelegate { - func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + func tableView(_ tableView: UITableView, + heightForRowAt indexPath: IndexPath) -> CGFloat + { 75 } - func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + func tableView(_ tableView: UITableView, + didSelectRowAt indexPath: IndexPath) + { switch viewModel.listSettings[indexPath.row] { case .category: - let selectCategoriesViewController = SelectCategoriesViewController(delegate: self, viewModel: CategoryViewModel()) - presentViewController(vc: selectCategoriesViewController, modalStyle: .formSheet) + let selectCategoriesViewController = SelectCategoriesViewController(delegate: self, + viewModel: CategoryViewModel()) + presentViewController(vc: selectCategoriesViewController, + modalStyle: .formSheet) case .schedule: let viewModel = SheduleViewModel() let scheduleViewController = ScheduleViewController(viewModel: viewModel) @@ -504,7 +557,9 @@ extension CreateTrackerViewController: UITextFieldDelegate { //MARK: - UICollectionViewDataSource extension CreateTrackerViewController: UICollectionViewDataSource { - func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + func collectionView(_ collectionView: UICollectionView, + numberOfItemsInSection section: Int) -> Int + { var count: Int switch viewModel.dataSection[section] { case .color: @@ -515,11 +570,14 @@ extension CreateTrackerViewController: UICollectionViewDataSource { return count } - func numberOfSections(in collectionView: UICollectionView) -> Int { + func numberOfSections(in collectionView: UICollectionView) -> Int + { viewModel.dataSection.count } - func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + func collectionView(_ collectionView: UICollectionView, + cellForItemAt indexPath: IndexPath) -> UICollectionViewCell + { var returnCell = UICollectionViewCell() if collectionView == colorCollectionView, let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "\(ColorCollectionViewCell.self)", @@ -543,37 +601,49 @@ extension CreateTrackerViewController: UICollectionViewDataSource { //MARK: - UICollectionViewDelegate extension CreateTrackerViewController: UICollectionViewDelegate { - func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + func collectionView(_ collectionView: UICollectionView, + didSelectItemAt indexPath: IndexPath) + { if collectionView == colorCollectionView { - guard let cell = collectionView.cellForItem(at: indexPath) as? ColorCollectionViewCell else { return } + guard let cell = collectionView.cellForItem(at: indexPath) as? ColorCollectionViewCell + else { return } let color = viewModel.colors[indexPath.row] cell.colorSelection(color: color, flag: true) viewModel.setColor(self, color: color) } if collectionView == emojiCollectionView { - guard let cell = collectionView.cellForItem(at: indexPath) as? EmojiCollectionViewCell else { return } + guard let cell = collectionView.cellForItem(at: indexPath) as? EmojiCollectionViewCell + else { return } let emoji = viewModel.emojies[indexPath.row] cell.emojiSelection(isBackground: true) viewModel.setEmojiTracker(self, emoji: emoji) } } - func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) { + func collectionView(_ collectionView: UICollectionView, + didDeselectItemAt indexPath: IndexPath) + { if collectionView == colorCollectionView { - guard let cell = collectionView.cellForItem(at: indexPath) as? ColorCollectionViewCell else { return } + guard let cell = collectionView.cellForItem(at: indexPath) as? ColorCollectionViewCell + else { return } let color: UIColor = .clear cell.colorSelection(color: color, flag: false) } if collectionView == emojiCollectionView { - guard let cell = collectionView.cellForItem(at: indexPath) as? EmojiCollectionViewCell else { return } + guard let cell = collectionView.cellForItem(at: indexPath) as? EmojiCollectionViewCell + else { return } cell.emojiSelection(isBackground: false) } } } //MARK: - UICollectionViewDelegateFlowLayout -extension CreateTrackerViewController: UICollectionViewDelegateFlowLayout { - func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { +extension CreateTrackerViewController: UICollectionViewDelegateFlowLayout +{ + func collectionView(_ collectionView: UICollectionView, + layout collectionViewLayout: UICollectionViewLayout, + sizeForItemAt indexPath: IndexPath) -> CGSize + { let availableWidth = collectionView.frame.width - emojiCollectionView.params.paddingWidth let cellWidth = availableWidth / CGFloat(emojiCollectionView.params.cellCount) let sizeCell = CGSize(width: cellWidth, height: ConstantsCreateVc.heightCollectionView) @@ -581,20 +651,42 @@ extension CreateTrackerViewController: UICollectionViewDelegateFlowLayout { return sizeCell } - func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { - UIEdgeInsets(top: 10, left: emojiCollectionView.params.leftInset, bottom: 16, right: emojiCollectionView.params.rightInset) + func collectionView(_ collectionView: UICollectionView, + layout collectionViewLayout: UICollectionViewLayout, + insetForSectionAt section: Int) -> UIEdgeInsets + { + UIEdgeInsets(top: 10, + left: emojiCollectionView.params.leftInset, + bottom: 16, + right: emojiCollectionView.params.rightInset) } - func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize { + func collectionView(_ collectionView: UICollectionView, + layout collectionViewLayout: UICollectionViewLayout, + referenceSizeForHeaderInSection section: Int) -> CGSize + { var systemLayoutSize: CGSize let indexPath = IndexPath(row: 0, section: section) - let headerView = self.collectionView(collectionView, viewForSupplementaryElementOfKind: UICollectionView.elementKindSectionHeader, at: indexPath) - systemLayoutSize = headerView.systemLayoutSizeFitting(CGSize(width: collectionView.frame.width, height: UIView.layoutFittingExpandedSize.height), withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel) + let headerView = self.collectionView(collectionView, + viewForSupplementaryElementOfKind: UICollectionView.elementKindSectionHeader, + at: indexPath) + systemLayoutSize = headerView.systemLayoutSizeFitting(CGSize(width: collectionView.frame.width, + height: UIView.layoutFittingExpandedSize.height), + withHorizontalFittingPriority: .required, + verticalFittingPriority: .fittingSizeLevel) return systemLayoutSize } - func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { - guard let supplementary = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "\(HeaderReusableView.self)", for: indexPath) as? HeaderReusableView else { return UICollectionReusableView() } + func collectionView(_ collectionView: UICollectionView, + viewForSupplementaryElementOfKind kind: String, + at indexPath: IndexPath) -> UICollectionReusableView + { + guard let supplementary = collectionView.dequeueReusableSupplementaryView(ofKind: kind, + withReuseIdentifier: "\(HeaderReusableView.self)", + for: indexPath) as? HeaderReusableView + else { return UICollectionReusableView() + + } switch kind { case UICollectionView.elementKindSectionHeader: if collectionView == colorCollectionView { @@ -612,14 +704,16 @@ extension CreateTrackerViewController: UICollectionViewDelegateFlowLayout { //MARK: - ScheduleViewControllerDelegate extension CreateTrackerViewController: ScheduleViewControllerDelegate { - func daysOfWeek(viewController: UIViewController, listDays: [WeekDay]) { + func daysOfWeek(viewController: UIViewController, listDays: [WeekDay]) + { viewModel.setListWeekDay(listWeekDay: listDays) } } //MARK: - GreateCategoriesViewControllerDelegate extension CreateTrackerViewController: SelectCategoriesViewControllerDelegate { - func categoriesViewController(vc: UIViewController, nameCategory: String) { + func categoriesViewController(vc: UIViewController, nameCategory: String) + { viewModel.setNameNewCategory(self, nameCategory: nameCategory) vc.dismiss(animated: true) } @@ -627,20 +721,31 @@ extension CreateTrackerViewController: SelectCategoriesViewControllerDelegate { //MARK: - TrackersViewControllerDelegate extension CreateTrackerViewController: TrackersViewControllerDelegate { - func editTracker(vc: TrackersViewController, editTracker: Tracker, count: Int, nameCategory: String, indexPath: IndexPath) { + func editTracker(vc: TrackersViewController, + editTracker: Tracker, + count: Int, + nameCategory: String, + indexPath: IndexPath) + { let colorRow = viewModel.getColorRow(color: editTracker.color) let emojiRow = viewModel.getEmojiRow(emoji: editTracker.emoji) countСompletedTrackers.text = setTextCountLable(count: count) - guard let emojiCell = emojiCollectionView.cellForItem(at: IndexPath(row: emojiRow, section: 0)) as? EmojiCollectionViewCell, - let colorCell = colorCollectionView.cellForItem(at: IndexPath(row: colorRow, section: 0)) as? ColorCollectionViewCell + guard let emojiCell = emojiCollectionView.cellForItem(at: IndexPath(row: emojiRow, + section: 0)) as? EmojiCollectionViewCell, + let colorCell = colorCollectionView.cellForItem(at: IndexPath(row: colorRow, + section: 0)) as? ColorCollectionViewCell else { return } - viewModel.editTracker(vc: self, editTracker: editTracker, nameCategory: nameCategory) + viewModel.editTracker(vc: self, + editTracker: editTracker, + nameCategory: nameCategory) emojiCell.emojiSelection(isBackground: true) colorCell.colorSelection(color: editTracker.color, flag: true) emojiCollectionView.selectItem(at: IndexPath(row: emojiRow, section: 0), - animated: true, scrollPosition: UICollectionView.ScrollPosition()) + animated: true, + scrollPosition: UICollectionView.ScrollPosition()) colorCollectionView.selectItem(at: IndexPath(row: colorRow, section: 0), - animated: true, scrollPosition: UICollectionView.ScrollPosition()) + animated: true, + scrollPosition: UICollectionView.ScrollPosition()) } } diff --git a/Tracker/TabBarController/TrackersViews/ViewControllers/EventSelectionViewController.swift b/Tracker/ViewControllers/ViewControllers/EventSelectionViewController.swift similarity index 69% rename from Tracker/TabBarController/TrackersViews/ViewControllers/EventSelectionViewController.swift rename to Tracker/ViewControllers/ViewControllers/EventSelectionViewController.swift index 2be7168..eccec9a 100644 --- a/Tracker/TabBarController/TrackersViews/ViewControllers/EventSelectionViewController.swift +++ b/Tracker/ViewControllers/ViewControllers/EventSelectionViewController.swift @@ -9,7 +9,9 @@ import UIKit //MARK: - EventSelectionViewControllerDelegate protocol EventSelectionViewControllerDelegate: AnyObject { - func eventSelectionViewController(vc: UIViewController, nameCategories: String, tracker: Tracker) + func eventSelectionViewController(vc: UIViewController, + nameCategories: String, + tracker: Tracker) func eventSelectionViewControllerDidCancel(_ vc: EventSelectionViewController) } @@ -19,6 +21,7 @@ final class EventSelectionViewController: UIViewController { static let texGreatetLabelName = NSLocalizedString("texGreatetLabelName", comment: "") static let textFont = UIFont.systemFont(ofSize: 16, weight: .medium) static let cornerRadius = CGFloat(16) + static let stackViewSpacing = CGFloat(10) } weak var delegate: EventSelectionViewControllerDelegate? @@ -38,26 +41,34 @@ final class EventSelectionViewController: UIViewController { let stackView = UIStackView() stackView.translatesAutoresizingMaskIntoConstraints = false stackView.axis = .vertical - stackView.spacing = 10 + stackView.spacing = ConstantsEventVc.stackViewSpacing return stackView }() private lazy var habitButton: UIButton = { - let habitButton = setupButton(text: .habit, font: ConstantsEventVc.textFont, cornerRadius: ConstantsEventVc.cornerRadius) - habitButton.addTarget(self, action: #selector(didTapHabitButton), for: .touchUpInside) + let habitButton = setupButton(text: .habit, + font: ConstantsEventVc.textFont, + cornerRadius: ConstantsEventVc.cornerRadius) + habitButton.addTarget(self, action: #selector(didTapHabitButton), + for: .touchUpInside) return habitButton }() private lazy var irregularEventButton: UIButton = { - let irregularEventButton = setupButton(text: .irregularEvent, font: ConstantsEventVc.textFont, cornerRadius: ConstantsEventVc.cornerRadius) - irregularEventButton.addTarget(self, action: #selector(didTapIrregularEventButton), for: .touchUpInside) + let irregularEventButton = setupButton(text: .irregularEvent, + font: ConstantsEventVc.textFont, + cornerRadius: ConstantsEventVc.cornerRadius) + irregularEventButton.addTarget(self, + action: #selector(didTapIrregularEventButton), + for: .touchUpInside) return irregularEventButton }() - override func viewDidLoad() { + override func viewDidLoad() + { super.viewDidLoad() view.backgroundColor = colors.viewBackground setupLableGreate() @@ -68,19 +79,22 @@ final class EventSelectionViewController: UIViewController { private extension EventSelectionViewController { //MARK: - Обработка событий @objc - func didTapHabitButton() { + func didTapHabitButton() + { let greateVC = createTrackerVC() greateVC.reverseIsSchedul() present(greateVC, animated: true) } @objc - func didTapIrregularEventButton() { + func didTapIrregularEventButton() + { let greateVC = createTrackerVC() present(greateVC, animated: true) } - func createTrackerVC() -> CreateTrackerViewController { + func createTrackerVC() -> CreateTrackerViewController + { let viewModel = EditTrackerViewModel() let createVC = CreateTrackerViewController(viewModel: viewModel, updateTrackerDelegate: nil, @@ -90,7 +104,10 @@ private extension EventSelectionViewController { } //MARK: - SetupUI - func setupButton(text: NameEvent, font: UIFont, cornerRadius: CGFloat? = nil) -> UIButton { + func setupButton(text: NameEvent, + font: UIFont, + cornerRadius: CGFloat? = nil) -> UIButton + { let button = UIButton() button.setTitle(text.name, for: .normal) button.backgroundColor = colors.buttonEventColor @@ -103,25 +120,30 @@ private extension EventSelectionViewController { return button } - func setupLableGreate() { + func setupLableGreate() + { view.addSubview(labelCreate) NSLayoutConstraint.activate([ labelCreate.centerXAnchor.constraint(equalTo: view.centerXAnchor), - labelCreate.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20), + labelCreate.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, + constant: 20), labelCreate.heightAnchor.constraint(equalToConstant: labelCreate.font.pointSize) ]) } - func setupStackView() { + func setupStackView() + { view.addSubview(stackView) [habitButton, irregularEventButton].forEach { stackView.addArrangedSubview($0) } NSLayoutConstraint.activate([ - stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20), - stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20), + stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, + constant: 20), + stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, + constant: -20), stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor), habitButton.heightAnchor.constraint(equalToConstant: 60), @@ -133,11 +155,17 @@ private extension EventSelectionViewController { //MARK: - CreateTrackerViewControllerDelegate extension EventSelectionViewController: CreateTrackerViewControllerDelegate { - func trackerViewController(vc: UIViewController, nameCategory: String, tracker: Tracker) { - delegate?.eventSelectionViewController(vc: self, nameCategories: nameCategory, tracker: tracker) + func trackerViewController(vc: UIViewController, + nameCategory: String, + tracker: Tracker) + { + delegate?.eventSelectionViewController(vc: self, + nameCategories: nameCategory, + tracker: tracker) } - func trackerViewControllerDidCancel(_ vc: CreateTrackerViewController) { + func trackerViewControllerDidCancel(_ vc: CreateTrackerViewController) + { vc.dismiss(animated: false) delegate?.eventSelectionViewControllerDidCancel(self) } diff --git a/Tracker/TabBarController/TrackersViews/ViewControllers/FiltersViewController.swift b/Tracker/ViewControllers/ViewControllers/FiltersViewController.swift similarity index 68% rename from Tracker/TabBarController/TrackersViews/ViewControllers/FiltersViewController.swift rename to Tracker/ViewControllers/ViewControllers/FiltersViewController.swift index fc02b4a..383c424 100644 --- a/Tracker/TabBarController/TrackersViews/ViewControllers/FiltersViewController.swift +++ b/Tracker/ViewControllers/ViewControllers/FiltersViewController.swift @@ -17,7 +17,10 @@ final class FiltersViewController: UIViewController { static let filterLabelFont = UIFont.systemFont(ofSize: 16, weight: .medium) static let cornerRadiusCell = CGFloat(16) static let heightCell = CGFloat(75) - static let insertSeparatorTableView = UIEdgeInsets(top: .zero, left: 12, bottom: .zero, right: 12) + static let insertSeparatorTableView = UIEdgeInsets(top: .zero, + left: 12, + bottom: .zero, + right: 12) } weak var delegate: FiltersViewControllerDelegate? @@ -42,7 +45,8 @@ final class FiltersViewController: UIViewController { filterTableView.backgroundColor = .clear filterTableView.separatorStyle = .singleLine filterTableView.separatorColor = .separatorColor - filterTableView.register(CustomTableViewCell.self, forCellReuseIdentifier: "\(CustomTableViewCell.self)") + filterTableView.register(CustomTableViewCell.self, + forCellReuseIdentifier: "\(CustomTableViewCell.self)") filterTableView.separatorInset = ConstantsFilters.insertSeparatorTableView return filterTableView @@ -55,7 +59,9 @@ final class FiltersViewController: UIViewController { setupTableView() } - init(viewModel: FiltersViewModelProtocol, delegate: FiltersViewControllerDelegate) { + init(viewModel: FiltersViewModelProtocol, + delegate: FiltersViewControllerDelegate) + { self.viewModel = viewModel self.delegate = delegate super.init(nibName: nil, bundle: nil) @@ -72,19 +78,24 @@ private extension FiltersViewController { filtersLable.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ - filtersLable.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 24), + filtersLable.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, + constant: 24), filtersLable.centerXAnchor.constraint(equalTo: view.centerXAnchor) ]) } - private func setupTableView() { + private func setupTableView() + { view.addSubview(filterTableView) filterTableView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ - filterTableView.topAnchor.constraint(equalTo: filtersLable.bottomAnchor, constant: 24), - filterTableView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16), - filterTableView.trailingAnchor.constraint(equalTo: view.trailingAnchor,constant: -16), + filterTableView.topAnchor.constraint(equalTo: filtersLable.bottomAnchor, + constant: 24), + filterTableView.leadingAnchor.constraint(equalTo: view.leadingAnchor, + constant: 16), + filterTableView.trailingAnchor.constraint(equalTo: view.trailingAnchor, + constant: -16), filterTableView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor) ]) } @@ -92,28 +103,41 @@ private extension FiltersViewController { //MARK: - UITableViewDataSource extension FiltersViewController: UITableViewDataSource { - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + func tableView(_ tableView: UITableView, + numberOfRowsInSection section: Int) -> Int + { viewModel.filtersState.count } - func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + func tableView(_ tableView: UITableView, + heightForRowAt indexPath: IndexPath) -> CGFloat + { ConstantsFilters.heightCell } - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - guard let cell = filterTableView.dequeueReusableCell(withIdentifier: "\(CustomTableViewCell.self)", for: indexPath) as? CustomTableViewCell + func tableView(_ tableView: UITableView, + cellForRowAt indexPath: IndexPath) -> UITableViewCell + { + guard let cell = filterTableView.dequeueReusableCell(withIdentifier: "\(CustomTableViewCell.self)", + for: indexPath) as? CustomTableViewCell else { return UITableViewCell() } cell.showSelectedImage(flag: viewModel.comparison(filter: viewModel.filtersState[indexPath.row].rawValue)) - let model = CustomCellModel(text: viewModel.filtersState[indexPath.row].name, color: .backgroundNight) + let model = CustomCellModel(text: viewModel.filtersState[indexPath.row].name, + color: .backgroundNight) cell.config(model: model) if indexPath.row == viewModel.filtersState.count - 1 { cell.setupCornerRadius(cornerRadius: ConstantsFilters.cornerRadiusCell, - maskedCorners: [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]) - cell.separatorInset = UIEdgeInsets(top: .zero, left: view.bounds.width, bottom: .zero, right: .zero) + maskedCorners: [.layerMinXMaxYCorner, + .layerMaxXMaxYCorner]) + cell.separatorInset = UIEdgeInsets(top: .zero, + left: view.bounds.width, + bottom: .zero, + right: .zero) } if indexPath.row == .zero { cell.setupCornerRadius(cornerRadius: ConstantsFilters.cornerRadiusCell, - maskedCorners: [.layerMaxXMinYCorner, .layerMinXMinYCorner]) + maskedCorners: [.layerMaxXMinYCorner, + .layerMinXMinYCorner]) } return cell @@ -122,7 +146,9 @@ extension FiltersViewController: UITableViewDataSource { //MARK: - UITableViewDelegate extension FiltersViewController: UITableViewDelegate { - func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + func tableView(_ tableView: UITableView, + didSelectRowAt indexPath: IndexPath) + { guard let delegate else { return } let selectFilter = viewModel.filtersState[indexPath.row] viewModel.setSelectFilter(selectFilter: selectFilter.rawValue) diff --git a/Tracker/TabBarController/TrackersViews/ViewControllers/NewCategoriViewController.swift b/Tracker/ViewControllers/ViewControllers/NewCategoriViewController.swift similarity index 82% rename from Tracker/TabBarController/TrackersViews/ViewControllers/NewCategoriViewController.swift rename to Tracker/ViewControllers/ViewControllers/NewCategoriViewController.swift index c80777d..d5f9e36 100644 --- a/Tracker/TabBarController/TrackersViews/ViewControllers/NewCategoriViewController.swift +++ b/Tracker/ViewControllers/ViewControllers/NewCategoriViewController.swift @@ -12,7 +12,9 @@ protocol NewCategoriViewControllerDelegate: AnyObject { } protocol UpdateCategoriViewControllerDelegate: AnyObject { - func didUpdateCategoryName(_ vc: UIViewController, newNameCategory: String, oldNameCategory: String) + func didUpdateCategoryName(_ vc: UIViewController, + newNameCategory: String, + oldNameCategory: String) } //MARK: - NewCategoriViewController @@ -81,15 +83,18 @@ class NewCategoriViewController: UIViewController { super.init(nibName: nil, bundle: nil) } - required init?(coder: NSCoder) { + required init?(coder: NSCoder) + { fatalError("init(coder:) has not been implemented") } - override func viewDidLoad() { + override func viewDidLoad() + { super.viewDidLoad() viewModel = NewCategoryViewModel() bind() - let tapGesture = UITapGestureRecognizer(target: self, action: #selector(hideKeyboard)) + let tapGesture = UITapGestureRecognizer(target: self, + action: #selector(hideKeyboard)) tapGesture.cancelsTouchesInView = false view.addGestureRecognizer(tapGesture) view.backgroundColor = colors.viewBackground @@ -98,7 +103,8 @@ class NewCategoriViewController: UIViewController { } private extension NewCategoriViewController { - func bind() { + func bind() + { guard let viewModel = viewModel as? NewCategoryViewModel else { return } viewModel.$newNameCategory.bind { [weak self] text in guard let self else { return } @@ -110,7 +116,8 @@ private extension NewCategoriViewController { //MARK: - Обработка событий @objc - func didTapNewСategoriButton() { + func didTapNewСategoriButton() + { guard let viewModel else { return } if let createCategorydelegate { createCategorydelegate.didNewCategoryName(self, @@ -125,11 +132,13 @@ private extension NewCategoriViewController { } @objc - private func hideKeyboard() { + private func hideKeyboard() + { view.endEditing(true) } - func chengeHiddenButton(flag: Bool) { + func chengeHiddenButton(flag: Bool) + { readyButton.isEnabled = !flag readyButton.backgroundColor = !flag ? .blackDay : .grayDay readyButton.backgroundColor = flag ? colors.buttonDisabledColor : .grayDay @@ -137,41 +146,52 @@ private extension NewCategoriViewController { } //MARK: - SetupUI - func setupUIElement() { + func setupUIElement() + { setupCategoriButton() setupCategoriLabel() setupCreateNameTextField() } - func setupCategoriButton() { + func setupCategoriButton() + { view.addSubview(readyButton) NSLayoutConstraint.activate([ readyButton.heightAnchor.constraint(equalToConstant: 60), - readyButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -10), - readyButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20), - readyButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20) + readyButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, + constant: -10), + readyButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, + constant: 20), + readyButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, + constant: -20) ]) } - func setupCategoriLabel() { + func setupCategoriLabel() + { view.addSubview(newCategoriLabel) newCategoriLabel.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ - newCategoriLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 24), + newCategoriLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, + constant: 24), newCategoriLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor) ]) } - func setupCreateNameTextField() { + func setupCreateNameTextField() + { view.addSubview(createNameTextField) createNameTextField.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ - createNameTextField.topAnchor.constraint(equalTo: newCategoriLabel.bottomAnchor, constant: 24), - createNameTextField.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16), - createNameTextField.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16), + createNameTextField.topAnchor.constraint(equalTo: newCategoriLabel.bottomAnchor, + constant: 24), + createNameTextField.leadingAnchor.constraint(equalTo: view.leadingAnchor, + constant: 16), + createNameTextField.trailingAnchor.constraint(equalTo: view.trailingAnchor, + constant: -16), createNameTextField.heightAnchor.constraint(equalToConstant: 75) ]) } @@ -179,20 +199,24 @@ private extension NewCategoriViewController { //MARK: - UITextFieldDelegate extension NewCategoriViewController: UITextFieldDelegate { - func textFieldDidEndEditing(_ textField: UITextField) { + func textFieldDidEndEditing(_ textField: UITextField) + { guard let text = textField.text, let viewModel else { return } viewModel.setNewNameCategory(text: text) } - func textFieldShouldReturn(_ textField: UITextField) -> Bool { + func textFieldShouldReturn(_ textField: UITextField) -> Bool + { createNameTextField.resignFirstResponder() return true } } extension NewCategoriViewController: SelectCategoryEditViewControllerDelegate { - func editCategoriesViewController(vc: UIViewController, oldNameCategory: String) { + func editCategoriesViewController(vc: UIViewController, + oldNameCategory: String) + { guard let viewModel else { return } createNameTextField.text = oldNameCategory viewModel.setNewNameCategory(text: oldNameCategory) diff --git a/Tracker/TabBarController/TrackersViews/ViewControllers/ScheduleViewController.swift b/Tracker/ViewControllers/ViewControllers/ScheduleViewController.swift similarity index 79% rename from Tracker/TabBarController/TrackersViews/ViewControllers/ScheduleViewController.swift rename to Tracker/ViewControllers/ViewControllers/ScheduleViewController.swift index a78ebf2..4889b44 100644 --- a/Tracker/TabBarController/TrackersViews/ViewControllers/ScheduleViewController.swift +++ b/Tracker/ViewControllers/ViewControllers/ScheduleViewController.swift @@ -47,14 +47,17 @@ final class ScheduleViewController: UIViewController { weekDayTableView.backgroundColor = .clear weekDayTableView.separatorInset = ConstantsShedulVc.separatorInsetTableView weekDayTableView.separatorColor = .separatorColor - weekDayTableView.register(WeekDayTableViewCell.self, forCellReuseIdentifier: "\(WeekDayTableViewCell.self)") + weekDayTableView.register(WeekDayTableViewCell.self, + forCellReuseIdentifier: "\(WeekDayTableViewCell.self)") return weekDayTableView }() private lazy var doneButton: UIButton = { let doneButton = UIButton() - doneButton.addTarget(self, action: #selector(didTapDoneButton), for: .touchUpInside) + doneButton.addTarget(self, + action: #selector(didTapDoneButton), + for: .touchUpInside) doneButton.layer.cornerRadius = ConstantsShedulVc.cornerRadiusUIElement doneButton.layer.masksToBounds = true doneButton.backgroundColor = colors.buttonDisabledColor @@ -73,16 +76,19 @@ final class ScheduleViewController: UIViewController { return contentStackView }() - init(viewModel: SheduleViewModelProtocol) { + init(viewModel: SheduleViewModelProtocol) + { self.viewModel = viewModel super.init(nibName: nil, bundle: nil) } - required init?(coder: NSCoder) { + required init?(coder: NSCoder) + { fatalError("init(coder:) has not been implemented") } - override func viewDidLoad() { + override func viewDidLoad() + { super.viewDidLoad() view.backgroundColor = colors.viewBackground setupContentSteck() @@ -93,7 +99,8 @@ final class ScheduleViewController: UIViewController { private extension ScheduleViewController { //MARK: - Обработка событий @objc - func didTapDoneButton() { + func didTapDoneButton() + { guard let delegate else { return } //передаем список дней для поля "schedule" при создании трекера @@ -104,7 +111,8 @@ private extension ScheduleViewController { //MARK: - Логика //метод обновления списка listWeekDay в зависимости от положения переключателя в яцейке weekDayTableView - func updateListWeekDay(flag: Bool, day: WeekDay) { + func updateListWeekDay(flag: Bool, day: WeekDay) + { if flag { var listDay: [WeekDay] = [] listDay = viewModel.listWeekDay @@ -120,7 +128,8 @@ private extension ScheduleViewController { } //MARK: - SetupUI - func setupContentSteck() { + func setupContentSteck() + { view.addSubview(contentStackView) [scheduleLable, weekDayTableView, doneButton].forEach { $0.translatesAutoresizingMaskIntoConstraints = false @@ -129,10 +138,14 @@ private extension ScheduleViewController { contentStackView.setCustomSpacing(38, after: scheduleLable) NSLayoutConstraint.activate([ - contentStackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 16), - contentStackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -16), - contentStackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16), - contentStackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16), + contentStackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, + constant: 16), + contentStackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, + constant: -16), + contentStackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, + constant: 16), + contentStackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, + constant: -16), doneButton.heightAnchor.constraint(equalToConstant: 60) ]) @@ -141,22 +154,29 @@ private extension ScheduleViewController { //MARK: - UITableViewDataSource extension ScheduleViewController: UITableViewDataSource { - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int + { viewModel.weekDay.count } - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - guard let cell = tableView.dequeueReusableCell(withIdentifier: "\(WeekDayTableViewCell.self)") as? WeekDayTableViewCell else { return UITableViewCell() } + func tableView(_ tableView: UITableView, + cellForRowAt indexPath: IndexPath) -> UITableViewCell + { + guard let cell = tableView.dequeueReusableCell(withIdentifier: "\(WeekDayTableViewCell.self)") + as? WeekDayTableViewCell + else { return UITableViewCell() } cell.delegate = self cell.config(nameDay: viewModel.weekDay[indexPath.row]) if viewModel.weekDay[indexPath.row] == viewModel.weekDay.first { cell.setupCornerRadius(cornerRadius: ConstantsShedulVc.cornerRadiusUIElement, - maskedCorners: [.layerMinXMinYCorner, .layerMaxXMinYCorner]) + maskedCorners: [.layerMinXMinYCorner, + .layerMaxXMinYCorner]) } if viewModel.weekDay[indexPath.row] == viewModel.weekDay.last { cell.setupCornerRadius(cornerRadius: ConstantsShedulVc.cornerRadiusUIElement, - maskedCorners: [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]) + maskedCorners: [.layerMinXMaxYCorner, + .layerMaxXMaxYCorner]) cell.separatorInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, @@ -168,15 +188,19 @@ extension ScheduleViewController: UITableViewDataSource { } //MARK: - UITableViewDelegate -extension ScheduleViewController: UITableViewDelegate { - func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { +extension ScheduleViewController: UITableViewDelegate +{ + func tableView(_ tableView: UITableView, + heightForRowAt indexPath: IndexPath) -> CGFloat + { ConstantsShedulVc.heightCell } } //MARK: - WeekDayTableViewCellDelegate extension ScheduleViewController: WeekDayTableViewCellDelegate { - func addDayInListkDay(cell: UITableViewCell, flag: Bool) { + func addDayInListkDay(cell: UITableViewCell, flag: Bool) + { guard let cell = cell as? WeekDayTableViewCell, let indexPath = weekDayTableView.indexPath(for: cell) else { return } diff --git a/Tracker/TabBarController/TrackersViews/ViewControllers/SelectCategoriesViewController.swift b/Tracker/ViewControllers/ViewControllers/SelectCategoriesViewController.swift similarity index 68% rename from Tracker/TabBarController/TrackersViews/ViewControllers/SelectCategoriesViewController.swift rename to Tracker/ViewControllers/ViewControllers/SelectCategoriesViewController.swift index 75f00f1..427fb4c 100644 --- a/Tracker/TabBarController/TrackersViews/ViewControllers/SelectCategoriesViewController.swift +++ b/Tracker/ViewControllers/ViewControllers/SelectCategoriesViewController.swift @@ -27,7 +27,10 @@ final class SelectCategoriesViewController: UIViewController { static let labelStabText = NSLocalizedString("labelStabText", comment: "") static let textEdit = NSLocalizedString("textEdit", comment: "") static let textDelete = NSLocalizedString("textDelete", comment: "") + static let textCancel = NSLocalizedString("textCancel", comment: "") static let textFixed = NSLocalizedString("textFixed", comment: "") + static let sureDeleteTracker = NSLocalizedString("sureDeleteTracker", comment: "") + static let alertDeleteCategoryMessage = NSLocalizedString("deleteCategoryMessage", comment: "") static let lableTextStabNumberOfLines = 2 static let one = 1 @@ -91,7 +94,8 @@ final class SelectCategoriesViewController: UIViewController { selectionCategoryTableView.backgroundColor = .clear selectionCategoryTableView.separatorStyle = .singleLine selectionCategoryTableView.separatorColor = .separatorColor - selectionCategoryTableView.register(CustomTableViewCell.self, forCellReuseIdentifier: "\(CustomTableViewCell.self)") + selectionCategoryTableView.register(CustomTableViewCell.self, + forCellReuseIdentifier: "\(CustomTableViewCell.self)") selectionCategoryTableView.separatorInset = ConstantsCreateCatVc.insertSeparatorTableView return selectionCategoryTableView @@ -106,28 +110,33 @@ final class SelectCategoriesViewController: UIViewController { createCategoryButton.layer.cornerRadius = ConstantsCreateCatVc.cornerRadius createCategoryButton.layer.masksToBounds = true createCategoryButton.translatesAutoresizingMaskIntoConstraints = false - createCategoryButton.addTarget(self, action: #selector(didTapСategoryButton), for: .touchUpInside) + createCategoryButton.addTarget(self, action: #selector(didTapСategoryButton), + for: .touchUpInside) return createCategoryButton }() - init(delegate: SelectCategoriesViewControllerDelegate, viewModel: CategoryViewModelProtocol) { + init(delegate: SelectCategoriesViewControllerDelegate, + viewModel: CategoryViewModelProtocol) + { self.delegate = delegate self.viewModel = viewModel super.init(nibName: nil, bundle: nil) } - required init?(coder: NSCoder) { + required init?(coder: NSCoder) + { fatalError("init(coder:) has not been implemented") } - override func viewDidLoad() { + override func viewDidLoad() + { super.viewDidLoad() guard let viewModel = viewModel as? CategoryViewModel else { return } setupUIElement() bind() view.backgroundColor = colors.viewBackground - handler.resultTypeHandler(viewModel.сategoryExcludingFixed()) { [weak self] cat in + handler.resultTypeHandler(viewModel.сategoryExcludingFixed(), vc: self) { [weak self] cat in guard let self else { return } self.showStabView(flag: !cat.isEmpty) } @@ -141,21 +150,24 @@ final class SelectCategoriesViewController: UIViewController { extension SelectCategoriesViewController { //MARK: - обработка событий @objc - private func didTapСategoryButton() { + private func didTapСategoryButton() + { let newCategoryVC = NewCategoriViewController(createCategorydelegate: self) newCategoryVC.modalPresentationStyle = .formSheet present(newCategoryVC, animated: true) } - private func showStabView(flag: Bool) { + private func showStabView(flag: Bool) + { conteinerStabView.isHidden = flag } - private func bind() { + private func bind() + { guard let viewModel = viewModel as? CategoryViewModel else { return } viewModel.$category.bind { [weak self] _ in guard let self else { return } - handler.resultTypeHandler(viewModel.сategoryExcludingFixed()) { cat in + handler.resultTypeHandler(viewModel.сategoryExcludingFixed(), vc: self) { cat in self.showStabView(flag: !cat.isEmpty) } self.selectionCategoryTableView.reloadData() @@ -171,33 +183,59 @@ extension SelectCategoriesViewController { self.editDelegate = newCategoryVC newCategoryVC.modalPresentationStyle = .formSheet present(newCategoryVC, animated: true) { - self.editDelegate?.editCategoriesViewController(vc: self, oldNameCategory: category.nameCategory) + self.editDelegate?.editCategoriesViewController(vc: self, + oldNameCategory: category.nameCategory) } } } - private func deleteAction(text: String, category: TrackerCategory, trackers: [Tracker]) -> UIAction { + private func deleteAction(text: String, + category: TrackerCategory, + index: Int, + trackers: [Tracker]) -> UIAction + { UIAction(title: text, attributes: .destructive) { [weak self] _ in guard let self else { return } - self.handler.resultTypeHandler(self.viewModel.deleteTrackerRecords(trackers: trackers)) {} - self.handler.resultTypeHandler(self.viewModel.deleteTrackers(trackers: category.arrayTrackers)) {} - self.handler.resultTypeHandler(self.viewModel.deleteCategory(nameCategory: category.nameCategory)) { - self.handler.resultTypeHandler(self.viewModel.deleteTrackers(trackers: category.arrayTrackers)) { - self.handler.resultTypeHandler(self.viewModel.deleteTrackerRecords(trackers: trackers)) {} + let alert = UIAlertController(title: nil, + message: ConstantsCreateCatVc.alertDeleteCategoryMessage, + preferredStyle: .actionSheet) + let deleteAction = UIAlertAction(title: ConstantsCreateCatVc.textDelete, + style: .destructive) { _ in + if let isCategorySelected = self.handler.resultTypeHandlerGetValue(self.viewModel.isCategorySelected(at: index), + vc: self), + !isCategorySelected { + self.viewModel.deleteSelectCategory() + } + self.handler.resultTypeHandler(self.viewModel.deleteCategory(nameCategory: category.nameCategory), + vc: self) { + self.handler.resultTypeHandler(self.viewModel.deleteTrackers(trackers: category.arrayTrackers), + vc: self) { + self.handler.resultTypeHandler(self.viewModel.deleteTrackerRecords(trackers: trackers), + vc: self) {} + } } } + let cancelAction = UIAlertAction(title: ConstantsCreateCatVc.textCancel, + style: .cancel) { _ in + alert.dismiss(animated: true) + } + alert.addAction(deleteAction) + alert.addAction(cancelAction) + present(alert, animated: true) } } //MARK: - SetupUI - private func setupUIElement() { + private func setupUIElement() + { setupCategoryButton() setupCategoryLabel() setupTableView() setupStabView() } - private func setupCategoryButton() { + private func setupCategoryButton() + { view.addSubview(createCategoryButton) NSLayoutConstraint.activate([ @@ -208,29 +246,36 @@ extension SelectCategoriesViewController { ]) } - private func setupCategoryLabel() { + private func setupCategoryLabel() + { view.addSubview(categoryLabel) categoryLabel.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ - categoryLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 24), + categoryLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, + constant: 24), categoryLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor) ]) } - private func setupTableView() { + private func setupTableView() + { view.addSubview(selectionCategoryTableView) selectionCategoryTableView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ - selectionCategoryTableView.topAnchor.constraint(equalTo: categoryLabel.bottomAnchor, constant: 24), - selectionCategoryTableView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16), - selectionCategoryTableView.trailingAnchor.constraint(equalTo: view.trailingAnchor,constant: -16), + selectionCategoryTableView.topAnchor.constraint(equalTo: categoryLabel.bottomAnchor, + constant: 24), + selectionCategoryTableView.leadingAnchor.constraint(equalTo: view.leadingAnchor, + constant: 16), + selectionCategoryTableView.trailingAnchor.constraint(equalTo: view.trailingAnchor, + constant: -16), selectionCategoryTableView.bottomAnchor.constraint(equalTo: createCategoryButton.topAnchor) ]) } - private func setupStabView() { + private func setupStabView() + { view.addSubview(conteinerStabView) [imageViewStab, lableTextStab].forEach { conteinerStabView.addSubview($0) @@ -247,22 +292,30 @@ extension SelectCategoriesViewController { lableTextStab.widthAnchor.constraint(equalToConstant: 200), lableTextStab.centerXAnchor.constraint(equalTo: conteinerStabView.centerXAnchor), - lableTextStab.topAnchor.constraint(equalTo: imageViewStab.bottomAnchor, constant: 10) + lableTextStab.topAnchor.constraint(equalTo: imageViewStab.bottomAnchor, + constant: 10) ]) } } //MARK: - UITableViewDataSource extension SelectCategoriesViewController: UITableViewDataSource { - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - handler.resultTypeHandlerGetValue(viewModel.сategoryExcludingFixed())?.count ?? 0 + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int + { + handler.resultTypeHandlerGetValue(viewModel.сategoryExcludingFixed(), + vc: self)?.count ?? 0 } - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - guard let cell = tableView.dequeueReusableCell(withIdentifier: "\(CustomTableViewCell.self)") as? CustomTableViewCell, - let isSelected = handler.resultTypeHandlerGetValue(viewModel.isCategorySelected(at: indexPath.row)), - let text = handler.resultTypeHandlerGetValue(viewModel.createNameCategory(at: indexPath.row)), - let category = handler.resultTypeHandlerGetValue(viewModel.сategoryExcludingFixed()) + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell + { + guard let cell = tableView.dequeueReusableCell(withIdentifier: "\(CustomTableViewCell.self)") + as? CustomTableViewCell, + let isSelected = handler.resultTypeHandlerGetValue(viewModel.isCategorySelected(at: indexPath.row), + vc: self), + let text = handler.resultTypeHandlerGetValue(viewModel.createNameCategory(at: indexPath.row), + vc: self), + let category = handler.resultTypeHandlerGetValue(viewModel.сategoryExcludingFixed(), + vc: self) else { return UITableViewCell() } cell.showSelectedImage(flag: isSelected) let model = CustomCellModel(text: text, color: .backgroundNight) @@ -275,23 +328,27 @@ extension SelectCategoriesViewController: UITableViewDataSource { if indexPath.row == .zero && category.count == ConstantsCreateCatVc.one { cell.setupCornerRadius(cornerRadius: ConstantsCreateCatVc.cornerRadius, maskedCorners: nil) - cell.separatorInset = UIEdgeInsets(top: .zero, left: view.bounds.width, bottom: .zero, right: .zero) + cell.separatorInset = UIEdgeInsets(top: .zero, + left: view.bounds.width, + bottom: .zero, + right: .zero) } if indexPath.row == .zero && category.count > ConstantsCreateCatVc.one { cell.setupCornerRadius(cornerRadius: ConstantsCreateCatVc.cornerRadius, - maskedCorners: [.layerMaxXMinYCorner, .layerMinXMinYCorner]) + maskedCorners: [.layerMaxXMinYCorner, + .layerMinXMinYCorner]) } if indexPath.row == category.count - ConstantsCreateCatVc.one && category.count > ConstantsCreateCatVc.one { cell.setupCornerRadius(cornerRadius: ConstantsCreateCatVc.cornerRadius, - maskedCorners: [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]) - cell.separatorInset = UIEdgeInsets(top: .zero, left: view.bounds.width, bottom: .zero, right: .zero) - } - - if category.count == ConstantsCreateCatVc.one { - cell.layer.cornerRadius = 16 + maskedCorners: [.layerMinXMaxYCorner, + .layerMaxXMaxYCorner]) + cell.separatorInset = UIEdgeInsets(top: .zero, + left: view.bounds.width, + bottom: .zero, + right: .zero) } return cell @@ -300,13 +357,17 @@ extension SelectCategoriesViewController: UITableViewDataSource { //MARK: - UITableViewDelegate extension SelectCategoriesViewController: UITableViewDelegate { - func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + func tableView(_ tableView: UITableView, + didSelectRowAt indexPath: IndexPath) + { guard let cell = tableView.cellForRow(at: indexPath) as? CustomTableViewCell, let delegate else { return } cell.showSelectedImage(flag: false) - handler.resultTypeHandler(viewModel.selectСategory(at: indexPath.row)) {} - handler.resultTypeHandler(viewModel.createNameCategory(at: indexPath.row)) { [weak self] cat in + handler.resultTypeHandler(viewModel.selectСategory(at: indexPath.row), + vc: self) {} + handler.resultTypeHandler(viewModel.createNameCategory(at: indexPath.row), + vc: self) { [weak self] cat in guard let self else { return } delegate.categoriesViewController(vc: self, nameCategory: cat) } @@ -315,14 +376,20 @@ extension SelectCategoriesViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint - ) -> UIContextMenuConfiguration? { + ) -> UIContextMenuConfiguration? + { return UIContextMenuConfiguration(actionProvider: { [weak self] _ in guard let self, - let category = handler.resultTypeHandlerGetValue(viewModel.сategoryExcludingFixed())?[indexPath.row] + let category = handler.resultTypeHandlerGetValue(viewModel.сategoryExcludingFixed(), + vc: self)?[indexPath.row] else { return UIMenu()} return UIMenu(children: [ - editAction(text: ConstantsCreateCatVc.textEdit, category: category), - deleteAction(text: ConstantsCreateCatVc.textDelete, category: category, trackers: category.arrayTrackers) + editAction(text: ConstantsCreateCatVc.textEdit, + category: category), + deleteAction(text: ConstantsCreateCatVc.textDelete, + category: category, + index: indexPath.row, + trackers: category.arrayTrackers) ]) }) } @@ -330,23 +397,32 @@ extension SelectCategoriesViewController: UITableViewDelegate { //MARK: - NewCategoriViewControllerDelegate extension SelectCategoriesViewController: NewCategoriViewControllerDelegate { - func didNewCategoryName(_ vc: UIViewController, nameCategory: String) { - guard let category = handler.resultTypeHandlerGetValue(viewModel.getCategory()) + func didNewCategoryName(_ vc: UIViewController, nameCategory: String) + { + guard let category = handler.resultTypeHandlerGetValue(viewModel.getCategory(), + vc: self) else { return } let nameFirstUppercased = nameCategory.lowercased().firstUppercased if let _ = category.filter({ $0.nameCategory == nameFirstUppercased }).first { return } - if nameFirstUppercased == Fixed.fixedRu.rawValue || nameFirstUppercased == Fixed.fixedEng.rawValue { + if nameFirstUppercased == Fixed.fixedRu.rawValue || + nameFirstUppercased == Fixed.fixedEng.rawValue { return } - handler.resultTypeHandler(viewModel.addCategory(nameCategory: nameFirstUppercased)) {} + handler.resultTypeHandler(viewModel.addCategory(nameCategory: nameFirstUppercased), + vc: self) {} } } //MARK: - UpdateCategoriViewControllerDelegate extension SelectCategoriesViewController: UpdateCategoriViewControllerDelegate { - func didUpdateCategoryName(_ vc: UIViewController, newNameCategory: String, oldNameCategory: String) { - handler.resultTypeHandler(viewModel.updateNameCategory(newNameCategory: newNameCategory, oldNameCaetegory: oldNameCategory)) {} + func didUpdateCategoryName(_ vc: UIViewController, + newNameCategory: String, + oldNameCategory: String) + { + handler.resultTypeHandler(viewModel.updateNameCategory(newNameCategory: newNameCategory, + oldNameCaetegory: oldNameCategory), + vc: self) {} } } diff --git a/Tracker/TabBarController/TrackersViews/StatisticViews/StatisticsViewController.swift b/Tracker/ViewControllers/ViewControllers/StatisticsViewController.swift similarity index 74% rename from Tracker/TabBarController/TrackersViews/StatisticViews/StatisticsViewController.swift rename to Tracker/ViewControllers/ViewControllers/StatisticsViewController.swift index 1359da3..fca1d92 100644 --- a/Tracker/TabBarController/TrackersViews/StatisticViews/StatisticsViewController.swift +++ b/Tracker/ViewControllers/ViewControllers/StatisticsViewController.swift @@ -43,6 +43,12 @@ class StatisticsViewController: UIViewController { return cardStatistics }() + private lazy var cardStatysticsViewStack: UIStackView = { + let cardStatysticsViewStack = UIStackView() + + return cardStatysticsViewStack + }() + private lazy var imageViewStab: UIImageView = { let imageViewStab = UIImageView() imageViewStab.image = UIImage(named: ConstantsStatisticVC.imageNothingFound) @@ -58,7 +64,8 @@ class StatisticsViewController: UIViewController { return lableTextStab }() - override func viewDidLoad() { + override func viewDidLoad() + { super.viewDidLoad() view.backgroundColor = colors.viewBackground viewModel = StatisticsViewModel() @@ -68,9 +75,10 @@ class StatisticsViewController: UIViewController { setupSubView() } - override func viewDidLayoutSubviews() { + override func viewDidLayoutSubviews() + { super.viewDidLayoutSubviews() - addBorderGradient(view: cardStatysticsView, + addBorderGradient(views: [cardStatysticsView], colors: [UIColor.redGradient.cgColor, UIColor.greenGradient.cgColor, UIColor.blueGradient.cgColor], @@ -80,14 +88,16 @@ class StatisticsViewController: UIViewController { cornerRadius: ConstantsStatisticVC.borderRadius) } - override func viewWillAppear(_ animated: Bool) { + override func viewWillAppear(_ animated: Bool) + { super.viewWillAppear(animated) showStabView(flag: viewModel?.getIsTracker()) } } private extension StatisticsViewController { - func bind() { + func bind() + { guard let viewModel = viewModel as? StatisticsViewModel else { return } viewModel.$countTrackerComplet.bind { [weak self] count in guard let self else { return } @@ -95,50 +105,56 @@ private extension StatisticsViewController { } } - func addBorderGradient(view: UIView, - colors: [CGColor], - borderWidth: CGFloat, - startPoint: CGPoint, - endPoint: CGPoint, - cornerRadius: CGFloat) + func addBorderGradient(views: [UIView], + colors: [CGColor], + borderWidth: CGFloat, + startPoint: CGPoint, + endPoint: CGPoint, + cornerRadius: CGFloat) { - view.layer.cornerRadius = cornerRadius - view.clipsToBounds = true - - let gradient = CAGradientLayer() - gradient.frame = view.bounds - gradient.colors = colors - gradient.startPoint = startPoint - gradient.endPoint = endPoint - - let shape = CAShapeLayer() - shape.lineWidth = borderWidth - let path = UIBezierPath(roundedRect: view.bounds, cornerRadius: cornerRadius).cgPath - shape.path = path - shape.strokeColor = UIColor.blackDay.cgColor - shape.fillColor = UIColor.clear.cgColor - gradient.mask = shape - view.layer.addSublayer(gradient) + views.forEach { + $0.layer.cornerRadius = cornerRadius + $0.clipsToBounds = true + + let gradient = CAGradientLayer() + gradient.frame = $0.bounds + gradient.colors = colors + gradient.startPoint = startPoint + gradient.endPoint = endPoint + + let shape = CAShapeLayer() + shape.lineWidth = borderWidth + let path = UIBezierPath(roundedRect: $0.bounds, + cornerRadius: cornerRadius).cgPath + shape.path = path + shape.strokeColor = UIColor.blackDay.cgColor + shape.fillColor = UIColor.clear.cgColor + gradient.mask = shape + $0.layer.addSublayer(gradient) + } } - func showStabView(flag: Bool?) { + func showStabView(flag: Bool?) + { guard let flag else { - imageViewStab.isHidden = false - lableTextStab.isHidden = false - cardStatysticsView.isHidden = true + imageViewStab.isHidden = flag == nil + lableTextStab.isHidden = flag == nil + cardStatysticsView.isHidden = flag != nil return } imageViewStab.isHidden = !flag lableTextStab.isHidden = !flag cardStatysticsView.isHidden = flag } - func setupSubView() { + func setupSubView() + { setupLabelHeader() setupStabView() setupCardStatysticsView() } - func setupLabelHeader() { + func setupLabelHeader() + { view.addSubview(labelHeader) NSLayoutConstraint.activate([ labelHeader.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16), @@ -146,7 +162,8 @@ private extension StatisticsViewController { ]) } - func setupStabView() { + func setupStabView() + { [imageViewStab, lableTextStab].forEach { view.addSubview($0) $0.translatesAutoresizingMaskIntoConstraints = false @@ -162,7 +179,8 @@ private extension StatisticsViewController { ]) } - func setupCardStatysticsView() { + func setupCardStatysticsView() + { view.addSubview(cardStatysticsView) NSLayoutConstraint.activate([ cardStatysticsView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16), diff --git a/Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift b/Tracker/ViewControllers/ViewControllers/TrackersViewController.swift similarity index 73% rename from Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift rename to Tracker/ViewControllers/ViewControllers/TrackersViewController.swift index 2727b49..c7efe42 100644 --- a/Tracker/TabBarController/TrackersViews/ViewControllers/TrackersViewController.swift +++ b/Tracker/ViewControllers/ViewControllers/TrackersViewController.swift @@ -8,8 +8,13 @@ import UIKit //MARK: - TrackersViewControllerDelegate -protocol TrackersViewControllerDelegate: AnyObject { - func editTracker(vc: TrackersViewController, editTracker: Tracker, count: Int, nameCategory: String, indexPath: IndexPath) +protocol TrackersViewControllerDelegate: AnyObject +{ + func editTracker(vc: TrackersViewController, + editTracker: Tracker, + count: Int, + nameCategory: String, + indexPath: IndexPath) } //MARK: - TrackersViewController @@ -113,6 +118,7 @@ final class TrackersViewController: UIViewController { trackerCollectionView.allowsMultipleSelection = false registerCellAndHeader(collectionView: trackerCollectionView) + return trackerCollectionView }() @@ -151,16 +157,19 @@ final class TrackersViewController: UIViewController { return buttonFilter }() - init(viewModel: TrackerViewModelProtocol) { + init(viewModel: TrackerViewModelProtocol) + { super.init(nibName: nil, bundle: nil) } - required init?(coder: NSCoder) { + required init?(coder: NSCoder) + { super.init(coder: coder) assertionFailure("init(coder:) has not been implemented") } - override func viewDidLoad() { + override func viewDidLoad() + { super.viewDidLoad() view.backgroundColor = colors.viewBackground setupUIElement() @@ -177,13 +186,15 @@ final class TrackersViewController: UIViewController { } } - override func viewWillAppear(_ animated: Bool) { + override func viewWillAppear(_ animated: Bool) + { AnalyticsService.shared.open() } } private extension TrackersViewController { - func bind() { + func bind() + { guard let viewModel = viewModel as? TrackerViewModel else { return } viewModel.$category.bind { [weak self] _ in guard let self else { return } @@ -195,14 +206,15 @@ private extension TrackersViewController { self.trackerCollectionView.reloadItems(at: [indexPath]) } - viewModel.$filterListTrackersWeekDay.bind { [weak self] category in - guard let self else { return } - self.buttonFilter.isHidden = category.isEmpty + viewModel.$isfilterListTrackersWeekDay.bind { [weak self] flag in + guard let self, let flag else { return } + self.buttonFilter.isHidden = !flag } viewModel.$visibleCategory.bind { [weak self] result in guard let self, - let cat = handler.resultTypeHandlerGetValue(result) + let cat = handler.resultTypeHandlerGetValue(result, + vc: self) else { return } self.showStabView(flag: !cat.isEmpty) self.trackerCollectionView.reloadData() @@ -213,76 +225,93 @@ private extension TrackersViewController { switch state { case .allTrackers: viewModel.allTrackersByDate(date: datePicker.date) - setColorButtonFilter(state: viewModel.getFilterState(), button: buttonFilter) + setColorButtonFilter(state: viewModel.getFilterState(), + button: buttonFilter) case .toDayTrackers: datePicker.date = Date() viewModel.allTrackersByDate(date: datePicker.date) - setColorButtonFilter(state: viewModel.getFilterState(), button: buttonFilter) + setColorButtonFilter(state: viewModel.getFilterState(), + button: buttonFilter) case .completed: viewModel.getCompleted(date: datePicker.date, flag: true) - setColorButtonFilter(state: viewModel.getFilterState(), button: buttonFilter) + setColorButtonFilter(state: viewModel.getFilterState(), + button: buttonFilter) case .notCompleted: viewModel.getNotCompleted(date: datePicker.date, flag: false) - setColorButtonFilter(state: viewModel.getFilterState(), button: buttonFilter) + setColorButtonFilter(state: viewModel.getFilterState(), + button: buttonFilter) } } } //MARK: - обработка событий @objc - func actionButtonFilter() { + func actionButtonFilter() + { AnalyticsService.shared.filter() - let filtersVC = FiltersViewController(viewModel: FiltersViewModel(), delegate: self) + let filtersVC = FiltersViewController(viewModel: FiltersViewModel(), + delegate: self) filtersVC.modalPresentationStyle = .formSheet present(filtersVC, animated: true) } @objc - func actionForTapDatePicker() { + func actionForTapDatePicker() + { guard let viewModel else { return } filterState(state: viewModel.getFilterState()) } @objc - func didTapLeftNavBarItem() { + func didTapLeftNavBarItem() + { let greateVC = EventSelectionViewController() greateVC.delegate = self greateVC.modalPresentationStyle = .formSheet present(greateVC, animated: true) } - func chengeStab(text: String, nameImage: String) { + func chengeStab(text: String, nameImage: String) + { lableTextStab.text = text imageViewStab.image = UIImage(named: nameImage) } - func showStabView(flag: Bool) { + func showStabView(flag: Bool) + { [imageViewStab, lableTextStab].forEach { $0.isHidden = flag } } - func filterState(state: FiltersState) { + func filterState(state: FiltersState) + { guard let viewModel else { return } switch state { case .allTrackers: viewModel.allTrackersByDate(date: datePicker.date) - setColorButtonFilter(state: viewModel.getFilterState(), button: buttonFilter) + setColorButtonFilter(state: viewModel.getFilterState(), + button: buttonFilter) case .toDayTrackers: viewModel.allTrackersByDate(date: datePicker.date) - setColorButtonFilter(state: viewModel.getFilterState(), button: buttonFilter) + setColorButtonFilter(state: viewModel.getFilterState(), + button: buttonFilter) case .completed: viewModel.getCompleted(date: datePicker.date, flag: true) - setColorButtonFilter(state: viewModel.getFilterState(), button: buttonFilter) + setColorButtonFilter(state: viewModel.getFilterState(), + button: buttonFilter) case .notCompleted: viewModel.getNotCompleted(date: datePicker.date, flag: false) - setColorButtonFilter(state: viewModel.getFilterState(), button: buttonFilter) + setColorButtonFilter(state: viewModel.getFilterState(), + button: buttonFilter) } } - func setColorButtonFilter(state: FiltersState, button: UIButton) { + func setColorButtonFilter(state: FiltersState, button: UIButton) + { button.setTitleColor(state == .allTrackers ? .colorSelection9 : .colorSelection1, for: .normal) } - func greateTrackerCollectionView() -> TrackerCollectionView { + func greateTrackerCollectionView() -> TrackerCollectionView + { let params = GeometricParams(cellCount: ConstantsTrackerVc.cellCount, leftInset: .zero, rightInset: .zero, @@ -294,7 +323,8 @@ private extension TrackersViewController { return trackerCollectionView } - func registerCellAndHeader(collectionView: TrackerCollectionView) { + func registerCellAndHeader(collectionView: TrackerCollectionView) + { collectionView.register(TrackersCollectionViewCell.self, forCellWithReuseIdentifier: "\(TrackersCollectionViewCell.self)") collectionView.register(HeaderReusableView.self, @@ -303,33 +333,49 @@ private extension TrackersViewController { } //MARK: - Menu Action - func pinnedAction(text: String, tracker: Tracker, category: TrackerCategory) -> UIAction { + func pinnedAction(text: String, tracker: Tracker, category: TrackerCategory) -> UIAction + { UIAction(title: text) { [weak self] _ in guard let self, let viewModel, - let cat = self.handler.resultTypeHandlerGetValue(viewModel.getCategory()) + let cat = self.handler.resultTypeHandlerGetValue(viewModel.getCategory(), + vc: self) else { return } if category.isPinned { - handler.resultTypeHandler(viewModel.addPinnedCategory(id: tracker.id, nameCategory: category.nameCategory)) { - self.handler.resultTypeHandler(viewModel.deleteTracker(tracker.id)) {} + handler.resultTypeHandler(viewModel.addPinnedCategory(id: tracker.id, + nameCategory: category.nameCategory), + vc: self) { + self.handler.resultTypeHandler(viewModel.deleteTracker(id: tracker.id), + vc: self) {} } let filterNameCategory = cat.filter { $0.nameCategory == ConstantsTrackerVc.textFixed } if let _ = filterNameCategory.first?.nameCategory { - self.handler.resultTypeHandler(viewModel.addNewTracker(tracker, nameCategory: ConstantsTrackerVc.textFixed)) {} + self.handler.resultTypeHandler(viewModel.addNewTracker(tracker, + nameCategory: ConstantsTrackerVc.textFixed), + vc: self) {} } else { - self.handler.resultTypeHandler(viewModel.addCategory(nameCategory: ConstantsTrackerVc.textFixed)) { - self.handler.resultTypeHandler(viewModel.addNewTracker(tracker, nameCategory: ConstantsTrackerVc.textFixed)) {} + self.handler.resultTypeHandler(viewModel.addCategory(nameCategory: ConstantsTrackerVc.textFixed), + vc: self) { + self.handler.resultTypeHandler(viewModel.addNewTracker(tracker, + nameCategory: ConstantsTrackerVc.textFixed), + vc: self) {} } } } else { - if let nameCategory = handler.resultTypeHandlerGetValue(viewModel.deleteAndGetPinnedCategory(id: tracker.id)) { - self.handler.resultTypeHandler(viewModel.deleteTracker(tracker.id)) {} - if let category = handler.resultTypeHandlerGetValue(viewModel.category)?.filter({ $0.nameCategory == nameCategory }).first { - self.handler.resultTypeHandler(viewModel.addNewTracker(tracker, nameCategory: category.nameCategory)) {} + if let nameCategory = handler.resultTypeHandlerGetValue(viewModel.deleteAndGetPinnedCategory(id: tracker.id), + vc: self) { + self.handler.resultTypeHandler(viewModel.deleteTracker(id: tracker.id), + vc: self) {} + if let category = handler.resultTypeHandlerGetValue(viewModel.category, + vc: self)?.filter({ $0.nameCategory == nameCategory }).first { + self.handler.resultTypeHandler(viewModel.addNewTracker(tracker, nameCategory: category.nameCategory), vc: self) {} } else { guard let nameCategory else { return } - self.handler.resultTypeHandler(viewModel.addCategory(nameCategory: nameCategory)) { - self.handler.resultTypeHandler(viewModel.addNewTracker(tracker, nameCategory: nameCategory)) {} + self.handler.resultTypeHandler(viewModel.addCategory(nameCategory: nameCategory), + vc: self) { + self.handler.resultTypeHandler(viewModel.addNewTracker(tracker, + nameCategory: nameCategory), + vc: self) {} } } } @@ -347,14 +393,16 @@ private extension TrackersViewController { let viewModel else { return } let editViewModel = EditTrackerViewModel() - let createTrackerVC = CreateTrackerViewController(viewModel: editViewModel, updateTrackerDelegate: self) + let createTrackerVC = CreateTrackerViewController(viewModel: editViewModel, + updateTrackerDelegate: self) delegate = createTrackerVC if tracker.schedule.count != editViewModel.regular.count { createTrackerVC.reverseIsSchedul() } createTrackerVC.modalPresentationStyle = .formSheet present(createTrackerVC, animated: true) { - let count = self.handler.resultTypeHandlerGetValue(viewModel.getCountTrackerCompleted(id: tracker.id)) + let count = self.handler.resultTypeHandlerGetValue(viewModel.getCountTrackerCompleted(id: tracker.id), + vc: self) guard let count else { return } self.delegate?.editTracker(vc: self, editTracker: tracker, @@ -366,7 +414,8 @@ private extension TrackersViewController { } } - func deleteAction(text: String, tracker: Tracker) -> UIAction { + func deleteAction(text: String, tracker: Tracker) -> UIAction + { UIAction(title: text, attributes: .destructive) { [weak self] _ in guard let self, let viewModel else { return } let alert = UIAlertController(title: nil, @@ -375,15 +424,15 @@ private extension TrackersViewController { let deleteAction = UIAlertAction(title: ConstantsTrackerVc.textDelete, style: .destructive) { _ in - self.handler.resultTypeHandler(viewModel.deleteTracker(tracker.id)) { - self.handler.resultTypeHandler(viewModel.deleteTrackersRecord(id: tracker.id)) {} - } + self.handler.resultTypeHandler(viewModel.deleteTrackersRecord(id: tracker.id), + vc: self) {} + self.handler.resultTypeHandler(viewModel.deleteTracker(id: tracker.id), + vc: self) {} AnalyticsService.shared.delete() - alert.dismiss(animated: true) } let cancelAction = UIAlertAction(title: ConstantsTrackerVc.textCancel, style: .cancel) { _ in - self.dismiss(animated: true) + alert.dismiss(animated: true) } alert.addAction(deleteAction) alert.addAction(cancelAction) @@ -400,7 +449,8 @@ private extension TrackersViewController { setupButtonFilter() } - func setupNavBarItem() { + func setupNavBarItem() + { guard let navBar = navigationController?.navigationBar, let topItem = navBar.topItem else { return } @@ -424,10 +474,12 @@ private extension TrackersViewController { searcheController.searchBar.placeholder = ConstantsTrackerVc.placeholderSearch searcheController.searchBar.resignFirstResponder() searcheController.searchBar.returnKeyType = .search - searcheController.searchBar.setValue(ConstantsTrackerVc.textCancel, forKey: ConstantsTrackerVc.forKeyTextCancel) + searcheController.searchBar.setValue(ConstantsTrackerVc.textCancel, + forKey: ConstantsTrackerVc.forKeyTextCancel) } - func setupContentView() { + func setupContentView() + { trackerCollectionView.translatesAutoresizingMaskIntoConstraints = false contentView.addSubview(trackerCollectionView) @@ -439,7 +491,8 @@ private extension TrackersViewController { ]) } - func setupVerticalSteck() { + func setupVerticalSteck() + { view.addSubview(verticalStack) contentView.translatesAutoresizingMaskIntoConstraints = false contentView.backgroundColor = .clear @@ -448,12 +501,15 @@ private extension TrackersViewController { NSLayoutConstraint.activate([ verticalStack.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), verticalStack.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor), - verticalStack.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16), - verticalStack.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16) + verticalStack.leadingAnchor.constraint(equalTo: view.leadingAnchor, + constant: 16), + verticalStack.trailingAnchor.constraint(equalTo: view.trailingAnchor, + constant: -16) ]) } - func setupStabView() { + func setupStabView() + { [imageViewStab, lableTextStab].forEach { view.addSubview($0) $0.translatesAutoresizingMaskIntoConstraints = false @@ -465,16 +521,19 @@ private extension TrackersViewController { imageViewStab.centerXAnchor.constraint(equalTo: view.centerXAnchor), lableTextStab.centerXAnchor.constraint(equalTo: view.centerXAnchor), - lableTextStab.topAnchor.constraint(equalTo: imageViewStab.bottomAnchor, constant: 10) + lableTextStab.topAnchor.constraint(equalTo: imageViewStab.bottomAnchor, + constant: 10) ]) } - func setupButtonFilter() { + func setupButtonFilter() + { view.addSubview(buttonFilter) NSLayoutConstraint.activate([ buttonFilter.centerXAnchor.constraint(equalTo: view.centerXAnchor), - buttonFilter.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -16), + buttonFilter.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, + constant: -16), buttonFilter.heightAnchor.constraint(equalToConstant: 50), buttonFilter.widthAnchor.constraint(equalToConstant: 114) ]) @@ -483,29 +542,39 @@ private extension TrackersViewController { //MARK: - UICollectionViewDataSource extension TrackersViewController: UICollectionViewDataSource { - func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + func collectionView(_ collectionView: UICollectionView, + numberOfItemsInSection section: Int) -> Int + { guard let viewModel = viewModel as? TrackerViewModel, - let itemsCount = handler.resultTypeHandlerGetValue(viewModel.visibleCategory)?[section].arrayTrackers.count + let itemsCount = handler.resultTypeHandlerGetValue(viewModel.visibleCategory, + vc: self)?[section].arrayTrackers.count else { return .zero } return itemsCount } - - func numberOfSections(in collectionView: UICollectionView) -> Int { + + func numberOfSections(in collectionView: UICollectionView) -> Int + { guard let viewModel = viewModel as? TrackerViewModel, - let sectionCount = handler.resultTypeHandlerGetValue(viewModel.visibleCategory)?.count + let sectionCount = handler.resultTypeHandlerGetValue(viewModel.visibleCategory, + vc: self)?.count else { return .zero } return sectionCount } - func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + func collectionView(_ collectionView: UICollectionView, + cellForItemAt indexPath: IndexPath) -> UICollectionViewCell + { guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "\(TrackersCollectionViewCell.self)", for: indexPath) as? TrackersCollectionViewCell, let viewModel = viewModel as? TrackerViewModel, - let visibleCategories = handler.resultTypeHandlerGetValue(viewModel.visibleCategory) + let visibleCategories = handler.resultTypeHandlerGetValue(viewModel.visibleCategory, + vc: self) else { return UICollectionViewCell() } cell.delegate = self let tracker = visibleCategories[indexPath.section].arrayTrackers[indexPath.row] - let dateComparison = Calendar.current.compare(Date(), to: datePicker.date, toGranularity: .day) + let dateComparison = Calendar.current.compare(Date(), + to: datePicker.date, + toGranularity: .day) switch dateComparison { case .orderedAscending: cell.isEnableAddButton(flag: false) @@ -518,12 +587,15 @@ extension TrackersViewController: UICollectionViewDataSource { cell.updateBackgraundAddButton(isHidden: true) } cell.setPinned(flag: visibleCategories[indexPath.section].isPinned) - handler.resultTypeHandler(viewModel.loadTrackerRecord(id: tracker.id)) { [weak self] count in + handler.resultTypeHandler(viewModel.loadTrackerRecord(id: tracker.id), + vc: self) { [weak self] count in guard let self, let isComplited = self.handler.resultTypeHandlerGetValue(viewModel.getIsComplited(tracker: tracker, - date: self.datePicker.date)) + date: self.datePicker.date), + vc: self) else { return } - let updateModel = UpdateTracker(count: count, flag: isComplited) + let updateModel = UpdateTracker(count: count, + flag: isComplited) cell.config(tracker: tracker) cell.updateLableCountAndImageAddButton(updateModel) } @@ -535,29 +607,39 @@ extension TrackersViewController: UICollectionViewDataSource { extension TrackersViewController: UICollectionViewDelegate { func collectionView(_ collectionView: UICollectionView, contextMenuConfiguration configuration: UIContextMenuConfiguration, - highlightPreviewForItemAt indexPath: IndexPath) -> UITargetedPreview? { + highlightPreviewForItemAt indexPath: IndexPath) -> UITargetedPreview? + { guard let cell = trackerCollectionView.cellForItem(at: indexPath) as? TrackersCollectionViewCell else { return nil } - let previewTarget = UIPreviewTarget(container: self.trackerCollectionView, center: cell.center) - return .init(view: cell.getView(), parameters: UIPreviewParameters(), target: previewTarget) + let previewTarget = UIPreviewTarget(container: self.trackerCollectionView, + center: cell.center) + return .init(view: cell.getView(), + parameters: UIPreviewParameters(), + target: previewTarget) } - - func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { - guard let viewModel, - let visibleCategories = handler.resultTypeHandlerGetValue(viewModel.visibleCategory) - else { return } - - if visibleCategories.count - 1 == indexPath.row { + + func scrollViewDidScroll(_ scrollView: UIScrollView) + { + guard let flag = viewModel?.getIsCategoryForDay(), flag else { return } + let offsetY = scrollView.contentOffset.y + if offsetY > 0 { + // Прокрутка вниз - скрыть кнопку + buttonFilter.isHidden = true + trackerCollectionView.contentInset.bottom = buttonFilter.bounds.height + } else { + // Прокрутка вверх или в самый верх - показать кнопку buttonFilter.isHidden = false } } func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemsAt indexPaths: [IndexPath], - point: CGPoint) -> UIContextMenuConfiguration? { + point: CGPoint) -> UIContextMenuConfiguration? + { guard let indexPath = indexPaths.first, let viewModel, - let visibleCategories = handler.resultTypeHandlerGetValue(viewModel.visibleCategory) + let visibleCategories = handler.resultTypeHandlerGetValue(viewModel.visibleCategory, + vc: self) else { return nil } let selectorCategory = visibleCategories[indexPath.section] let selectedTracker = selectorCategory.arrayTrackers[indexPath.row] @@ -582,7 +664,10 @@ extension TrackersViewController: UICollectionViewDelegate { //MARK: - UICollectionViewDelegateFlowLayout extension TrackersViewController: UICollectionViewDelegateFlowLayout { - func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + func collectionView(_ collectionView: UICollectionView, + layout collectionViewLayout: UICollectionViewLayout, + sizeForItemAt indexPath: IndexPath) -> CGSize + { let availableWidth = collectionView.frame.width - trackerCollectionView.params.paddingWidth let cellWidth = availableWidth / CGFloat(trackerCollectionView.params.cellCount) let sizeCell = CGSize(width: cellWidth, height: ConstantsTrackerVc.heightCollectionView) @@ -590,27 +675,44 @@ extension TrackersViewController: UICollectionViewDelegateFlowLayout { return sizeCell } - func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { + func collectionView(_ collectionView: UICollectionView, + layout collectionViewLayout: UICollectionViewLayout, + insetForSectionAt section: Int) -> UIEdgeInsets + { UIEdgeInsets(top: 10, left: trackerCollectionView.params.leftInset, bottom: 16, right: trackerCollectionView.params.rightInset) } - func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize { + func collectionView(_ collectionView: UICollectionView, + layout collectionViewLayout: UICollectionViewLayout, + referenceSizeForHeaderInSection section: Int) -> CGSize + { guard let viewModel = viewModel as? TrackerViewModel, - let visibleCategories = handler.resultTypeHandlerGetValue(viewModel.visibleCategory) + let visibleCategories = handler.resultTypeHandlerGetValue(viewModel.visibleCategory, + vc: self) else { return CGSize() } let indexPath = IndexPath(row: visibleCategories.count, section: section) - let headerView = self.collectionView(collectionView, viewForSupplementaryElementOfKind: UICollectionView.elementKindSectionHeader, at: indexPath) + let headerView = self.collectionView(collectionView, + viewForSupplementaryElementOfKind: UICollectionView.elementKindSectionHeader, + at: indexPath) - return headerView.systemLayoutSizeFitting(CGSize(width: collectionView.frame.width, height: UIView.layoutFittingExpandedSize.height), withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel) + return headerView.systemLayoutSizeFitting(CGSize(width: collectionView.frame.width, + height: UIView.layoutFittingExpandedSize.height), + withHorizontalFittingPriority: .required, + verticalFittingPriority: .fittingSizeLevel) } - func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { + func collectionView(_ collectionView: UICollectionView, + viewForSupplementaryElementOfKind kind: String, + at indexPath: IndexPath) -> UICollectionReusableView + { switch kind { case UICollectionView.elementKindSectionHeader: - guard let supplementary = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "\(HeaderReusableView.self)", for: indexPath) as? HeaderReusableView, + guard let supplementary = collectionView.dequeueReusableSupplementaryView(ofKind: kind, + withReuseIdentifier: "\(HeaderReusableView.self)", + for: indexPath) as? HeaderReusableView, let viewModel = viewModel as? TrackerViewModel, - let visibleCategories = handler.resultTypeHandlerGetValue(viewModel.visibleCategory) + let visibleCategories = handler.resultTypeHandlerGetValue(viewModel.visibleCategory, vc: self) else { return UICollectionReusableView() } let nameCategory = visibleCategories[indexPath.section].nameCategory if nameCategory == Fixed.fixedRu.rawValue || nameCategory == Fixed.fixedEng.rawValue { @@ -627,12 +729,14 @@ extension TrackersViewController: UICollectionViewDelegateFlowLayout { //MARK: - UISearchResultsUpdating extension TrackersViewController: UISearchResultsUpdating { - func updateSearchResults(for searchController: UISearchController) { + func updateSearchResults(for searchController: UISearchController) + { guard let word = searchController.searchBar.text, let viewModel else { return } if !word.isEmpty { - guard let seachCategory = handler.resultTypeHandlerGetValue(viewModel.filterListTrackersName(word: word)) + guard let seachCategory = handler.resultTypeHandlerGetValue(viewModel.filterListTrackersName(word: word), + vc: self) else { return } viewModel.getShowListTrackerSearchForName(seachCategory) return @@ -653,46 +757,63 @@ extension TrackersViewController: UISearchResultsUpdating { } //MARK: - TrackersCollectionViewCellDelegate -extension TrackersViewController: TrackersCollectionViewCellDelegate { +extension TrackersViewController: TrackersCollectionViewCellDelegate +{ func didTrackerCompleted(_ cell: UICollectionViewCell) { guard let trackerCell = cell as? TrackersCollectionViewCell, let indexPath = trackerCollectionView.indexPath(for: trackerCell), let viewModel = viewModel as? TrackerViewModel, - let visibleCategories = handler.resultTypeHandlerGetValue(viewModel.visibleCategory) + let visibleCategories = handler.resultTypeHandlerGetValue(viewModel.visibleCategory, + vc: self) else { return } let tracker = visibleCategories[indexPath.section].arrayTrackers[indexPath.row] viewModel.setIndexPath(indexPath) - handler.resultTypeHandler(viewModel.updateCompletedTrackers(tracker: tracker, date: datePicker.date)) {} + handler.resultTypeHandler(viewModel.updateCompletedTrackers(tracker: tracker, + date: datePicker.date), + vc: self) {} AnalyticsService.shared.track() } } //MARK: - EventSelectionViewControllerDelegate extension TrackersViewController: EventSelectionViewControllerDelegate { - func eventSelectionViewController(vc: UIViewController, nameCategories: String, tracker: Tracker) { + func eventSelectionViewController(vc: UIViewController, + nameCategories: String, + tracker: Tracker) + { guard let viewModel else { return } - handler.resultTypeHandler(viewModel.addNewTracker(tracker, nameCategory: nameCategories)) {} + handler.resultTypeHandler(viewModel.addNewTracker(tracker, + nameCategory: nameCategories), + vc: self) {} } - func eventSelectionViewControllerDidCancel(_ vc: EventSelectionViewController) { + func eventSelectionViewControllerDidCancel(_ vc: EventSelectionViewController) + { vc.dismiss(animated: false) } } //MARK: - UpdateTrackerViewControllerDelegate -extension TrackersViewController: UpdateTrackerViewControllerDelegate { - func trackerViewController(vc: UIViewController, nameCategory: String, tracker: Tracker) { +extension TrackersViewController: UpdateTrackerViewControllerDelegate +{ + func trackerViewController(vc: UIViewController, + nameCategory: String, + tracker: Tracker) { guard let viewModel else { return } - handler.resultTypeHandler(viewModel.updateTracker(tracker: tracker, nameCategory: nameCategory)) {} + handler.resultTypeHandler(viewModel.updateTracker(tracker: tracker, + nameCategory: nameCategory), + vc: self) {} } - func trackerViewControllerDidCancel(_ vc: CreateTrackerViewController) { + func trackerViewControllerDidCancel(_ vc: CreateTrackerViewController) + { vc.dismiss(animated: true) } } //MARK: - FiltersViewControllerDelegate -extension TrackersViewController: FiltersViewControllerDelegate { +extension TrackersViewController: FiltersViewControllerDelegate +{ func filter(vc: FiltersViewController, filterState: FiltersState) { guard let viewModel else { return } viewModel.setFilterState(state: filterState) diff --git a/Tracker/ViewModel/CategoryViewModel.swift b/Tracker/ViewModel/CategoryViewModel.swift index 2dd67cd..5404ea7 100644 --- a/Tracker/ViewModel/CategoryViewModel.swift +++ b/Tracker/ViewModel/CategoryViewModel.swift @@ -19,12 +19,16 @@ protocol CategoryViewModelProtocol { func deleteTrackers(trackers: [Tracker]) -> Result func deleteTrackerRecords(trackers: [Tracker]) -> Result func selectСategory(at index: Int) -> Result + func deleteSelectCategory() } //MARK: - ViewModel final class CategoryViewModel { - @Observable> private(set) var category: Result<[TrackerCategory], Error> - @UserDefaultsBacked(key: UserDefaultKeys.selectNameCategory.rawValue) private(set) var selectNameCategory: String? + @Observable> + private(set) var category: Result<[TrackerCategory], Error> + + @UserDefaultsBacked(key: UserDefaultKeys.selectNameCategory.rawValue) + private(set) var selectNameCategory: String? private let trackerCategoryStore: TrackerCategoryStoreProtocol private let trackerStore: TrackerStoreProtocol @@ -38,7 +42,7 @@ final class CategoryViewModel { trackerStore: trackerStore, trackerRecordStore: trackerRecordStore, category: .success([]) - ) + ) trackerCategoryStore.delegate = self trackerStore.delegate = self category = getCategory() @@ -47,8 +51,7 @@ final class CategoryViewModel { init(trackerCategoryStore: TrackerCategoryStoreProtocol, trackerStore: TrackerStoreProtocol, trackerRecordStore: TrackerRecordStoreProtocol, - category: Result<[TrackerCategory], Error> -) + category: Result<[TrackerCategory], Error>) { self.trackerCategoryStore = trackerCategoryStore self.trackerStore = trackerStore @@ -58,11 +61,13 @@ final class CategoryViewModel { } //MARK: - Extension private extension CategoryViewModel { - func category(from trackerCategoryCoreData: TrackerCategoryCoreData) throws -> TrackerCategory { + func category(from trackerCategoryCoreData: TrackerCategoryCoreData) throws -> TrackerCategory + { guard let nameCategory = trackerCategoryCoreData.nameCategory else { - throw StoreErrors.TrackrerCategoryStoreError.decodingErrorInvalidNameCategori + throw StoreErrors.TrackrerCategoryStoreError.decodingErrorInvalidNameCategory } - guard let arrayTrackersCoreData = trackerCategoryCoreData.trakers?.allObjects as? [TrackerCoreData] + guard let arrayTrackersCoreData = trackerCategoryCoreData.trakers?.allObjects + as? [TrackerCoreData] else { throw StoreErrors.NSSetError.transformationErrorInvalid } let result = trackerStore.getTrackers(arrayTrackersCoreData) switch result { @@ -82,12 +87,15 @@ extension CategoryViewModel: CategoryViewModelProtocol { trackerCategoryStore.addCategory(nameCategory) } - func updateNameCategory(newNameCategory: String, oldNameCaetegory: String) -> Result { - trackerCategoryStore.updateNameCategory(newNameCategory: newNameCategory, oldNameCategory: oldNameCaetegory) + func updateNameCategory(newNameCategory: String, + oldNameCaetegory: String) -> Result { + trackerCategoryStore.updateNameCategory(newNameCategory: newNameCategory, + oldNameCategory: oldNameCaetegory) } - func isCategorySelected(at index: Int) -> Result { - switch category { + func isCategorySelected(at index: Int) -> Result + { + switch сategoryExcludingFixed() { case .success(let category): return .success(category[index].nameCategory != selectNameCategory) case .failure(let error): @@ -95,18 +103,21 @@ extension CategoryViewModel: CategoryViewModelProtocol { } } - func getCategory() -> Result<[TrackerCategory], Error> { + func getCategory() -> Result<[TrackerCategory], Error> + { let trackerCategoryCoreData = trackerCategoryStore.getListTrackerCategoryCoreData() - guard let trackerCategoryCoreData else { return .failure(StoreErrors.TrackrerStoreError.getTrackerError) } + guard let trackerCategoryCoreData else + { return .failure(StoreErrors.TrackrerStoreError.getTrackerError) } do { - let listCategory = try trackerCategoryCoreData.map ({ try category(from: $0) }) + let listCategory = try trackerCategoryCoreData.map { try category(from: $0) } return .success(listCategory) } catch { return .failure(error) } } - - func сategoryExcludingFixed() -> Result<[TrackerCategory], Error> { + + func сategoryExcludingFixed() -> Result<[TrackerCategory], Error> + { switch category { case .success(let category): let filterCat = category.filter { @@ -119,7 +130,8 @@ extension CategoryViewModel: CategoryViewModelProtocol { } } - func createNameCategory(at index: Int) -> Result { + func createNameCategory(at index: Int) -> Result + { switch сategoryExcludingFixed() { case .success(let category): let name = category[index].nameCategory @@ -141,15 +153,21 @@ extension CategoryViewModel: CategoryViewModelProtocol { trackerRecordStore.deleteTrackerRecords(trackers: trackers) } - func selectСategory(at index: Int) -> Result { - switch category { + func selectСategory(at index: Int) -> Result + { + switch сategoryExcludingFixed() { case .success(let category): + print(category[index].nameCategory) selectNameCategory = category[index].nameCategory return .success(()) case .failure(let error): return .failure(error) } } + + func deleteSelectCategory() { + selectNameCategory = nil + } } //MARK: - TrackerCategoryStoreDelegate diff --git a/Tracker/ViewModel/EditTrackerViewModel.swift b/Tracker/ViewModel/EditTrackerViewModel.swift index 7f8c3d8..25f58eb 100644 --- a/Tracker/ViewModel/EditTrackerViewModel.swift +++ b/Tracker/ViewModel/EditTrackerViewModel.swift @@ -35,13 +35,13 @@ final class EditTrackerViewModel { var listSettings: [ChoiceParametrs] { isSchedul ? [.category] : [.category, .schedule] } let dataSection: [Header] = [.emoji, .color] let emojies: [String] = ["🙂", "😻", "🌺", "🐶", "❤️", "😱", "😇", "😡", "🥶", - "🤔", "🙌", "🍔", "🥦", "🏓", "🥇", "🎸", "🏝️", "😪"] + "🤔", "🙌", "🍔", "🥦", "🏓", "🥇", "🎸", "🏝️", "😪"] let colors: [UIColor] = [.colorSelection1, .colorSelection2, .colorSelection3,.colorSelection4, - .colorSelection5, .colorSelection6, .colorSelection7, .colorSelection8, - .colorSelection9, .colorSelection10, .colorSelection11, .colorSelection12, - .colorSelection13, .colorSelection14, .colorSelection15, .colorSelection16, - .colorSelection17, .colorSelection18] + .colorSelection5, .colorSelection6, .colorSelection7, .colorSelection8, + .colorSelection9, .colorSelection10, .colorSelection11, .colorSelection12, + .colorSelection13, .colorSelection14, .colorSelection15, .colorSelection16, + .colorSelection17, .colorSelection18] let regular: [WeekDay] = [.monday, .tuesday, .wednesday, .thursday, .friday, .saturday, .sunday] @@ -54,19 +54,22 @@ final class EditTrackerViewModel { private let marshalling: UIColorMarshalling - convenience init() { + convenience init() + { let marshalling = UIColorMarshalling() self.init(marshalling: marshalling) } - init(marshalling: UIColorMarshalling) { + init(marshalling: UIColorMarshalling) + { self.marshalling = marshalling } } //MARK: - EditTrackerViewModel extension EditTrackerViewModel: EditTrackerModelProtocol { - func checkingForEmptiness() -> Bool { + func checkingForEmptiness() -> Bool + { var flag: Bool if isSchedul { flag = !nameTracker.isEmpty && @@ -84,11 +87,14 @@ extension EditTrackerViewModel: EditTrackerModelProtocol { return flag } - func reverseIsSchedul() { + func reverseIsSchedul() + { isSchedul = isSchedul ? !isSchedul : isSchedul } - func jonedSchedule(schedule: [WeekDay], stringArrayDay: String) -> String { + func jonedSchedule(schedule: [WeekDay], + stringArrayDay: String) -> String + { var stringListDay: String if schedule.count == regular.count { stringListDay = stringArrayDay @@ -100,11 +106,13 @@ extension EditTrackerViewModel: EditTrackerModelProtocol { return stringListDay } - func setListWeekDay(listWeekDay: [WeekDay]) { + func setListWeekDay(listWeekDay: [WeekDay]) + { schedule = listWeekDay } - func getColorRow(color: UIColor) -> Int { + func getColorRow(color: UIColor) -> Int + { var colorRow: Int = 0 for (index, value) in colors.enumerated() { let colorMarshalling = marshalling.color(from: marshalling.hexString(from: value)) @@ -115,7 +123,8 @@ extension EditTrackerViewModel: EditTrackerModelProtocol { return colorRow } - func getEmojiRow(emoji: String) -> Int { + func getEmojiRow(emoji: String) -> Int + { var emojiRow: Int = 0 for (index, value) in emojies.enumerated() { if value == emoji { @@ -125,27 +134,38 @@ extension EditTrackerViewModel: EditTrackerModelProtocol { return emojiRow } - func setSchedule(_ vc: CreateTrackerViewController, schedule: [WeekDay]) { + func setSchedule(_ vc: CreateTrackerViewController, schedule: [WeekDay]) + { self.schedule = schedule } - func setColor(_ vc: CreateTrackerViewController, color: UIColor) { + func setColor(_ vc: CreateTrackerViewController, color: UIColor) + { self.color = color } - func setNameNewCategory(_ vc: CreateTrackerViewController, nameCategory: String) { + func setNameNewCategory(_ vc: CreateTrackerViewController, + nameCategory: String) + { self.nameNewCategory = nameCategory } - func setNameTracker(_ vc: CreateTrackerViewController, nameTracker: String) { + func setNameTracker(_ vc: CreateTrackerViewController, + nameTracker: String) + { self.nameTracker = nameTracker } - func setEmojiTracker(_ vc: CreateTrackerViewController, emoji: String) { + func setEmojiTracker(_ vc: CreateTrackerViewController, + emoji: String) + { self.emoji = emoji } - func editTracker(vc: CreateTrackerViewController, editTracker: Tracker, nameCategory: String) { + func editTracker(vc: CreateTrackerViewController, + editTracker: Tracker, + nameCategory: String) + { schedule = editTracker.schedule color = editTracker.color nameNewCategory = nameCategory diff --git a/Tracker/ViewModel/FiltersViewModel.swift b/Tracker/ViewModel/FiltersViewModel.swift index db94cc6..906dd1a 100644 --- a/Tracker/ViewModel/FiltersViewModel.swift +++ b/Tracker/ViewModel/FiltersViewModel.swift @@ -16,7 +16,9 @@ protocol FiltersViewModelProtocol { final class FiltersViewModel { let filtersState: [FiltersState] = [.allTrackers, .toDayTrackers, .completed, .notCompleted] - @UserDefaultsBacked(key: UserDefaultKeys.selectFilter.rawValue) private var selectFilter: String? + + @UserDefaultsBacked(key: UserDefaultKeys.selectFilter.rawValue) + private var selectFilter: String? } extension FiltersViewModel: FiltersViewModelProtocol { diff --git a/Tracker/ViewModel/ShedulViewModel.swift b/Tracker/ViewModel/ShedulViewModel.swift index 590988b..3856f7e 100644 --- a/Tracker/ViewModel/ShedulViewModel.swift +++ b/Tracker/ViewModel/ShedulViewModel.swift @@ -19,7 +19,8 @@ final class SheduleViewModel { } extension SheduleViewModel: SheduleViewModelProtocol { - func setListWeekDay(listWeekDay: [WeekDay]) { + func setListWeekDay(listWeekDay: [WeekDay]) + { self.listWeekDay = listWeekDay } } diff --git a/Tracker/ViewModel/StatisticViewModel.swift b/Tracker/ViewModel/StatisticViewModel.swift index cc8aa65..be25198 100644 --- a/Tracker/ViewModel/StatisticViewModel.swift +++ b/Tracker/ViewModel/StatisticViewModel.swift @@ -14,11 +14,14 @@ protocol StatisticsViewModelProtocol { final class StatisticsViewModel { @Observable private(set) var countTrackerComplet: Int? - @UserDefaultsBacked(key: UserDefaultKeys.isTracker.rawValue) private(set) var isTracker: Bool? + + @UserDefaultsBacked(key: UserDefaultKeys.isTracker.rawValue) + private(set) var isTracker: Bool? let trackerRecordStore: TrackerRecordStoreProtocol - convenience init() { + convenience init() + { let trackerRecordStore = TrackerRecordStore() self.init(trackerRecordStore: trackerRecordStore) countTrackerComplet = getCountTrackerComplet() @@ -41,7 +44,7 @@ extension StatisticsViewModel: StatisticsViewModelProtocol { } extension StatisticsViewModel: TrackerRecordStoreDelegate { - func trackerRecordStore(trackerRecordStore: TrackerRecordStoreProtocol) { + func trackerRecordStore(trackerRecordStore: TrackerRecordStoreProtocol, flag: Bool) { countTrackerComplet = getCountTrackerComplet() } } diff --git a/Tracker/ViewModel/TrackerViewModel.swift b/Tracker/ViewModel/TrackerViewModel.swift index 9e2188f..fd98a61 100644 --- a/Tracker/ViewModel/TrackerViewModel.swift +++ b/Tracker/ViewModel/TrackerViewModel.swift @@ -19,7 +19,7 @@ protocol TrackerViewModelProtocol { func addNewTracker(_ tracker: Tracker, nameCategory: String) -> Result func updateTracker(tracker: Tracker, nameCategory: String) -> Result - func deleteTracker(_ id: UUID) -> Result + func deleteTracker(id: UUID) -> Result func getShowListTrackersForDay(date: Date) func getShowListTrackerSearchForName(_ searchCategory: [TrackerCategory]) @@ -35,21 +35,31 @@ protocol TrackerViewModelProtocol { func setFilterState(state: FiltersState) func getFilterState() -> FiltersState - func getIsCategoryForDay() -> Bool + func getIsCategoryForDay() -> Bool? } //MARK: - TrackerViewModel final class TrackerViewModel { private let textFixed = NSLocalizedString("textFixed", comment: "") - @Observable> private(set) var category: Result<[TrackerCategory], Error> - @Observable> private(set) var visibleCategory: Result<[TrackerCategory], Error> - @Observable<[TrackerCategory]> private(set) var filterListTrackersWeekDay: [TrackerCategory] + @Observable> + private(set) var category: Result<[TrackerCategory], Error> + + @Observable> + private(set) var visibleCategory: Result<[TrackerCategory], Error> + + @Observable private(set) var isfilterListTrackersWeekDay: Bool? @Observable private(set) var indexPath: IndexPath @Observableprivate(set) var filterState: FiltersState - @UserDefaultsBacked(key: UserDefaultKeys.selectNameCategory.rawValue) private var selectNameCategory: String? - @UserDefaultsBacked(key: UserDefaultKeys.selectFilter.rawValue) private var selectFilter: String? - @UserDefaultsBacked(key: UserDefaultKeys.isTracker.rawValue) private(set) var isTracker: Bool? + + @UserDefaultsBacked(key: UserDefaultKeys.selectNameCategory.rawValue) + private var selectNameCategory: String? + + @UserDefaultsBacked(key: UserDefaultKeys.selectFilter.rawValue) + private var selectFilter: String? + + @UserDefaultsBacked(key: UserDefaultKeys.isTracker.rawValue) + private(set) var isTracker: Bool? private(set) var completedTrackers: Result, Error> @@ -58,7 +68,8 @@ final class TrackerViewModel { private let trackerStore: TrackerStoreProtocol private let pinnedCategoryStore: PinnedCategoryStoreProtocol - convenience init() { + convenience init() + { let trackerCategoryStore = TrackerCategoryStore() let trackerRecordStore = TrackerRecordStore() let trackerStore = TrackerStore() @@ -70,7 +81,6 @@ final class TrackerViewModel { category: .success([]), completedTrackers: .success(Set()), visibleCategory: .success([]), - filterListTrackersWeekDay: [], indexPath: IndexPath(), filterState: .allTrackers) trackerCategoryStore.delegate = self @@ -92,7 +102,6 @@ final class TrackerViewModel { category: Result<[TrackerCategory], Error>, completedTrackers: Result, Error>, visibleCategory: Result<[TrackerCategory], Error>, - filterListTrackersWeekDay: [TrackerCategory], indexPath: IndexPath, filterState: FiltersState) { self.trackerCategoryStore = trackerCategoryStore @@ -102,18 +111,19 @@ final class TrackerViewModel { self.category = category self.completedTrackers = completedTrackers self.visibleCategory = visibleCategory - self.filterListTrackersWeekDay = filterListTrackersWeekDay self.indexPath = indexPath self.filterState = filterState } } //MARK: - Extension private extension TrackerViewModel { - func category(from trackerCategoryCoreData: TrackerCategoryCoreData) throws -> TrackerCategory { + func category(from trackerCategoryCoreData: TrackerCategoryCoreData) throws -> TrackerCategory + { guard let nameCategory = trackerCategoryCoreData.nameCategory else { - throw StoreErrors.TrackrerCategoryStoreError.decodingErrorInvalidNameCategori + throw StoreErrors.TrackrerCategoryStoreError.decodingErrorInvalidNameCategory } - guard let arrayTrackersCoreData = trackerCategoryCoreData.trakers?.allObjects as? [TrackerCoreData] + guard let arrayTrackersCoreData = trackerCategoryCoreData.trakers?.allObjects + as? [TrackerCoreData] else { throw StoreErrors.NSSetError.transformationErrorInvalid } let result = trackerStore.getTrackers(arrayTrackersCoreData) switch result { @@ -126,11 +136,13 @@ private extension TrackerViewModel { } } - func setVisibleCategory(date: Date, flag: Bool) { + func setVisibleCategory(date: Date, flag: Bool) + { switch category { case .success(let category): var listCategories: [TrackerCategory] = [] - let filterCategory = filterListTrackersWeekDay(trackerCategory: category, date: date) + let filterCategory = filterListTrackersWeekDay(trackerCategory: category, + date: date) filterCategory.forEach { do { let cat = try trackerCompletedOrNotByCategory(category: $0, @@ -150,52 +162,44 @@ private extension TrackerViewModel { } } - func comparisonWeekDays(date: Date, day: WeekDay) -> Bool { + func comparisonWeekDays(date: Date, day: WeekDay) -> Bool + { FormatDate.shared.greateWeekDayInt(date: date) == day.rawValue } - func filterListTrackersWeekDay(trackerCategory: [TrackerCategory], date: Date) -> [TrackerCategory] { - var listCategories: [TrackerCategory] = [] - for cat in .zero.. [TrackerCategory] + { + let listCategories = trackerCategory.filter { + !$0.arrayTrackers.filter { + !$0.schedule.filter { + comparisonWeekDays(date: date, day: $0) }.isEmpty }.isEmpty } - filterListTrackersWeekDay = listCategories + isfilterListTrackersWeekDay = !listCategories.isEmpty return listCategories } - func filterListTrackersCompleted(date: Date, flag: Bool) { + func filterListTrackersCompleted(date: Date, flag: Bool) + { setVisibleCategory(date: date, flag: flag) } - func filterListTrackersNotCompleted(date: Date, flag: Bool) { + func filterListTrackersNotCompleted(date: Date, flag: Bool) + { setVisibleCategory(date: date, flag: flag) } - func deleteOrCreateTrackerRecord(id: UUID, date: Date) -> Result { - return trackerRecordStore.deleteOrCreateTrackerRecord(id: id, date: date.ignoringTime) + func deleteOrCreateTrackerRecord(id: UUID, date: Date) -> Result + { + return trackerRecordStore.deleteOrCreateTrackerRecord(id: id, + date: date.ignoringTime) } func trackerCompletedOrNotByCategory(category: TrackerCategory, date: Date, flag: Bool, - filter: (UUID, Date) throws -> Bool) throws -> TrackerCategory { + filter: (UUID, Date) throws -> Bool) throws -> TrackerCategory + { let trackers = category.arrayTrackers do { let trackers = try trackers.filter { try flag == filter($0.id, date) } @@ -210,22 +214,27 @@ private extension TrackerViewModel { } //MARK: - TrackerViewModelProtocol -extension TrackerViewModel: TrackerViewModelProtocol { - func deleteTracker(_ id: UUID) -> Result { +extension TrackerViewModel: TrackerViewModelProtocol +{ + func deleteTracker(id: UUID) -> Result { trackerStore.deleteTracker(id) } - func deleteTrackersRecord(id: UUID) -> Result { + func deleteTrackersRecord(id: UUID) -> Result + { trackerRecordStore.deleteTrackerRecord(id: id) } - func addCategory(nameCategory: String) -> Result { + func addCategory(nameCategory: String) -> Result + { trackerCategoryStore.addCategory(nameCategory) } - func getCategory() -> Result<[TrackerCategory], Error> { + func getCategory() -> Result<[TrackerCategory], Error> + { let trackerCategoryCoreData = trackerCategoryStore.getListTrackerCategoryCoreData() - guard let trackerCategoryCoreData else { return .failure(StoreErrors.TrackrerStoreError.getTrackerError) } + guard let trackerCategoryCoreData + else { return .failure(StoreErrors.TrackrerStoreError.getTrackerError) } do { let listCategory = try trackerCategoryCoreData.map ({ try category(from: $0) }) return .success(listCategory) @@ -234,34 +243,44 @@ extension TrackerViewModel: TrackerViewModelProtocol { } } - func treckersRecordsResult() -> Result, Error> { + func treckersRecordsResult() -> Result, Error> + { trackerRecordStore.treckersRecordsResult() } - func loadTrackerRecord(id: UUID) -> Result { + func loadTrackerRecord(id: UUID) -> Result + { trackerRecordStore.getCountTrackerRecord(id: id) } - func addNewTracker(_ tracker: Tracker, nameCategory: String) -> Result { - let trackerCoreData = trackerStore.addNewTracker(tracker, nameCategory: nameCategory) + func addNewTracker(_ tracker: Tracker, + nameCategory: String) -> Result + { + let trackerCoreData = trackerStore.addNewTracker(tracker, + nameCategory: nameCategory) return trackerCoreData } - func updateTracker(tracker: Tracker, nameCategory: String) -> Result { - trackerStore.updateTracker(tracker: tracker, nameCategory: nameCategory) + func updateTracker(tracker: Tracker, nameCategory: String) -> Result + { + trackerStore.updateTracker(tracker: tracker, + nameCategory: nameCategory) } - func getShowListTrackersForDay(date: Date) { + func getShowListTrackersForDay(date: Date) + { switch category { case .success(let trackerCategory): - let filterList = filterListTrackersWeekDay(trackerCategory: trackerCategory, date: date) + let filterList = filterListTrackersWeekDay(trackerCategory: trackerCategory, + date: date) visibleCategory = .success(filterList) case .failure(let error): visibleCategory = .failure(error) } } - func filterListTrackersName(word: String) -> Result<[TrackerCategory], Error> { + func filterListTrackersName(word: String) -> Result<[TrackerCategory], Error> + { var listCategories: [TrackerCategory] = [] switch category { case .success(let cat): @@ -279,92 +298,115 @@ extension TrackerViewModel: TrackerViewModelProtocol { } } - func updateCompletedTrackers(tracker: Tracker, date: Date) -> Result { + func updateCompletedTrackers(tracker: Tracker, date: Date) -> Result + { return deleteOrCreateTrackerRecord(id: tracker.id, date: date) } - func getShowListTrackerSearchForName(_ searchCategory: [TrackerCategory]) { + func getShowListTrackerSearchForName(_ searchCategory: [TrackerCategory]) + { visibleCategory = .success(searchCategory) } - func getIsComplited(tracker: Tracker, date: Date) -> Result{ + func getIsComplited(tracker: Tracker, date: Date) -> Result + { switch completedTrackers { case .success(let compTrack): return .success(compTrack.contains (where: { $0.id == tracker.id && - Calendar.current.isDate(date, equalTo: $0.date, toGranularity: .day)})) + Calendar.current.isDate(date, + equalTo: $0.date, + toGranularity: .day)})) case .failure(let error): return .failure(error) } } - func setIndexPath(_ indexPath: IndexPath) { + func setIndexPath(_ indexPath: IndexPath) + { self.indexPath = indexPath } - func addPinnedCategory(id: UUID, nameCategory: String) -> Result { + func addPinnedCategory(id: UUID, nameCategory: String) -> Result + { let pinnCategoryCoreData = pinnedCategoryStore.addPinnedCategory(nameCategory) switch pinnCategoryCoreData { case .success(let pinnCategoryCoreData): - return trackerStore.addPinnedCategory(id, pinnedCategory: pinnCategoryCoreData) + return trackerStore.addPinnedCategory(id, + pinnedCategory: pinnCategoryCoreData) case .failure(let error): return .failure(error) } } - func deleteAndGetPinnedCategory(id: UUID) -> Result { + func deleteAndGetPinnedCategory(id: UUID) -> Result + { pinnedCategoryStore.deleteAndGetPinnedCategory(id) } - func allTrackersByDate(date: Date) { + func allTrackersByDate(date: Date) + { getShowListTrackersForDay(date: date) } - func getNotCompleted(date: Date, flag: Bool) { + func getNotCompleted(date: Date, flag: Bool) + { filterListTrackersNotCompleted(date: date, flag: flag) } - func getCompleted(date: Date, flag: Bool) { + func getCompleted(date: Date, flag: Bool) + { filterListTrackersCompleted(date: date, flag: flag) } - func setFilterState(state: FiltersState) { + func setFilterState(state: FiltersState) + { filterState = state } - func getFilterState() -> FiltersState { + func getFilterState() -> FiltersState + { filterState } - func getSelectFilter() -> String? { + func getSelectFilter() -> String? + { selectFilter } - func getCountTrackerCompleted(id: UUID) -> Result { + func getCountTrackerCompleted(id: UUID) -> Result + { trackerRecordStore.getCountTrackerRecord(id: id) } - func getIsCategoryForDay() -> Bool { - filterListTrackersWeekDay.isEmpty + func getIsCategoryForDay() -> Bool? + { + isfilterListTrackersWeekDay } } //MARK: - TrackerCategoryStoreDelegate extension TrackerViewModel: TrackerCategoryStoreDelegate { - func storeCategory(trackerCategoryStore: TrackerCategoryStoreProtocol) { + func storeCategory(trackerCategoryStore: TrackerCategoryStoreProtocol) + { category = getCategory() } } //MARK: - TrackerRecordStoreDelegate extension TrackerViewModel: TrackerRecordStoreDelegate { - func trackerRecordStore(trackerRecordStore: TrackerRecordStoreProtocol) { + func trackerRecordStore(trackerRecordStore: TrackerRecordStoreProtocol, + flag: Bool) + { completedTrackers = trackerRecordStore.treckersRecordsResult() - self.indexPath = indexPath + if flag { + self.indexPath = indexPath + } } } //MARK: - TrackerStoreDelegate -extension TrackerViewModel: TrackerStoreDelegate { +extension TrackerViewModel: TrackerStoreDelegate +{ func updateTracker(_ trackerCoreData: TrackerStoreProtocol) { category = getCategory() } diff --git a/Tracker/TabBarController/TrackersViews/Cell/ColorCollectionViewCell.swift b/Tracker/Views/CollectionViewCell/ColorCollectionViewCell.swift similarity index 90% rename from Tracker/TabBarController/TrackersViews/Cell/ColorCollectionViewCell.swift rename to Tracker/Views/CollectionViewCell/ColorCollectionViewCell.swift index 1027184..62fa638 100644 --- a/Tracker/TabBarController/TrackersViews/Cell/ColorCollectionViewCell.swift +++ b/Tracker/Views/CollectionViewCell/ColorCollectionViewCell.swift @@ -14,7 +14,7 @@ final class ColorCollectionViewCell: UICollectionViewCell { static let borderWidth = CGFloat(3) static let alfaComponent = CGFloat(0.3) } - + private var contentColorView: UIView = { let contentColorView = UIView() contentColorView.layer.masksToBounds = true @@ -31,13 +31,15 @@ final class ColorCollectionViewCell: UICollectionViewCell { return colorView }() - - override init(frame: CGRect) { + + override init(frame: CGRect) + { super.init(frame: frame) setupColorView() } - required init?(coder: NSCoder) { + required init?(coder: NSCoder) + { super.init(coder: coder) assertionFailure("init(coder:) has not been implemented") } @@ -45,11 +47,13 @@ final class ColorCollectionViewCell: UICollectionViewCell { extension ColorCollectionViewCell { //MARK: - Config - func configColor(color: UIColor) { + func configColor(color: UIColor) + { colorView.backgroundColor = color } - func colorSelection(color: UIColor, flag: Bool) { + func colorSelection(color: UIColor, flag: Bool) + { contentColorView.layer.cornerRadius = flag ? ConstantsCell.cornerRadius : 0 contentColorView.layer.borderWidth = flag ? ConstantsCell.borderWidth : 0 contentColorView.layer.masksToBounds = flag @@ -57,7 +61,8 @@ extension ColorCollectionViewCell { } //MARK: - SetupUI - private func setupColorView() { + private func setupColorView() + { addSubview(contentColorView) contentColorView.addSubview(colorView) diff --git a/Tracker/TabBarController/TrackersViews/Cell/EmojiCollectionViewCell.swift b/Tracker/Views/CollectionViewCell/EmojiCollectionViewCell.swift similarity index 87% rename from Tracker/TabBarController/TrackersViews/Cell/EmojiCollectionViewCell.swift rename to Tracker/Views/CollectionViewCell/EmojiCollectionViewCell.swift index 1a2504d..5597036 100644 --- a/Tracker/TabBarController/TrackersViews/Cell/EmojiCollectionViewCell.swift +++ b/Tracker/Views/CollectionViewCell/EmojiCollectionViewCell.swift @@ -25,13 +25,15 @@ final class EmojiCollectionViewCell: UICollectionViewCell { return emojiView }() - - override init(frame: CGRect) { + + override init(frame: CGRect) + { super.init(frame: frame) setupColorView() } - required init?(coder: NSCoder) { + required init?(coder: NSCoder) + { super.init(coder: coder) assertionFailure("init(coder:) has not been implemented") } @@ -39,17 +41,20 @@ final class EmojiCollectionViewCell: UICollectionViewCell { extension EmojiCollectionViewCell { //MARK: - Config - func configEmoji(emoji: String) { + func configEmoji(emoji: String) + { emojiView.text = emoji } - + //MARK: - Selected - func emojiSelection(isBackground: Bool) { + func emojiSelection(isBackground: Bool) + { emojiView.backgroundColor = isBackground ? .grayDay : .clear } //MARK: - SetupUI - private func setupColorView() { + private func setupColorView() + { addSubview(emojiView) NSLayoutConstraint.activate([ diff --git a/Tracker/TabBarController/TrackersViews/Cell/TrackersCollectionViewCell.swift b/Tracker/Views/CollectionViewCell/TrackersCollectionViewCell.swift similarity index 85% rename from Tracker/TabBarController/TrackersViews/Cell/TrackersCollectionViewCell.swift rename to Tracker/Views/CollectionViewCell/TrackersCollectionViewCell.swift index ae5383c..c25af2c 100644 --- a/Tracker/TabBarController/TrackersViews/Cell/TrackersCollectionViewCell.swift +++ b/Tracker/Views/CollectionViewCell/TrackersCollectionViewCell.swift @@ -125,12 +125,14 @@ final class TrackersCollectionViewCell: UICollectionViewCell { return verticalStack }() - override init(frame: CGRect) { + override init(frame: CGRect) + { super.init(frame: frame) setupUIElement() } - required init?(coder: NSCoder) { + required init?(coder: NSCoder) + { super.init(coder: coder) } } @@ -138,7 +140,8 @@ final class TrackersCollectionViewCell: UICollectionViewCell { extension TrackersCollectionViewCell { //MARK: - Обработка событий @objc - private func didTapAddButton() { + private func didTapAddButton() + { guard let delegate else { return } delegate.didTrackerCompleted(self) } @@ -146,7 +149,8 @@ extension TrackersCollectionViewCell { //MARK: - Configuration //метод обновления счетчика выполнения трекера и imageButton - func updateLableCountAndImageAddButton(_ updateModel: UpdateTracker) { + func updateLableCountAndImageAddButton(_ updateModel: UpdateTracker) + { let textLable = String.localizedStringWithFormat(ConstantsTrackerCell.numberOfDays, updateModel.count) dayCounterLable.text = textLable @@ -160,42 +164,50 @@ extension TrackersCollectionViewCell { } } - func updateBackgraundAddButton(isHidden: Bool) { + func updateBackgraundAddButton(isHidden: Bool) + { backgroundAddButtonView.isHidden = isHidden } - func setPinned(flag: Bool) { + func setPinned(flag: Bool) + { isPinnedImageView.isHidden = flag } - func isEnableAddButton(flag: Bool) { + func isEnableAddButton(flag: Bool) + { addButton.isEnabled = flag } - private func setIsSelectedAddButton(flag: Bool) { + private func setIsSelectedAddButton(flag: Bool) + { isSelectedAddButton = flag } - func config(tracker: Tracker) { + func config(tracker: Tracker) + { colorView.backgroundColor = tracker.color emojiLable.text = tracker.emoji addButton.backgroundColor = tracker.color nameTrackerLabel.text = tracker.name } - func getView() -> UIView { + func getView() -> UIView + { colorView } //MARK: - SetupUI - private func setupUIElement() { + private func setupUIElement() + { setupColorView() setupVerticallStack() setupHorisontalStack() setupBackgroundAddButtonView() } - private func setupVerticallStack() { + private func setupVerticallStack() + { contentView.addSubview(verticalStack) [colorView, horisontalStack].forEach { $0.translatesAutoresizingMaskIntoConstraints = false @@ -210,24 +222,30 @@ extension TrackersCollectionViewCell { ]) } - private func setupHorisontalStack() { + private func setupHorisontalStack() + { [dayCounterLable, addButton].forEach { $0.translatesAutoresizingMaskIntoConstraints = false horisontalStack.addArrangedSubview($0) } NSLayoutConstraint.activate([ - dayCounterLable.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 12), - dayCounterLable.topAnchor.constraint(equalTo: colorView.bottomAnchor, constant: 16), + dayCounterLable.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, + constant: 12), + dayCounterLable.topAnchor.constraint(equalTo: colorView.bottomAnchor, + constant: 16), addButton.heightAnchor.constraint(equalToConstant: ConstantsTrackerCell.sizeAddButton.height), addButton.widthAnchor.constraint(equalToConstant: ConstantsTrackerCell.sizeAddButton.width), - addButton.topAnchor.constraint(equalTo: colorView.bottomAnchor, constant: 8), - addButton.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -12) + addButton.topAnchor.constraint(equalTo: colorView.bottomAnchor, + constant: 8), + addButton.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, + constant: -12) ]) } - private func setupBackgroundAddButtonView() { + private func setupBackgroundAddButtonView() + { addButton.addSubview(backgroundAddButtonView) NSLayoutConstraint.activate([ @@ -238,7 +256,8 @@ extension TrackersCollectionViewCell { ]) } - private func setupColorView() { + private func setupColorView() + { [emojiLable, nameTrackerLabel, isPinnedImageView].forEach { $0.translatesAutoresizingMaskIntoConstraints = false colorView.addSubview($0) @@ -247,17 +266,24 @@ extension TrackersCollectionViewCell { let indentation = CGFloat(12) NSLayoutConstraint.activate([ - emojiLable.topAnchor.constraint(equalTo: colorView.topAnchor, constant: indentation), - emojiLable.leadingAnchor.constraint(equalTo: colorView.leadingAnchor, constant: indentation), + emojiLable.topAnchor.constraint(equalTo: colorView.topAnchor, + constant: indentation), + emojiLable.leadingAnchor.constraint(equalTo: colorView.leadingAnchor, + constant: indentation), emojiLable.heightAnchor.constraint(equalToConstant: ConstantsTrackerCell.sizeLableView.height), emojiLable.widthAnchor.constraint(equalToConstant: ConstantsTrackerCell.sizeLableView.width), - nameTrackerLabel.leadingAnchor.constraint(equalTo: colorView.leadingAnchor, constant: indentation), - nameTrackerLabel.trailingAnchor.constraint(equalTo: colorView.trailingAnchor, constant: -indentation), - nameTrackerLabel.bottomAnchor.constraint(equalTo: colorView.bottomAnchor, constant: -indentation), + nameTrackerLabel.leadingAnchor.constraint(equalTo: colorView.leadingAnchor, + constant: indentation), + nameTrackerLabel.trailingAnchor.constraint(equalTo: colorView.trailingAnchor, + constant: -indentation), + nameTrackerLabel.bottomAnchor.constraint(equalTo: colorView.bottomAnchor, + constant: -indentation), - isPinnedImageView.topAnchor.constraint(equalTo: colorView.topAnchor, constant: indentation), - isPinnedImageView.trailingAnchor.constraint(equalTo: colorView.trailingAnchor, constant: -6), + isPinnedImageView.topAnchor.constraint(equalTo: colorView.topAnchor, + constant: indentation), + isPinnedImageView.trailingAnchor.constraint(equalTo: colorView.trailingAnchor, + constant: -6), isPinnedImageView.heightAnchor.constraint(equalToConstant: ConstantsTrackerCell.sizeIsPinnedImage.height), isPinnedImageView.widthAnchor.constraint(equalToConstant: ConstantsTrackerCell.sizeIsPinnedImage.width) ]) diff --git a/Tracker/TabBarController/TrackersViews/SuplementaryView/HeaderReusableView.swift b/Tracker/Views/CustomViews/HeaderReusableView.swift similarity index 85% rename from Tracker/TabBarController/TrackersViews/SuplementaryView/HeaderReusableView.swift rename to Tracker/Views/CustomViews/HeaderReusableView.swift index 391a986..aff515a 100644 --- a/Tracker/TabBarController/TrackersViews/SuplementaryView/HeaderReusableView.swift +++ b/Tracker/Views/CustomViews/HeaderReusableView.swift @@ -24,31 +24,36 @@ final class HeaderReusableView: UICollectionReusableView { return label }() - override init(frame: CGRect) { + override init(frame: CGRect) + { super.init(frame: frame) backgroundColor = colors.viewBackground setupLable() } - required init?(coder: NSCoder) { + required init?(coder: NSCoder) + { fatalError("init(coder:) has not been implemented") } } //MARK: - SetupUI extension HeaderReusableView { - private func setupLable() { + private func setupLable() + { addSubview(label) label.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ - label.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 12), + label.leadingAnchor.constraint(equalTo: leadingAnchor, + constant: 12), label.topAnchor.constraint(equalTo: topAnchor), label.bottomAnchor.constraint(equalTo: bottomAnchor) ]) } - func setTextLable(text: String) { + func setTextLable(text: String) + { label.text = text } } diff --git a/Tracker/TabBarController/TrackersViews/Cell/StatisticView.swift b/Tracker/Views/CustomViews/StatisticView.swift similarity index 76% rename from Tracker/TabBarController/TrackersViews/Cell/StatisticView.swift rename to Tracker/Views/CustomViews/StatisticView.swift index 9f0a312..2cb8dc8 100644 --- a/Tracker/TabBarController/TrackersViews/Cell/StatisticView.swift +++ b/Tracker/Views/CustomViews/StatisticView.swift @@ -31,19 +31,23 @@ final class StatisticsView: UIView { return secondaryTextLable }() - override init(frame: CGRect) { + override init(frame: CGRect) + + { super.init(frame: frame) setupView() backgroundColor = colors.viewBackground } - required init?(coder: NSCoder) { + required init?(coder: NSCoder) + { fatalError("init(coder:) has not been implemented") } } extension StatisticsView { - private func setupView() { + private func setupView() + { [lableHeader, secondaryTextLable].forEach { $0.backgroundColor = .clear $0.translatesAutoresizingMaskIntoConstraints = false @@ -53,18 +57,24 @@ extension StatisticsView { let indentation = CGFloat(12) NSLayoutConstraint.activate([ - lableHeader.topAnchor.constraint(equalTo: topAnchor, constant: indentation), - lableHeader.leadingAnchor.constraint(equalTo: leadingAnchor, constant: indentation), - secondaryTextLable.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -indentation), - secondaryTextLable.leadingAnchor.constraint(equalTo: leadingAnchor, constant: indentation) + lableHeader.topAnchor.constraint(equalTo: topAnchor, + constant: indentation), + lableHeader.leadingAnchor.constraint(equalTo: leadingAnchor, + constant: indentation), + secondaryTextLable.bottomAnchor.constraint(equalTo: bottomAnchor, + constant: -indentation), + secondaryTextLable.leadingAnchor.constraint(equalTo: leadingAnchor, + constant: indentation) ]) } - func setCount(count: Int) { + func setCount(count: Int) + { lableHeader.text = String(count) } - func setSecondaryTextLable(text: String) { + func setSecondaryTextLable(text: String) + { secondaryTextLable.text = text } } diff --git a/Tracker/TabBarController/TrackersViews/CollectionView/TrackerCollectionView.swift b/Tracker/Views/CustomViews/TrackerCollectionView.swift similarity index 71% rename from Tracker/TabBarController/TrackersViews/CollectionView/TrackerCollectionView.swift rename to Tracker/Views/CustomViews/TrackerCollectionView.swift index e571661..90ea716 100644 --- a/Tracker/TabBarController/TrackersViews/CollectionView/TrackerCollectionView.swift +++ b/Tracker/Views/CustomViews/TrackerCollectionView.swift @@ -11,12 +11,16 @@ import UIKit final class TrackerCollectionView: UICollectionView { var params: GeometricParams - init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout, params: GeometricParams) { + init(frame: CGRect, + collectionViewLayout layout: UICollectionViewLayout, + params: GeometricParams) + { self.params = params super.init(frame: frame, collectionViewLayout: layout) } - required init?(coder: NSCoder) { + required init?(coder: NSCoder) + { fatalError("init(coder:) has not been implemented") } } diff --git a/Tracker/TabBarController/TrackersViews/Cell/CreateTrackerTableViewCell.swift b/Tracker/Views/TableViewCell/CreateTrackerTableViewCell.swift similarity index 87% rename from Tracker/TabBarController/TrackersViews/Cell/CreateTrackerTableViewCell.swift rename to Tracker/Views/TableViewCell/CreateTrackerTableViewCell.swift index cd44119..d9caee4 100644 --- a/Tracker/TabBarController/TrackersViews/Cell/CreateTrackerTableViewCell.swift +++ b/Tracker/Views/TableViewCell/CreateTrackerTableViewCell.swift @@ -49,14 +49,17 @@ class CreateTrackerTableViewCell: UITableViewCell { return clickImage }() - override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) + + { super.init(style: style, reuseIdentifier: reuseIdentifier) selectionStyle = .none backgroundColor = colors.viewBackground setupUIElement() } - required init?(coder: NSCoder) { + required init?(coder: NSCoder) + { super.init(coder: coder) assertionFailure("init(coder:) has not been implemented") } @@ -65,12 +68,14 @@ class CreateTrackerTableViewCell: UITableViewCell { extension CreateTrackerTableViewCell { //MARK: - SetupUI - private func setupUIElement() { + private func setupUIElement() + { setupSelf() setupSteckView() } - private func setupSelf() { + private func setupSelf() + { backgroundColor = .backgroundNight layer.masksToBounds = true clickImage.translatesAutoresizingMaskIntoConstraints = false @@ -78,14 +83,16 @@ extension CreateTrackerTableViewCell { clickImage.backgroundColor = .clear NSLayoutConstraint.activate([ - clickImage.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -12), + clickImage.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, + constant: -12), clickImage.centerYAnchor.constraint(equalTo: contentView.centerYAnchor), clickImage.heightAnchor.constraint(equalToConstant: 24), clickImage.widthAnchor.constraint(equalToConstant: 24) ]) } - private func setupSteckView() { + private func setupSteckView() + { contentView.addSubview(lableStackView) [lableView, secondaryTextLable].forEach { $0.backgroundColor = .clear @@ -94,24 +101,28 @@ extension CreateTrackerTableViewCell { } NSLayoutConstraint.activate([ - lableStackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 12), + lableStackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, + constant: 12), lableStackView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor) ]) } //MARK: - Configuration - func configCell(choice: ChoiceParametrs) { + func configCell(choice: ChoiceParametrs) + { lableView.text = choice.name } - func configSecondaryLableShedule(secondaryText: String) { + func configSecondaryLableShedule(secondaryText: String) + { if !secondaryText.isEmpty { secondaryTextLable.text = secondaryText secondaryTextLable.isHidden = false } } - func setupCornerRadius(cornerRadius: CGFloat, maskedCorners: CACornerMask) { + func setupCornerRadius(cornerRadius: CGFloat, maskedCorners: CACornerMask) + { layer.cornerRadius = cornerRadius layer.maskedCorners = maskedCorners } diff --git a/Tracker/TabBarController/TrackersViews/Cell/CustomTableViewCell.swift b/Tracker/Views/TableViewCell/CustomTableViewCell.swift similarity index 85% rename from Tracker/TabBarController/TrackersViews/Cell/CustomTableViewCell.swift rename to Tracker/Views/TableViewCell/CustomTableViewCell.swift index eafa9b8..30af489 100644 --- a/Tracker/TabBarController/TrackersViews/Cell/CustomTableViewCell.swift +++ b/Tracker/Views/TableViewCell/CustomTableViewCell.swift @@ -34,7 +34,8 @@ final class CustomTableViewCell: UITableViewCell { return selectedImage }() - override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) + { super.init(style: style, reuseIdentifier: reuseIdentifier) selectionStyle = .none layer.masksToBounds = true @@ -43,11 +44,13 @@ final class CustomTableViewCell: UITableViewCell { setupSelectedImage() } - required init?(coder: NSCoder) { + required init?(coder: NSCoder) + { fatalError("init(coder:) has not been implemented") } - override func prepareForReuse() { + override func prepareForReuse() + { super.prepareForReuse() layer.cornerRadius = 0 layer.maskedCorners = [.layerMinXMinYCorner, @@ -59,40 +62,47 @@ final class CustomTableViewCell: UITableViewCell { extension CustomTableViewCell { //MARK: - Config - func config(model: CustomCellModel) { + func config(model: CustomCellModel) + { nameCategoriLableView.text = model.text backgroundColor = model.color } - func showSelectedImage(flag: Bool) { + func showSelectedImage(flag: Bool) + { selectedImage.isHidden = flag } - func setupCornerRadius(cornerRadius: CGFloat, maskedCorners: CACornerMask?) { + func setupCornerRadius(cornerRadius: CGFloat, maskedCorners: CACornerMask?) + { layer.cornerRadius = cornerRadius guard let maskedCorners else { return } layer.maskedCorners = maskedCorners } //MARK: - SetupUI - private func setupLable() { + private func setupLable() + { nameCategoriLableView.translatesAutoresizingMaskIntoConstraints = false nameCategoriLableView.backgroundColor = .clear contentView.addSubview(nameCategoriLableView) NSLayoutConstraint.activate([ nameCategoriLableView.centerYAnchor.constraint(equalTo: centerYAnchor), - nameCategoriLableView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 12) + nameCategoriLableView.leadingAnchor.constraint(equalTo: leadingAnchor, + constant: 12) ]) } - private func setupSelectedImage() { + private func setupSelectedImage() + { contentView.addSubview(selectedImage) selectedImage.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ selectedImage.centerYAnchor.constraint(equalTo: centerYAnchor), - selectedImage.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -12) + selectedImage.trailingAnchor.constraint(equalTo: trailingAnchor, + constant: -12) ]) } } diff --git a/Tracker/TabBarController/TrackersViews/Cell/WeekDayTableViewCell.swift b/Tracker/Views/TableViewCell/WeekDayTableViewCell.swift similarity index 74% rename from Tracker/TabBarController/TrackersViews/Cell/WeekDayTableViewCell.swift rename to Tracker/Views/TableViewCell/WeekDayTableViewCell.swift index 96fad35..952befd 100644 --- a/Tracker/TabBarController/TrackersViews/Cell/WeekDayTableViewCell.swift +++ b/Tracker/Views/TableViewCell/WeekDayTableViewCell.swift @@ -14,7 +14,8 @@ protocol WeekDayTableViewCellDelegate: AnyObject { //MARK: - final class WeekDayTableViewCell: UITableViewCell { - private static let dayLableFont = UIFont.systemFont(ofSize: 17, weight: .regular) + private static let dayLableFont = UIFont.systemFont(ofSize: 17, + weight: .regular) weak var delegate: WeekDayTableViewCellDelegate? @@ -28,12 +29,15 @@ final class WeekDayTableViewCell: UITableViewCell { private lazy var choiceDaySwitch: UISwitch = { let choiceDaySwitch = UISwitch() choiceDaySwitch.onTintColor = .blueDay - choiceDaySwitch.addTarget(self, action: #selector(switching), for: .touchUpInside) + choiceDaySwitch.addTarget(self, + action: #selector(switching), + for: .touchUpInside) return choiceDaySwitch }() - override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) + { super.init(style: style, reuseIdentifier: reuseIdentifier) backgroundColor = .backgroundDay layer.masksToBounds = true @@ -41,7 +45,8 @@ final class WeekDayTableViewCell: UITableViewCell { setupElement() } - required init?(coder: NSCoder) { + required init?(coder: NSCoder) + { fatalError("init(coder:) has not been implemented") } } @@ -49,13 +54,17 @@ final class WeekDayTableViewCell: UITableViewCell { extension WeekDayTableViewCell { //MARK: - Обработка событий @objc - private func switching(_ sender: UISwitch) { + private func switching(_ sender: UISwitch) + { guard let delegate else { return } - delegate.addDayInListkDay(cell: self, flag: choiceDaySwitch.isOn) + delegate.addDayInListkDay(cell: self, + flag: choiceDaySwitch.isOn) } //MARK: - Configuration - func setupCornerRadius(cornerRadius: CGFloat, maskedCorners: CACornerMask) { + func setupCornerRadius(cornerRadius: CGFloat, + maskedCorners: CACornerMask) + { layer.cornerRadius = cornerRadius layer.maskedCorners = maskedCorners } @@ -73,11 +82,13 @@ extension WeekDayTableViewCell { } NSLayoutConstraint.activate([ - dayLable.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 12), + dayLable.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, + constant: 12), dayLable.centerYAnchor.constraint(equalTo: contentView.centerYAnchor), - choiceDaySwitch.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -12), + choiceDaySwitch.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, + constant: -12), choiceDaySwitch.centerYAnchor.constraint(equalTo: contentView.centerYAnchor) ]) } diff --git a/TrackerTests/TrackerTests.swift b/TrackerTests/TrackerTests.swift index 2b64bef..a7e896d 100644 --- a/TrackerTests/TrackerTests.swift +++ b/TrackerTests/TrackerTests.swift @@ -10,9 +10,15 @@ import SnapshotTesting @testable import Tracker final class TrackerTests: XCTestCase { + let vc = TrackersViewController(viewModel: TrackerViewModel()) - func testViewController() { - let vc = TabBarController() - assertSnapshot(matching: vc, as: .image) + func testTrackersViewControllerDark() { + assertSnapshot(matching: vc, as: .image(traits: .init(userInterfaceStyle: .dark)), + record: false) + } + + func testTrackersViewControllerLight() { + assertSnapshot(matching: vc, as: .image(traits: .init(userInterfaceStyle: .light)), + record: false) } } diff --git a/TrackerTests/__Snapshots__/TrackerTests/testTrackersViewControllerDark.1.png b/TrackerTests/__Snapshots__/TrackerTests/testTrackersViewControllerDark.1.png new file mode 100644 index 0000000000000000000000000000000000000000..32d55d5cd056ef80bf61822b2d5c2640ffde17bd GIT binary patch literal 97098 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 literal 0 HcmV?d00001 diff --git a/TrackerTests/__Snapshots__/TrackerTests/testTrackersViewControllerLight.1.png b/TrackerTests/__Snapshots__/TrackerTests/testTrackersViewControllerLight.1.png new file mode 100644 index 0000000000000000000000000000000000000000..09e1a35f2f24122b36cc2500b0443eca10baf191 GIT binary patch literal 85463 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 literal 0 HcmV?d00001 diff --git a/TrackerTests/__Snapshots__/TrackerTests/testViewController.1.png b/TrackerTests/__Snapshots__/TrackerTests/testViewController.1.png index 55b9338152d0c3b0e8f2302e24c6935625bb476b..32d55d5cd056ef80bf61822b2d5c2640ffde17bd 100644 GIT binary patch literal 97098 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 literal 197401 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

mCAf#?SAJOb0rArlJwzgdgA7*E<#Jl89Djz-fY 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

NT*(|tlX>Q<7HQofEq9AH1wo1 zeQrERTvK!OwHxX6xO1mufhKU!tEp5JY2UyU!`uRwq=M#&jZ|gRY>zuj!SU z2hDvwt9tIaho|k^K1Q)_Sx113@HUC7Zx0mG_XL`YCJGe9?G>2Qg5o;7Nl%p356G!- z5^f`E63Bcx>T|&@-69XICk_~`F<-eZAQW9$(Y=DVbaS;55WAQG3pJ=ck`;&fACJhS zTN!y#+ZvzrcEjV#7l-0Ua{WSVnf9?EctLwjTB>rh`@7cGfj^MKAJ%B3-Bv3V%!r1X z1I^B+$22L+NBeiYB3Tx&Zp!-_4Wf@ZMj^_h*qIkO3a1}l*YB5E-fh3`&$8)xVf_#? z2}#-qPQ_l!A$K6;3V>rt3Ox6v*txE`w^U5=O0#U~iQ?cyApcdZ9CmI^Qbd*x$v(N| zE72~}8^~OlAkNZHA+-Z9?io1Db0(y(C=s&8%Z~YlIKND2Q=?Xmz!fEg3gRZbTll$n ze{+8L>wdX(h@~ap!A%FIY>zuPR?<>cK;3=npC-w;c%J=pbQ;Yg;_QXw9J8t@N^l`x z8Ed>NUXa*!W0)TESPu7<#=?!T!tD;nlc-O|muLLIEx9JV)p@MirP&B!;|OMZeD%W= z_sCDjOjdnK;veSUy}F2XSRle0lFi0&5W$!Zp}V z{V+x9*WaQVAM02;gt%7Bz1iE@Am*4n5+Z;84_e^W;9`hyh!Y=f3Z;7Ma~&6RxaMpF z?Df%KYH<1(Mdr>;rbBJu_5?B|(884uNP~vxnBa)mAZiVZ30pn{KeuhbWtS+bW~pap z_NaG)kHJNSvFnFRUiLv~+ojl0V)4RCen9DE$`=^?=FI3w5SaSvbXL1B;}_`G<1y#X zVYl@5xuZ`u$GLnJjvEzpFMZ(|=Wi7xMqX>d%}>MWGsM)?tf_I9JlFs1BF@%Y^O27R9%#ieV8iJ~eIf*Bc4eR{ zUp#SFOj=sE5!Wf&FbZ`}oy>iZHWcO?ql>roRvYJzp?yM0p6v^0Kgm;^eDfKY$(i>0 z0G0;x4FftoL|9w4D8SQ3@Q+Z)u!01y2IuRMY8{j)=H!x}-(HCw`{lq8VG}+sDYLZR zd{~g06pneuITFu%@>lhk{4un&+RbuidOR~QNY`pm04{W4BsjSnSn~4~$9+5rsaFr1 z0@HP$NyC2kX6`#o+GPB^cH6{UK1E<3E*6 zaz)Ey%|nD0AL*#GZ&Il8*4x*+Xl7WXpN~3#Obv0n7@2CQ0rav9!7s~v@D99?) z{0a#fd*%$mYc7JYnhkKt0R%p1)-lBqidjWypFNRf~H0?LQf)3wrE>FGMz$KTMxti*;@E@rtsdK z7o%5m$C@!p@Ou^X*~-oZB9djbd*qjNofeqr!vsr&qDDP_G0+scWVOS|U}tqCV|8Zu zy!hI|tUTFW*hdIY$+)=ugf37XZAROg&ngzaEQasmPH(+G?|yUC2$HQEk-yJihIiMN zgwkK2=4X2Jq0!Ev#-E~+?ODKXHic0S%6;T~9ltF8Hj@xnvk}9B7D_V(7G|c&&^f2q zTcThTwLgu*BvC(x9JMn+7{m})6$k8r!2&oAS4vznBX9A39h^5YzA*W?ed`bK>~9Al zaTu6C`s2R=VA0i3{pVSCoqzwsCNPFrVPiMnx3#}r37z0%baf2R KvJ6^f8HBMf(Z0|V#*(!d%nU;@WKSj8 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 literal 0 HcmV?d00001 From aa129ef0c7268d285133ae62733d3f09bb1f8d2e Mon Sep 17 00:00:00 2001 From: Grifon Date: Wed, 24 Jan 2024 17:22:37 +0300 Subject: [PATCH 13/24] refactoring --- Tracker.xcodeproj/project.pbxproj | 10 +- .../xcdebugger/Breakpoints_v2.xcbkptlist | 44 ++++---- Tracker/Application/AppDelegate.swift | 3 +- .../Domain/Store/TrackerCategoryStore.swift | 3 +- Tracker/Domain/Store/TrackerRecordStore.swift | 9 +- Tracker/Domain/Store/TrackerStore.swift | 6 +- Tracker/Helpers/FormatDate.swift | 28 ++--- Tracker/Models/Enum/ChoiceParametrs.swift | 6 +- Tracker/Models/Enum/Errors.swift | 10 +- .../Resources/en.lproj/Localizable.strings | 8 +- .../Resources/ru.lproj/Localizable.strings | 8 +- .../TrackersViewController.swift | 12 +-- .../CategoryViewModel.swift | 0 .../EditTrackerViewModel.swift | 24 ++--- .../FiltersViewModel.swift | 0 .../NewCategoryViewModel.swift | 0 .../ShedulViewModel.swift | 3 +- .../StatisticViewModel.swift | 0 .../TrackerViewModel.swift | 102 ++++++++---------- .../trackersCellViewModel.swift | 0 20 files changed, 118 insertions(+), 158 deletions(-) rename Tracker/{ViewModel => ViewModels}/CategoryViewModel.swift (100%) rename Tracker/{ViewModel => ViewModels}/EditTrackerViewModel.swift (92%) rename Tracker/{ViewModel => ViewModels}/FiltersViewModel.swift (100%) rename Tracker/{ViewModel => ViewModels}/NewCategoryViewModel.swift (100%) rename Tracker/{ViewModel => ViewModels}/ShedulViewModel.swift (91%) rename Tracker/{ViewModel => ViewModels}/StatisticViewModel.swift (100%) rename Tracker/{ViewModel => ViewModels}/TrackerViewModel.swift (88%) rename Tracker/{ViewModel => ViewModels}/trackersCellViewModel.swift (100%) diff --git a/Tracker.xcodeproj/project.pbxproj b/Tracker.xcodeproj/project.pbxproj index 8163826..09b02b3 100644 --- a/Tracker.xcodeproj/project.pbxproj +++ b/Tracker.xcodeproj/project.pbxproj @@ -25,7 +25,6 @@ 1A45A6782ADE8E9E0063F08C /* TrackerStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A45A6772ADE8E9E0063F08C /* TrackerStore.swift */; }; 1A45A67A2ADE8EDB0063F08C /* TrackerCategoryStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A45A6792ADE8EDB0063F08C /* TrackerCategoryStore.swift */; }; 1A45A67C2ADE8F0F0063F08C /* TrackerRecordStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A45A67B2ADE8F0F0063F08C /* TrackerRecordStore.swift */; }; - 1A46558A2AD3D2B8006DE343 /* FormatDate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A4655892AD3D2B8006DE343 /* FormatDate.swift */; }; 1A4AA6D52B34BD8B00411C75 /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A4AA6D42B34BD8B00411C75 /* Colors.swift */; }; 1A52EFAF2ADC194C0002BD4B /* Date.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A52EFAE2ADC194C0002BD4B /* Date.swift */; }; 1A598BB62AC6F352009B166D /* TabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A598BB52AC6F352009B166D /* TabBarController.swift */; }; @@ -113,7 +112,6 @@ 1A45A6772ADE8E9E0063F08C /* TrackerStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerStore.swift; sourceTree = ""; }; 1A45A6792ADE8EDB0063F08C /* TrackerCategoryStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerCategoryStore.swift; sourceTree = ""; }; 1A45A67B2ADE8F0F0063F08C /* TrackerRecordStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerRecordStore.swift; sourceTree = ""; }; - 1A4655892AD3D2B8006DE343 /* FormatDate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormatDate.swift; sourceTree = ""; }; 1A4AA6D42B34BD8B00411C75 /* Colors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Colors.swift; sourceTree = ""; }; 1A52EFAE2ADC194C0002BD4B /* Date.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Date.swift; sourceTree = ""; }; 1A598BB52AC6F352009B166D /* TabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarController.swift; sourceTree = ""; }; @@ -243,7 +241,6 @@ 1A3477CB2AD18D2100EDEAED /* Helpers */ = { isa = PBXGroup; children = ( - 1A4655892AD3D2B8006DE343 /* FormatDate.swift */, 1A83156D2B2C786000097DE7 /* HandlerResultType.swift */, 1A4AA6D42B34BD8B00411C75 /* Colors.swift */, ); @@ -347,7 +344,7 @@ children = ( 1A598BB42AC6F0A9009B166D /* ViewControllers */, 1A598BB92AC6FB97009B166D /* Views */, - 1A9AC5B52AF3854A00A9213D /* ViewModel */, + 1A9AC5B52AF3854A00A9213D /* ViewModels */, 1A2FC2482AED1E05000156FE /* Domain */, 1A21DD932ADE5A0700583A01 /* CoreData */, 1A84F3772AC87F28001DD42F /* Models */, @@ -383,7 +380,7 @@ path = CollectionViewCell; sourceTree = ""; }; - 1A9AC5B52AF3854A00A9213D /* ViewModel */ = { + 1A9AC5B52AF3854A00A9213D /* ViewModels */ = { isa = PBXGroup; children = ( 1A9AC5B32AF37AED00A9213D /* TrackerViewModel.swift */, @@ -394,7 +391,7 @@ 1A79C42C2B39C3E4009CE43A /* StatisticViewModel.swift */, 1AD888B92B3AF7A400F8C26D /* NewCategoryViewModel.swift */, ); - path = ViewModel; + path = ViewModels; sourceTree = ""; }; 1AA7BFC52B00E41A00C678FF /* Resources */ = { @@ -712,7 +709,6 @@ 1A272CE42B27922A00D39A44 /* PinnedCategoryStore.swift in Sources */, 1AD277712ACD4B1800F92219 /* UITextField.swift in Sources */, 1A0C3C192B388CC3008E5202 /* MetrycEvent.swift in Sources */, - 1A46558A2AD3D2B8006DE343 /* FormatDate.swift in Sources */, 1A52EFAF2ADC194C0002BD4B /* Date.swift in Sources */, 1AE9892D2ACAE76E00392315 /* WeekDay.swift in Sources */, 1A3ED4522ACDEA3E0006EBBB /* WeekDayTableViewCell.swift in Sources */, diff --git a/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index 14aed4a..e11d673 100644 --- a/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -4493,8 +4493,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "142" - endingLineNumber = "142" + startingLineNumber = "141" + endingLineNumber = "141" landmarkName = "addNewTracker(_:nameCategory:)" landmarkType = "7"> @@ -4525,8 +4525,8 @@ filePath = "Tracker/Domain/Store/TrackerRecordStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "177" - endingLineNumber = "177" + startingLineNumber = "176" + endingLineNumber = "176" landmarkName = "deleteOrCreateTrackerRecord(id:date:)" landmarkType = "7"> @@ -4618,8 +4618,8 @@ filePath = "Tracker/Domain/Store/TrackerRecordStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "97" - endingLineNumber = "97" + startingLineNumber = "96" + endingLineNumber = "96" landmarkName = "addNewTrackerRecord(id:date:)" landmarkType = "7"> @@ -4837,8 +4837,8 @@ filePath = "Tracker/Domain/Store/TrackerCategoryStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "137" - endingLineNumber = "137" + startingLineNumber = "136" + endingLineNumber = "136" landmarkName = "controllerDidChangeContent(_:)" landmarkType = "7"> @@ -4853,8 +4853,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "226" - endingLineNumber = "226" + startingLineNumber = "225" + endingLineNumber = "225" landmarkName = "addPinnedCategory(_:pinnedCategory:)" landmarkType = "7"> @@ -5550,8 +5550,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "114" - endingLineNumber = "114" + startingLineNumber = "113" + endingLineNumber = "113" landmarkName = "searchTracker(id:)" landmarkType = "7"> @@ -5598,8 +5598,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "112" - endingLineNumber = "112" + startingLineNumber = "111" + endingLineNumber = "111" landmarkName = "searchTracker(id:)" landmarkType = "7"> @@ -5738,8 +5738,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "242" - endingLineNumber = "242" + startingLineNumber = "240" + endingLineNumber = "240" landmarkName = "controllerDidChangeContent(_:)" landmarkType = "7"> @@ -6119,8 +6119,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "171" - endingLineNumber = "171" + startingLineNumber = "170" + endingLineNumber = "170" landmarkName = "updateTracker(tracker:nameCategory:)" landmarkType = "7"> @@ -6212,8 +6212,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "158" - endingLineNumber = "158" + startingLineNumber = "157" + endingLineNumber = "157" landmarkName = "updateTracker(tracker:nameCategory:)" landmarkType = "7"> @@ -6354,8 +6354,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "154" - endingLineNumber = "154" + startingLineNumber = "153" + endingLineNumber = "153" landmarkName = "updateTracker(tracker:nameCategory:)" landmarkType = "7"> diff --git a/Tracker/Application/AppDelegate.swift b/Tracker/Application/AppDelegate.swift index e31d71d..e8073ac 100644 --- a/Tracker/Application/AppDelegate.swift +++ b/Tracker/Application/AppDelegate.swift @@ -9,8 +9,7 @@ import UIKit import CoreData import YandexMobileMetrica -//please insert your key -private let apiKeyYMM = "" +private let apiKeyYMM = "4a0432b5-adf6-45f2-800c-b160a4f7c44a" @main class AppDelegate: UIResponder, UIApplicationDelegate { diff --git a/Tracker/Domain/Store/TrackerCategoryStore.swift b/Tracker/Domain/Store/TrackerCategoryStore.swift index b408c92..368bfc8 100644 --- a/Tracker/Domain/Store/TrackerCategoryStore.swift +++ b/Tracker/Domain/Store/TrackerCategoryStore.swift @@ -46,8 +46,7 @@ final class TrackerCategoryStore: NSObject { return fetchedResultController }() - convenience override init() - { + convenience override init() { let context = AppDelegate.container.viewContext self.init(context: context) } diff --git a/Tracker/Domain/Store/TrackerRecordStore.swift b/Tracker/Domain/Store/TrackerRecordStore.swift index 14b31c1..9f89f55 100644 --- a/Tracker/Domain/Store/TrackerRecordStore.swift +++ b/Tracker/Domain/Store/TrackerRecordStore.swift @@ -45,8 +45,7 @@ final class TrackerRecordStore: NSObject { return fetchedResultController }() - convenience override init() - { + convenience override init() { let context = AppDelegate.container.viewContext self.init(context: context) } @@ -97,8 +96,7 @@ private extension TrackerRecordStore { return save() } - func deleteTrackerRecord(_ trackerRecordCoreData: TrackerRecordCoreData) -> Result - { + func deleteTrackerRecord(_ trackerRecordCoreData: TrackerRecordCoreData) -> Result { context.delete(trackerRecordCoreData) return save() } @@ -116,7 +114,8 @@ private extension TrackerRecordStore { return trackerRecord.first } - func getLisTrackersRecord(id: UUID) throws -> [TrackerRecordCoreData] { + func getLisTrackersRecord(id: UUID) throws -> [TrackerRecordCoreData] + { let request = NSFetchRequest(entityName: "\(TrackerRecordCoreData.self)") request.returnsObjectsAsFaults = false request.predicate = NSPredicate(format: "%K == %@", diff --git a/Tracker/Domain/Store/TrackerStore.swift b/Tracker/Domain/Store/TrackerStore.swift index 8125fee..277fc24 100644 --- a/Tracker/Domain/Store/TrackerStore.swift +++ b/Tracker/Domain/Store/TrackerStore.swift @@ -44,8 +44,7 @@ final class TrackerStore: NSObject { return fetchedResultController }() - convenience override init() - { + convenience override init() { let context = AppDelegate.container.viewContext self.init(context: context) } @@ -236,8 +235,7 @@ extension TrackerStore: TrackerStoreProtocol { //MARK: - NSFetchedResultsControllerDelegate extension TrackerStore: NSFetchedResultsControllerDelegate { - func controllerDidChangeContent(_ controller: NSFetchedResultsController) - { + func controllerDidChangeContent(_ controller: NSFetchedResultsController) { guard let delegate else { return } delegate.updateTracker(self) } diff --git a/Tracker/Helpers/FormatDate.swift b/Tracker/Helpers/FormatDate.swift index ade411c..7ac4e82 100644 --- a/Tracker/Helpers/FormatDate.swift +++ b/Tracker/Helpers/FormatDate.swift @@ -7,19 +7,19 @@ import Foundation -final class FormatDate { - static let shared = FormatDate() +//final class FormatDate { +// static let shared = FormatDate() - private lazy var dateFormatter: DateFormatter = { - let dateFormatter = DateFormatter() - dateFormatter.dateFormat = "e" - - return dateFormatter - }() +// private lazy var dateFormatter: DateFormatter = { +// let dateFormatter = DateFormatter() +// dateFormatter.dateFormat = "e" +// +// return dateFormatter +// }() - func greateWeekDayInt(date: Date) -> Int { - let weekDayString = dateFormatter.string(from: date) - let deyWeek = NSString(string: weekDayString).intValue - 1 - return Int(deyWeek) - } -} +// func greateWeekDayInt(date: Date) -> Int { +// let weekDayString = dateFormatter.string(from: date) +// let deyWeek = NSString(string: weekDayString).intValue +// return Int(deyWeek) +// } +//} diff --git a/Tracker/Models/Enum/ChoiceParametrs.swift b/Tracker/Models/Enum/ChoiceParametrs.swift index c2fcddf..cb54cef 100644 --- a/Tracker/Models/Enum/ChoiceParametrs.swift +++ b/Tracker/Models/Enum/ChoiceParametrs.swift @@ -7,7 +7,7 @@ import Foundation -enum ChoiceParametrs { +enum ChoiceParametrs: String { case category case schedule @@ -15,9 +15,9 @@ enum ChoiceParametrs { var name: String switch self { case .category: - name = NSLocalizedString("category", comment: "") + name = NSLocalizedString(self.rawValue, comment: "") case .schedule: - name = NSLocalizedString("schedule", comment: "") + name = NSLocalizedString(self.rawValue, comment: "") } return name } diff --git a/Tracker/Models/Enum/Errors.swift b/Tracker/Models/Enum/Errors.swift index 68e629a..de21c2a 100644 --- a/Tracker/Models/Enum/Errors.swift +++ b/Tracker/Models/Enum/Errors.swift @@ -60,7 +60,7 @@ enum StoreErrors { } } - enum TrackrerRecordStoreError: Error { + enum TrackrerRecordStoreError: String, Error { case decodingErrorInvalidId case decodingErrorInvalidDate case loadTrackerRecord @@ -70,13 +70,13 @@ enum StoreErrors { var localizedDescription = "" switch self { case .decodingErrorInvalidId: - localizedDescription = NSLocalizedString("decodingErrorInvalidId", comment: "") + localizedDescription = NSLocalizedString(self.rawValue, comment: "") case .decodingErrorInvalidDate: - localizedDescription = NSLocalizedString("decodingErrorInvalidDate", comment: "") + localizedDescription = NSLocalizedString(self.rawValue, comment: "") case .loadTrackerRecord: - localizedDescription = NSLocalizedString("loadTrackerRecord", comment: "") + localizedDescription = NSLocalizedString(self.rawValue, comment: "") case .getTrackerRecord: - localizedDescription = NSLocalizedString("getTrackerRecord", comment: "") + localizedDescription = NSLocalizedString(self.rawValue, comment: "") } return localizedDescription diff --git a/Tracker/Resources/en.lproj/Localizable.strings b/Tracker/Resources/en.lproj/Localizable.strings index d71f0fc..defcda2 100644 --- a/Tracker/Resources/en.lproj/Localizable.strings +++ b/Tracker/Resources/en.lproj/Localizable.strings @@ -113,7 +113,7 @@ "getTrackerRecord" = "Error receiving the tracker"; //MARK: - StatisticsEvent -"bestPeriod" = "Best period" -"perfectDays" = "Perfect days" -"trackersCompleted" = "Trackers completed" -"averageValue" = "Average Value" +"bestPeriod" = "Best period"; +"perfectDays" = "Perfect days"; +"trackersCompleted" = "Trackers completed"; +"averageValue" = "Average Value"; diff --git a/Tracker/Resources/ru.lproj/Localizable.strings b/Tracker/Resources/ru.lproj/Localizable.strings index 7e0e4fd..0112121 100644 --- a/Tracker/Resources/ru.lproj/Localizable.strings +++ b/Tracker/Resources/ru.lproj/Localizable.strings @@ -112,7 +112,7 @@ "getTrackerRecord" = "Ошибка получения трекера"; //MARK: - StatisticsEvent -"bestPeriod" = "Лучший период" -"perfectDays" = "Идеальные дни" -"trackersCompleted" = "Трекеров завершено" -"averageValue" = "Среднее значение" +"bestPeriod" = "Лучший период"; +"perfectDays" = "Идеальные дни"; +"trackersCompleted" = "Трекеров завершено"; +"averageValue" = "Среднее значение"; diff --git a/Tracker/ViewControllers/ViewControllers/TrackersViewController.swift b/Tracker/ViewControllers/ViewControllers/TrackersViewController.swift index c7efe42..2268391 100644 --- a/Tracker/ViewControllers/ViewControllers/TrackersViewController.swift +++ b/Tracker/ViewControllers/ViewControllers/TrackersViewController.swift @@ -225,21 +225,17 @@ private extension TrackersViewController { switch state { case .allTrackers: viewModel.allTrackersByDate(date: datePicker.date) - setColorButtonFilter(state: viewModel.getFilterState(), - button: buttonFilter) + setColorButtonFilter(state: state, button: buttonFilter) case .toDayTrackers: datePicker.date = Date() viewModel.allTrackersByDate(date: datePicker.date) - setColorButtonFilter(state: viewModel.getFilterState(), - button: buttonFilter) + setColorButtonFilter(state: state, button: buttonFilter) case .completed: viewModel.getCompleted(date: datePicker.date, flag: true) - setColorButtonFilter(state: viewModel.getFilterState(), - button: buttonFilter) + setColorButtonFilter(state: state, button: buttonFilter) case .notCompleted: viewModel.getNotCompleted(date: datePicker.date, flag: false) - setColorButtonFilter(state: viewModel.getFilterState(), - button: buttonFilter) + setColorButtonFilter(state: state, button: buttonFilter) } } } diff --git a/Tracker/ViewModel/CategoryViewModel.swift b/Tracker/ViewModels/CategoryViewModel.swift similarity index 100% rename from Tracker/ViewModel/CategoryViewModel.swift rename to Tracker/ViewModels/CategoryViewModel.swift diff --git a/Tracker/ViewModel/EditTrackerViewModel.swift b/Tracker/ViewModels/EditTrackerViewModel.swift similarity index 92% rename from Tracker/ViewModel/EditTrackerViewModel.swift rename to Tracker/ViewModels/EditTrackerViewModel.swift index 25f58eb..5951308 100644 --- a/Tracker/ViewModel/EditTrackerViewModel.swift +++ b/Tracker/ViewModels/EditTrackerViewModel.swift @@ -87,8 +87,7 @@ extension EditTrackerViewModel: EditTrackerModelProtocol { return flag } - func reverseIsSchedul() - { + func reverseIsSchedul() { isSchedul = isSchedul ? !isSchedul : isSchedul } @@ -106,8 +105,7 @@ extension EditTrackerViewModel: EditTrackerModelProtocol { return stringListDay } - func setListWeekDay(listWeekDay: [WeekDay]) - { + func setListWeekDay(listWeekDay: [WeekDay]) { schedule = listWeekDay } @@ -134,31 +132,23 @@ extension EditTrackerViewModel: EditTrackerModelProtocol { return emojiRow } - func setSchedule(_ vc: CreateTrackerViewController, schedule: [WeekDay]) - { + func setSchedule(_ vc: CreateTrackerViewController, schedule: [WeekDay]) { self.schedule = schedule } - func setColor(_ vc: CreateTrackerViewController, color: UIColor) - { + func setColor(_ vc: CreateTrackerViewController, color: UIColor) { self.color = color } - func setNameNewCategory(_ vc: CreateTrackerViewController, - nameCategory: String) - { + func setNameNewCategory(_ vc: CreateTrackerViewController, nameCategory: String) { self.nameNewCategory = nameCategory } - func setNameTracker(_ vc: CreateTrackerViewController, - nameTracker: String) - { + func setNameTracker(_ vc: CreateTrackerViewController, nameTracker: String) { self.nameTracker = nameTracker } - func setEmojiTracker(_ vc: CreateTrackerViewController, - emoji: String) - { + func setEmojiTracker(_ vc: CreateTrackerViewController, emoji: String) { self.emoji = emoji } diff --git a/Tracker/ViewModel/FiltersViewModel.swift b/Tracker/ViewModels/FiltersViewModel.swift similarity index 100% rename from Tracker/ViewModel/FiltersViewModel.swift rename to Tracker/ViewModels/FiltersViewModel.swift diff --git a/Tracker/ViewModel/NewCategoryViewModel.swift b/Tracker/ViewModels/NewCategoryViewModel.swift similarity index 100% rename from Tracker/ViewModel/NewCategoryViewModel.swift rename to Tracker/ViewModels/NewCategoryViewModel.swift diff --git a/Tracker/ViewModel/ShedulViewModel.swift b/Tracker/ViewModels/ShedulViewModel.swift similarity index 91% rename from Tracker/ViewModel/ShedulViewModel.swift rename to Tracker/ViewModels/ShedulViewModel.swift index 3856f7e..590988b 100644 --- a/Tracker/ViewModel/ShedulViewModel.swift +++ b/Tracker/ViewModels/ShedulViewModel.swift @@ -19,8 +19,7 @@ final class SheduleViewModel { } extension SheduleViewModel: SheduleViewModelProtocol { - func setListWeekDay(listWeekDay: [WeekDay]) - { + func setListWeekDay(listWeekDay: [WeekDay]) { self.listWeekDay = listWeekDay } } diff --git a/Tracker/ViewModel/StatisticViewModel.swift b/Tracker/ViewModels/StatisticViewModel.swift similarity index 100% rename from Tracker/ViewModel/StatisticViewModel.swift rename to Tracker/ViewModels/StatisticViewModel.swift diff --git a/Tracker/ViewModel/TrackerViewModel.swift b/Tracker/ViewModels/TrackerViewModel.swift similarity index 88% rename from Tracker/ViewModel/TrackerViewModel.swift rename to Tracker/ViewModels/TrackerViewModel.swift index fd98a61..4a5cd6f 100644 --- a/Tracker/ViewModel/TrackerViewModel.swift +++ b/Tracker/ViewModels/TrackerViewModel.swift @@ -162,37 +162,42 @@ private extension TrackerViewModel { } } - func comparisonWeekDays(date: Date, day: WeekDay) -> Bool - { - FormatDate.shared.greateWeekDayInt(date: date) == day.rawValue - } - func filterListTrackersWeekDay(trackerCategory: [TrackerCategory], date: Date) -> [TrackerCategory] { - let listCategories = trackerCategory.filter { - !$0.arrayTrackers.filter { - !$0.schedule.filter { - comparisonWeekDays(date: date, day: $0) }.isEmpty }.isEmpty + let calendar = Calendar.current + let filter = calendar.component(.weekday, from: date) + + let listCategories: [TrackerCategory] = trackerCategory.compactMap { cat in + let trackers = cat.arrayTrackers.filter { tracker in + tracker.schedule.contains { weekDay in + weekDay.rawValue + 1 == filter + } + } + + if trackers.isEmpty { + return nil + } + + return TrackerCategory(nameCategory: cat.nameCategory, + arrayTrackers: trackers, + isPinned: cat.isPinned) } + isfilterListTrackersWeekDay = !listCategories.isEmpty return listCategories } - func filterListTrackersCompleted(date: Date, flag: Bool) - { + func filterListTrackersCompleted(date: Date, flag: Bool) { setVisibleCategory(date: date, flag: flag) } - func filterListTrackersNotCompleted(date: Date, flag: Bool) - { + func filterListTrackersNotCompleted(date: Date, flag: Bool) { setVisibleCategory(date: date, flag: flag) } - func deleteOrCreateTrackerRecord(id: UUID, date: Date) -> Result - { - return trackerRecordStore.deleteOrCreateTrackerRecord(id: id, - date: date.ignoringTime) + func deleteOrCreateTrackerRecord(id: UUID, date: Date) -> Result { + trackerRecordStore.deleteOrCreateTrackerRecord(id: id, date: date.ignoringTime) } func trackerCompletedOrNotByCategory(category: TrackerCategory, @@ -220,13 +225,11 @@ extension TrackerViewModel: TrackerViewModelProtocol trackerStore.deleteTracker(id) } - func deleteTrackersRecord(id: UUID) -> Result - { + func deleteTrackersRecord(id: UUID) -> Result { trackerRecordStore.deleteTrackerRecord(id: id) } - func addCategory(nameCategory: String) -> Result - { + func addCategory(nameCategory: String) -> Result { trackerCategoryStore.addCategory(nameCategory) } @@ -243,13 +246,11 @@ extension TrackerViewModel: TrackerViewModelProtocol } } - func treckersRecordsResult() -> Result, Error> - { + func treckersRecordsResult() -> Result, Error> { trackerRecordStore.treckersRecordsResult() } - func loadTrackerRecord(id: UUID) -> Result - { + func loadTrackerRecord(id: UUID) -> Result { trackerRecordStore.getCountTrackerRecord(id: id) } @@ -261,10 +262,8 @@ extension TrackerViewModel: TrackerViewModelProtocol return trackerCoreData } - func updateTracker(tracker: Tracker, nameCategory: String) -> Result - { - trackerStore.updateTracker(tracker: tracker, - nameCategory: nameCategory) + func updateTracker(tracker: Tracker, nameCategory: String) -> Result{ + trackerStore.updateTracker(tracker: tracker, nameCategory: nameCategory) } func getShowListTrackersForDay(date: Date) @@ -298,13 +297,11 @@ extension TrackerViewModel: TrackerViewModelProtocol } } - func updateCompletedTrackers(tracker: Tracker, date: Date) -> Result - { + func updateCompletedTrackers(tracker: Tracker, date: Date) -> Result { return deleteOrCreateTrackerRecord(id: tracker.id, date: date) } - func getShowListTrackerSearchForName(_ searchCategory: [TrackerCategory]) - { + func getShowListTrackerSearchForName(_ searchCategory: [TrackerCategory]) { visibleCategory = .success(searchCategory) } @@ -321,8 +318,7 @@ extension TrackerViewModel: TrackerViewModelProtocol } } - func setIndexPath(_ indexPath: IndexPath) - { + func setIndexPath(_ indexPath: IndexPath) { self.indexPath = indexPath } @@ -338,56 +334,46 @@ extension TrackerViewModel: TrackerViewModelProtocol } } - func deleteAndGetPinnedCategory(id: UUID) -> Result - { + func deleteAndGetPinnedCategory(id: UUID) -> Result { pinnedCategoryStore.deleteAndGetPinnedCategory(id) } - func allTrackersByDate(date: Date) - { + func allTrackersByDate(date: Date) { getShowListTrackersForDay(date: date) } - func getNotCompleted(date: Date, flag: Bool) - { + func getNotCompleted(date: Date, flag: Bool) { filterListTrackersNotCompleted(date: date, flag: flag) } - func getCompleted(date: Date, flag: Bool) - { + func getCompleted(date: Date, flag: Bool) { filterListTrackersCompleted(date: date, flag: flag) } - func setFilterState(state: FiltersState) - { + func setFilterState(state: FiltersState) { filterState = state } - func getFilterState() -> FiltersState - { + func getFilterState() -> FiltersState { filterState } - func getSelectFilter() -> String? - { + func getSelectFilter() -> String? { selectFilter } - func getCountTrackerCompleted(id: UUID) -> Result - { + func getCountTrackerCompleted(id: UUID) -> Result { trackerRecordStore.getCountTrackerRecord(id: id) } - func getIsCategoryForDay() -> Bool? - { + func getIsCategoryForDay() -> Bool? { isfilterListTrackersWeekDay } } //MARK: - TrackerCategoryStoreDelegate extension TrackerViewModel: TrackerCategoryStoreDelegate { - func storeCategory(trackerCategoryStore: TrackerCategoryStoreProtocol) - { + func storeCategory(trackerCategoryStore: TrackerCategoryStoreProtocol) { category = getCategory() } } @@ -395,8 +381,7 @@ extension TrackerViewModel: TrackerCategoryStoreDelegate { //MARK: - TrackerRecordStoreDelegate extension TrackerViewModel: TrackerRecordStoreDelegate { func trackerRecordStore(trackerRecordStore: TrackerRecordStoreProtocol, - flag: Bool) - { + flag: Bool) { completedTrackers = trackerRecordStore.treckersRecordsResult() if flag { self.indexPath = indexPath @@ -405,8 +390,7 @@ extension TrackerViewModel: TrackerRecordStoreDelegate { } //MARK: - TrackerStoreDelegate -extension TrackerViewModel: TrackerStoreDelegate -{ +extension TrackerViewModel: TrackerStoreDelegate { func updateTracker(_ trackerCoreData: TrackerStoreProtocol) { category = getCategory() } diff --git a/Tracker/ViewModel/trackersCellViewModel.swift b/Tracker/ViewModels/trackersCellViewModel.swift similarity index 100% rename from Tracker/ViewModel/trackersCellViewModel.swift rename to Tracker/ViewModels/trackersCellViewModel.swift From a6848937c96d23bb9de07f89aceb8f614c11e9bf Mon Sep 17 00:00:00 2001 From: Grifon Date: Fri, 26 Jan 2024 10:32:31 +0300 Subject: [PATCH 14/24] refactoring --- Tracker/Extension/UIColor.swift | 2 +- Tracker/Helpers/FormatDate.swift | 25 ------------------- Tracker/Resources/en.lproj/InfoPlist.strings | 2 +- Tracker/Resources/ru.lproj/InfoPlist.strings | 2 +- .../TrackersViewController.swift | 2 +- 5 files changed, 4 insertions(+), 29 deletions(-) delete mode 100644 Tracker/Helpers/FormatDate.swift diff --git a/Tracker/Extension/UIColor.swift b/Tracker/Extension/UIColor.swift index c165cc5..2a9af9f 100644 --- a/Tracker/Extension/UIColor.swift +++ b/Tracker/Extension/UIColor.swift @@ -15,7 +15,7 @@ extension UIColor { static var whiteDay: UIColor {UIColor(named: "whiteDay") ?? .white} static var redDay: UIColor {UIColor(named: "redDay") ?? .red} static var backgroundDay: UIColor {UIColor(named: "backgroundDay") ?? .gray} - static var liteGray: UIColor {UIColor(named: "liteGray") ?? .gray} + static var lightGray: UIColor {UIColor(named: "lightGray") ?? .gray} static var backgroundNight: UIColor {UIColor(named: "backgroundNight") ?? .gray} static var grayOpacity30: UIColor {UIColor(named: "grayOpacity30") ?? .gray} diff --git a/Tracker/Helpers/FormatDate.swift b/Tracker/Helpers/FormatDate.swift deleted file mode 100644 index 7ac4e82..0000000 --- a/Tracker/Helpers/FormatDate.swift +++ /dev/null @@ -1,25 +0,0 @@ -// -// File.swift -// FormatDate -// -// Created by Григорий Машук on 9.10.23. -// - -import Foundation - -//final class FormatDate { -// static let shared = FormatDate() - -// private lazy var dateFormatter: DateFormatter = { -// let dateFormatter = DateFormatter() -// dateFormatter.dateFormat = "e" -// -// return dateFormatter -// }() - -// func greateWeekDayInt(date: Date) -> Int { -// let weekDayString = dateFormatter.string(from: date) -// let deyWeek = NSString(string: weekDayString).intValue -// return Int(deyWeek) -// } -//} diff --git a/Tracker/Resources/en.lproj/InfoPlist.strings b/Tracker/Resources/en.lproj/InfoPlist.strings index 409f92b..678e92d 100644 --- a/Tracker/Resources/en.lproj/InfoPlist.strings +++ b/Tracker/Resources/en.lproj/InfoPlist.strings @@ -2,7 +2,7 @@ InfoPlist.strings Tracker - Created by Марина Машук on 14.11.23. + Created by Григорий Машук on 14.11.23. */ diff --git a/Tracker/Resources/ru.lproj/InfoPlist.strings b/Tracker/Resources/ru.lproj/InfoPlist.strings index 31acaad..7574a1f 100644 --- a/Tracker/Resources/ru.lproj/InfoPlist.strings +++ b/Tracker/Resources/ru.lproj/InfoPlist.strings @@ -2,7 +2,7 @@ InfoPlist.strings Tracker - Created by Марина Машук on 14.11.23. + Created by Григорий Машук on 14.11.23. */ diff --git a/Tracker/ViewControllers/ViewControllers/TrackersViewController.swift b/Tracker/ViewControllers/ViewControllers/TrackersViewController.swift index 2268391..a90d34c 100644 --- a/Tracker/ViewControllers/ViewControllers/TrackersViewController.swift +++ b/Tracker/ViewControllers/ViewControllers/TrackersViewController.swift @@ -84,7 +84,7 @@ final class TrackersViewController: UIViewController { private lazy var datePicker: UIDatePicker = { let datePicker = UIDatePicker() - datePicker.backgroundColor = .liteGray + datePicker.backgroundColor = .lightGray datePicker.datePickerMode = .date datePicker.tintColor = .blueDay datePicker.preferredDatePickerStyle = .compact From aca401164f889dc32d28be85f48f477a3dee9197 Mon Sep 17 00:00:00 2001 From: Grifon Date: Fri, 26 Jan 2024 11:18:29 +0300 Subject: [PATCH 15/24] refactoring --- Podfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Podfile b/Podfile index ba8c6fb..94e7dfd 100644 --- a/Podfile +++ b/Podfile @@ -3,10 +3,12 @@ platform :ios, '13.4' target 'Tracker' do use_frameworks! - pod 'YandexMobileMetrica' +# pod 'YandexMobileMetrica' target 'TrackerTests' do inherit! :search_paths end + + pod 'YandexMobileMetrica/Dynamic', '4.5.2' end From 400c8608ee73bba0b0cddc221e01d1cd91bd5938 Mon Sep 17 00:00:00 2001 From: Grifon Date: Fri, 26 Jan 2024 12:12:14 +0300 Subject: [PATCH 16/24] refactoring --- Podfile | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Podfile b/Podfile index 94e7dfd..d9914de 100644 --- a/Podfile +++ b/Podfile @@ -3,12 +3,10 @@ platform :ios, '13.4' target 'Tracker' do use_frameworks! -# pod 'YandexMobileMetrica' + pod 'YandexMobileMetrica/Dinamic' target 'TrackerTests' do inherit! :search_paths end - - pod 'YandexMobileMetrica/Dynamic', '4.5.2' end From dfef8babe63eef2ea839fdd899718c73bd64a2bf Mon Sep 17 00:00:00 2001 From: Grifon Date: Fri, 26 Jan 2024 14:18:57 +0300 Subject: [PATCH 17/24] refactoring --- Podfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Podfile b/Podfile index d9914de..9c84027 100644 --- a/Podfile +++ b/Podfile @@ -3,10 +3,10 @@ platform :ios, '13.4' target 'Tracker' do use_frameworks! - pod 'YandexMobileMetrica/Dinamic' - target 'TrackerTests' do inherit! :search_paths end + + pod 'YandexMobileMetrica/Dinamic' end From 3b7e9d6a49f64b99e9f09fc27f5e983020438e8c Mon Sep 17 00:00:00 2001 From: Grifon Date: Sun, 28 Jan 2024 08:50:56 +0300 Subject: [PATCH 18/24] refactoring --- Podfile | 4 ++-- .../{liteGray.colorset => lightGray.colorset}/Contents.json | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename Tracker/Assets.xcassets/Colors/{liteGray.colorset => lightGray.colorset}/Contents.json (100%) diff --git a/Podfile b/Podfile index 9c84027..e7352d7 100644 --- a/Podfile +++ b/Podfile @@ -2,11 +2,11 @@ platform :ios, '13.4' target 'Tracker' do use_frameworks! + + pod 'YandexMobileMetrica' target 'TrackerTests' do inherit! :search_paths end - pod 'YandexMobileMetrica/Dinamic' - end diff --git a/Tracker/Assets.xcassets/Colors/liteGray.colorset/Contents.json b/Tracker/Assets.xcassets/Colors/lightGray.colorset/Contents.json similarity index 100% rename from Tracker/Assets.xcassets/Colors/liteGray.colorset/Contents.json rename to Tracker/Assets.xcassets/Colors/lightGray.colorset/Contents.json From d1174f9a812d399443be30bed83138f1fc29e4cf Mon Sep 17 00:00:00 2001 From: Grifon Date: Mon, 29 Jan 2024 16:09:13 +0300 Subject: [PATCH 19/24] refactoring by Pull Requesr --- Podfile | 2 +- Podfile.lock | 18 +- Tracker.xcodeproj/project.pbxproj | 74 +++++- .../xcdebugger/Breakpoints_v2.xcbkptlist | 227 ++---------------- .../AnalyticsService/AnalyticsService.swift | 69 +----- Tracker/Application/AppDelegate.swift | 21 +- Tracker/Application/SceneDelegate.swift | 6 +- .../Domain/Helpers/DaysValueTransformer.swift | 16 +- .../Domain/Helpers/UIColorMarshalling.swift | 2 +- .../Domain/Store/PinnedCategoryStore.swift | 38 ++- .../Domain/Store/TrackerCategoryStore.swift | 27 +-- Tracker/Domain/Store/TrackerRecordStore.swift | 36 +-- Tracker/Domain/Store/TrackerStore.swift | 98 ++++---- Tracker/Extension/UITextField.swift | 3 +- Tracker/Helpers/HandlerResultType.swift | 9 +- Tracker/Models/ConfigModels.swift | 28 --- Tracker/Models/CustomCellModel.swift | 15 ++ Tracker/Models/Enum/ChoiceParametrs.swift | 6 +- Tracker/Models/Enum/DataSource.swift | 23 ++ Tracker/Models/Enum/Errors.swift | 27 +-- Tracker/Models/Enum/Event.swift | 12 + Tracker/Models/Enum/EventAnalytics.swift | 23 +- Tracker/Models/Enum/FiltersState.swift | 10 +- Tracker/Models/Enum/Fixed.swift | 11 + Tracker/Models/Enum/Header.swift | 10 +- Tracker/Models/Enum/ItemClick.swift | 14 ++ Tracker/Models/Enum/NameEvent.swift | 6 +- Tracker/Models/Enum/Screen.swift | 10 + Tracker/Models/Enum/StatisticsEvent.swift | 10 +- Tracker/Models/Enum/UserDefaultKeys.swift | 7 +- Tracker/Models/Enum/WeekDay.swift | 30 ++- Tracker/Models/Enum/YandexApiKey.swift | 10 + Tracker/Models/MetrycEvent.swift | 2 - Tracker/Models/Onboarding.swift | 11 + Tracker/Models/PinnTrackerModel.swift | 4 +- Tracker/Models/TrackerCategoryModel.swift | 2 - Tracker/Models/TrackerModel.swift | 5 +- Tracker/Models/Translate.swift | 88 +++++++ Tracker/Models/UpdateTracker.swift | 11 + .../Resources/en.lproj/Localizable.strings | 12 +- .../Resources/ru.lproj/Localizable.strings | 12 +- .../OnboardingPageViewController.swift | 15 +- .../OnboardingViewController.swift | 24 +- .../ViewControllers/TabBarController.swift | 22 +- .../CreateTrackerViewController.swift | 178 ++++++-------- .../EventSelectionViewController.swift | 30 +-- .../FiltersViewController.swift | 21 +- .../NewCategoriViewController.swift | 52 ++-- .../ScheduleViewController.swift | 40 +-- .../SelectCategoriesViewController.swift | 85 +++---- .../StatisticsViewController.swift | 41 ++-- .../TrackersViewController.swift | 208 +++++++--------- Tracker/ViewModels/CategoryViewModel.swift | 21 +- Tracker/ViewModels/EditTrackerViewModel.swift | 58 ++--- Tracker/ViewModels/StatisticViewModel.swift | 5 +- Tracker/ViewModels/TrackerViewModel.swift | 52 ++-- .../ColorCollectionViewCell.swift | 15 +- .../EmojiCollectionViewCell.swift | 15 +- .../TrackersCollectionViewCell.swift | 48 ++-- .../CustomViews/HeaderReusableView.swift | 12 +- Tracker/Views/CustomViews/StatisticView.swift | 16 +- .../CustomViews/TrackerCollectionView.swift | 6 +- .../CreateTrackerTableViewCell.swift | 25 +- .../TableViewCell/CustomTableViewCell.swift | 24 +- .../TableViewCell/WeekDayTableViewCell.swift | 12 +- TrackerTests/TrackerTests.swift | 2 + .../testTrackersViewControllerDark.1.png | Bin 97098 -> 183031 bytes .../testTrackersViewControllerLight.1.png | Bin 85463 -> 176485 bytes .../TrackerTests/testViewController.1.png | Bin 97098 -> 0 bytes .../TrackerTests/testViewController.2.png | Bin 85463 -> 0 bytes 70 files changed, 868 insertions(+), 1204 deletions(-) delete mode 100644 Tracker/Models/ConfigModels.swift create mode 100644 Tracker/Models/CustomCellModel.swift create mode 100644 Tracker/Models/Enum/DataSource.swift create mode 100644 Tracker/Models/Enum/Event.swift create mode 100644 Tracker/Models/Enum/Fixed.swift create mode 100644 Tracker/Models/Enum/ItemClick.swift create mode 100644 Tracker/Models/Enum/Screen.swift create mode 100644 Tracker/Models/Enum/YandexApiKey.swift create mode 100644 Tracker/Models/Onboarding.swift create mode 100644 Tracker/Models/Translate.swift create mode 100644 Tracker/Models/UpdateTracker.swift delete mode 100644 TrackerTests/__Snapshots__/TrackerTests/testViewController.1.png delete mode 100644 TrackerTests/__Snapshots__/TrackerTests/testViewController.2.png diff --git a/Podfile b/Podfile index e7352d7..05399c8 100644 --- a/Podfile +++ b/Podfile @@ -3,7 +3,7 @@ platform :ios, '13.4' target 'Tracker' do use_frameworks! - pod 'YandexMobileMetrica' + pod 'YandexMobileMetrica/Dynamic' target 'TrackerTests' do inherit! :search_paths diff --git a/Podfile.lock b/Podfile.lock index 99cf77c..33dd8d2 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,15 +1,13 @@ PODS: - - YandexMobileMetrica (4.5.2): - - YandexMobileMetrica/Static (= 4.5.2) - - YandexMobileMetrica/Static (4.5.2): - - YandexMobileMetrica/Static/Core (= 4.5.2) - - YandexMobileMetrica/Static/Crashes (= 4.5.2) - - YandexMobileMetrica/Static/Core (4.5.2) - - YandexMobileMetrica/Static/Crashes (4.5.2): - - YandexMobileMetrica/Static/Core + - YandexMobileMetrica/Dynamic (4.5.2): + - YandexMobileMetrica/Dynamic/Core (= 4.5.2) + - YandexMobileMetrica/Dynamic/Crashes (= 4.5.2) + - YandexMobileMetrica/Dynamic/Core (4.5.2) + - YandexMobileMetrica/Dynamic/Crashes (4.5.2): + - YandexMobileMetrica/Dynamic/Core DEPENDENCIES: - - YandexMobileMetrica + - YandexMobileMetrica/Dynamic SPEC REPOS: trunk: @@ -18,6 +16,6 @@ SPEC REPOS: SPEC CHECKSUMS: YandexMobileMetrica: f5368ee93f286c793d73b58da00929babfc897c1 -PODFILE CHECKSUM: 27628e4a578a0edaa576d75cf46598218538c633 +PODFILE CHECKSUM: 134a89b347022b6f71780478cfaefa2eefbc0db9 COCOAPODS: 1.14.3 diff --git a/Tracker.xcodeproj/project.pbxproj b/Tracker.xcodeproj/project.pbxproj index 09b02b3..c925aee 100644 --- a/Tracker.xcodeproj/project.pbxproj +++ b/Tracker.xcodeproj/project.pbxproj @@ -8,6 +8,14 @@ /* Begin PBXBuildFile section */ 1A0077F82B2DD6DF000EAE69 /* CategoryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A0077F72B2DD6DF000EAE69 /* CategoryViewModel.swift */; }; + 1A06D9312B66B793005227E1 /* YandexApiKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A06D9302B66B793005227E1 /* YandexApiKey.swift */; }; + 1A06D9332B66B9EF005227E1 /* Onboarding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A06D9322B66B9EF005227E1 /* Onboarding.swift */; }; + 1A06D9352B66BA29005227E1 /* UpdateTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A06D9342B66BA29005227E1 /* UpdateTracker.swift */; }; + 1A06D9392B66CC00005227E1 /* Fixed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A06D9382B66CC00005227E1 /* Fixed.swift */; }; + 1A06D93B2B66CCAC005227E1 /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A06D93A2B66CCAC005227E1 /* Event.swift */; }; + 1A06D93D2B66CCDF005227E1 /* Screen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A06D93C2B66CCDF005227E1 /* Screen.swift */; }; + 1A06D93F2B66CCFF005227E1 /* ItemClick.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A06D93E2B66CCFF005227E1 /* ItemClick.swift */; }; + 1A06D9412B66DD69005227E1 /* DataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A06D9402B66DD69005227E1 /* DataSource.swift */; }; 1A0C3C192B388CC3008E5202 /* MetrycEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A0C3C182B388CC3008E5202 /* MetrycEvent.swift */; }; 1A14106A2ADD5185001D6DE6 /* ColorCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A1410692ADD5185001D6DE6 /* ColorCollectionViewCell.swift */; }; 1A1E94B52AD952EF00FE9A0C /* CustomTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A1E94B42AD952EF00FE9A0C /* CustomTableViewCell.swift */; }; @@ -36,6 +44,7 @@ 1A760A9D2B29EDC900B0C1CC /* EditTrackerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A760A9C2B29EDC900B0C1CC /* EditTrackerViewModel.swift */; }; 1A79C42D2B39C3E4009CE43A /* StatisticViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A79C42C2B39C3E4009CE43A /* StatisticViewModel.swift */; }; 1A79C42F2B39FBCC009CE43A /* UserDefaultKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A79C42E2B39FBCC009CE43A /* UserDefaultKeys.swift */; }; + 1A7D1BE72B668414007ECC7D /* Translate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A7D1BE62B668414007ECC7D /* Translate.swift */; }; 1A80951A2B3964A000591911 /* StatisticView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A8095192B3964A000591911 /* StatisticView.swift */; }; 1A83156E2B2C786000097DE7 /* HandlerResultType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A83156D2B2C786000097DE7 /* HandlerResultType.swift */; }; 1A84F3792AC87F4E001DD42F /* GeometricParams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A84F3782AC87F4E001DD42F /* GeometricParams.swift */; }; @@ -60,7 +69,7 @@ 1AAD15652AF14E220080EB72 /* UserDefaultsBacked.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AAD15642AF14E220080EB72 /* UserDefaultsBacked.swift */; }; 1AB0E14F2B036CE8007183D8 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1AB0E1512B036CE8007183D8 /* InfoPlist.strings */; }; 1AB78AA32AC750A900904AB2 /* TrackersCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AB78AA22AC750A900904AB2 /* TrackersCollectionViewCell.swift */; }; - 1ABABBF72AFC10AD007281E0 /* ConfigModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ABABBF62AFC10AD007281E0 /* ConfigModels.swift */; }; + 1ABABBF72AFC10AD007281E0 /* CustomCellModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ABABBF62AFC10AD007281E0 /* CustomCellModel.swift */; }; 1ABB3D2C2AC9AAB40037B601 /* HeaderReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ABB3D2B2AC9AAB40037B601 /* HeaderReusableView.swift */; }; 1AC642722B383AD50072E081 /* AnalyticsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AC642712B383AD50072E081 /* AnalyticsService.swift */; }; 1AC642742B38442E0072E081 /* EventAnalytics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AC642732B38442E0072E081 /* EventAnalytics.swift */; }; @@ -79,7 +88,8 @@ 1AE989322ACAF20500392315 /* TrackerRecordModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AE989312ACAF20500392315 /* TrackerRecordModel.swift */; }; 1AF05E062B2B011D0099D022 /* ShedulViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AF05E052B2B011D0099D022 /* ShedulViewModel.swift */; }; 767676C468C29BFDD74BC6BA /* Pods_TrackerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D9581169B12C04C00893FD1F /* Pods_TrackerTests.framework */; }; - A080B51F7DCAD954FB042507 /* Pods_Tracker.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 236701E2AE3D50DC6C3D59C3 /* Pods_Tracker.framework */; }; + 98B0393B47194B5A7BE9B9F0 /* Pods_Tracker.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C0F47A7130325894562A81CA /* Pods_Tracker.framework */; }; + A080B51F7DCAD954FB042507 /* (null) in Frameworks */ = {isa = PBXBuildFile; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -94,6 +104,14 @@ /* Begin PBXFileReference section */ 1A0077F72B2DD6DF000EAE69 /* CategoryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CategoryViewModel.swift; sourceTree = ""; }; + 1A06D9302B66B793005227E1 /* YandexApiKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YandexApiKey.swift; sourceTree = ""; }; + 1A06D9322B66B9EF005227E1 /* Onboarding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Onboarding.swift; sourceTree = ""; }; + 1A06D9342B66BA29005227E1 /* UpdateTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateTracker.swift; sourceTree = ""; }; + 1A06D9382B66CC00005227E1 /* Fixed.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Fixed.swift; sourceTree = ""; }; + 1A06D93A2B66CCAC005227E1 /* Event.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Event.swift; sourceTree = ""; }; + 1A06D93C2B66CCDF005227E1 /* Screen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Screen.swift; sourceTree = ""; }; + 1A06D93E2B66CCFF005227E1 /* ItemClick.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemClick.swift; sourceTree = ""; }; + 1A06D9402B66DD69005227E1 /* DataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataSource.swift; sourceTree = ""; }; 1A0C3C182B388CC3008E5202 /* MetrycEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetrycEvent.swift; sourceTree = ""; }; 1A1410692ADD5185001D6DE6 /* ColorCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorCollectionViewCell.swift; sourceTree = ""; }; 1A1E94B42AD952EF00FE9A0C /* CustomTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTableViewCell.swift; sourceTree = ""; }; @@ -123,6 +141,7 @@ 1A760A9C2B29EDC900B0C1CC /* EditTrackerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditTrackerViewModel.swift; sourceTree = ""; }; 1A79C42C2B39C3E4009CE43A /* StatisticViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatisticViewModel.swift; sourceTree = ""; }; 1A79C42E2B39FBCC009CE43A /* UserDefaultKeys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultKeys.swift; sourceTree = ""; }; + 1A7D1BE62B668414007ECC7D /* Translate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Translate.swift; sourceTree = ""; }; 1A8095192B3964A000591911 /* StatisticView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatisticView.swift; sourceTree = ""; }; 1A83156D2B2C786000097DE7 /* HandlerResultType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandlerResultType.swift; sourceTree = ""; }; 1A84F3782AC87F4E001DD42F /* GeometricParams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeometricParams.swift; sourceTree = ""; }; @@ -152,7 +171,7 @@ 1AB0E1502B036CE8007183D8 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 1AB0E1522B036CEF007183D8 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/InfoPlist.strings; sourceTree = ""; }; 1AB78AA22AC750A900904AB2 /* TrackersCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackersCollectionViewCell.swift; sourceTree = ""; }; - 1ABABBF62AFC10AD007281E0 /* ConfigModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigModels.swift; sourceTree = ""; }; + 1ABABBF62AFC10AD007281E0 /* CustomCellModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomCellModel.swift; sourceTree = ""; }; 1ABB3D2B2AC9AAB40037B601 /* HeaderReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderReusableView.swift; sourceTree = ""; }; 1AC642712B383AD50072E081 /* AnalyticsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsService.swift; sourceTree = ""; }; 1AC642732B38442E0072E081 /* EventAnalytics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventAnalytics.swift; sourceTree = ""; }; @@ -169,11 +188,11 @@ 1AE9892F2ACAF14B00392315 /* TrackerCategoryModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerCategoryModel.swift; sourceTree = ""; }; 1AE989312ACAF20500392315 /* TrackerRecordModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerRecordModel.swift; sourceTree = ""; }; 1AF05E052B2B011D0099D022 /* ShedulViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShedulViewModel.swift; sourceTree = ""; }; - 236701E2AE3D50DC6C3D59C3 /* Pods_Tracker.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Tracker.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 6682ACA7FBAC750BABF3A93E /* Pods-Tracker.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tracker.release.xcconfig"; path = "Target Support Files/Pods-Tracker/Pods-Tracker.release.xcconfig"; sourceTree = ""; }; 6E5A6EC6ED52C7BE406D080A /* Pods-TrackerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TrackerTests.release.xcconfig"; path = "Target Support Files/Pods-TrackerTests/Pods-TrackerTests.release.xcconfig"; sourceTree = ""; }; A4D50E97DFCE527E61A29054 /* Pods-Tracker.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tracker.debug.xcconfig"; path = "Target Support Files/Pods-Tracker/Pods-Tracker.debug.xcconfig"; sourceTree = ""; }; B99BCC20A3F3BF18F8D8A525 /* Pods-TrackerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TrackerTests.debug.xcconfig"; path = "Target Support Files/Pods-TrackerTests/Pods-TrackerTests.debug.xcconfig"; sourceTree = ""; }; + C0F47A7130325894562A81CA /* Pods_Tracker.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Tracker.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D9581169B12C04C00893FD1F /* Pods_TrackerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_TrackerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -182,7 +201,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - A080B51F7DCAD954FB042507 /* Pods_Tracker.framework in Frameworks */, + A080B51F7DCAD954FB042507 /* (null) in Frameworks */, + 98B0393B47194B5A7BE9B9F0 /* Pods_Tracker.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -311,9 +331,12 @@ 1AE989282ACAE13800392315 /* TrackerModel.swift */, 1AE9892F2ACAF14B00392315 /* TrackerCategoryModel.swift */, 1AE989312ACAF20500392315 /* TrackerRecordModel.swift */, - 1ABABBF62AFC10AD007281E0 /* ConfigModels.swift */, + 1ABABBF62AFC10AD007281E0 /* CustomCellModel.swift */, 1A66BB182B25D16500838F5F /* PinnTrackerModel.swift */, 1A0C3C182B388CC3008E5202 /* MetrycEvent.swift */, + 1A7D1BE62B668414007ECC7D /* Translate.swift */, + 1A06D9322B66B9EF005227E1 /* Onboarding.swift */, + 1A06D9342B66BA29005227E1 /* UpdateTracker.swift */, ); path = Models; sourceTree = ""; @@ -450,7 +473,13 @@ 1A6724122B2F108000719A9D /* FiltersState.swift */, 1AC642732B38442E0072E081 /* EventAnalytics.swift */, 1A79C42E2B39FBCC009CE43A /* UserDefaultKeys.swift */, + 1A06D9302B66B793005227E1 /* YandexApiKey.swift */, 1A92B6862B488439006C18C2 /* StatisticsEvent.swift */, + 1A06D9382B66CC00005227E1 /* Fixed.swift */, + 1A06D93A2B66CCAC005227E1 /* Event.swift */, + 1A06D93C2B66CCDF005227E1 /* Screen.swift */, + 1A06D93E2B66CCFF005227E1 /* ItemClick.swift */, + 1A06D9402B66DD69005227E1 /* DataSource.swift */, ); path = Enum; sourceTree = ""; @@ -478,8 +507,8 @@ 34047FD0212AA329047260EE /* Frameworks */ = { isa = PBXGroup; children = ( - 236701E2AE3D50DC6C3D59C3 /* Pods_Tracker.framework */, D9581169B12C04C00893FD1F /* Pods_TrackerTests.framework */, + C0F47A7130325894562A81CA /* Pods_Tracker.framework */, ); name = Frameworks; sourceTree = ""; @@ -496,14 +525,13 @@ 1A8A7FDE2AC55D960038CB42 /* Sources */, 1A8A7FDF2AC55D960038CB42 /* Frameworks */, 1A8A7FE02AC55D960038CB42 /* Resources */, + 298866F4B4E62114E795C7DB /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); dependencies = ( ); name = Tracker; - packageProductDependencies = ( - ); productName = Tracker; productReference = 1A8A7FE22AC55D970038CB42 /* Tracker.app */; productType = "com.apple.product-type.application"; @@ -614,6 +642,23 @@ shellPath = /bin/sh; shellScript = "echo \"Build Phase - Begin SwiftGen\"\n./runSwiftGenPlugin.sh\necho \"Build Phase - End SwiftGen\"\n"; }; + 298866F4B4E62114E795C7DB /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Tracker/Pods-Tracker-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Tracker/Pods-Tracker-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Tracker/Pods-Tracker-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; 780E122B20FF45050EAC7691 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -681,7 +726,7 @@ 1AE989322ACAF20500392315 /* TrackerRecordModel.swift in Sources */, 1A45A67A2ADE8EDB0063F08C /* TrackerCategoryStore.swift in Sources */, 1A6724132B2F108000719A9D /* FiltersState.swift in Sources */, - 1ABABBF72AFC10AD007281E0 /* ConfigModels.swift in Sources */, + 1ABABBF72AFC10AD007281E0 /* CustomCellModel.swift in Sources */, 1A32FF912ADC72880065E44C /* Header.swift in Sources */, 1A760A9D2B29EDC900B0C1CC /* EditTrackerViewModel.swift in Sources */, 1A80951A2B3964A000591911 /* StatisticView.swift in Sources */, @@ -689,14 +734,20 @@ 1A3B83AA2AD56323003C3897 /* NameEvent.swift in Sources */, 1A92B6872B488439006C18C2 /* StatisticsEvent.swift in Sources */, 1A45A6782ADE8E9E0063F08C /* TrackerStore.swift in Sources */, + 1A7D1BE72B668414007ECC7D /* Translate.swift in Sources */, + 1A06D9312B66B793005227E1 /* YandexApiKey.swift in Sources */, 1AE27C2D2B0604A800C4A925 /* Errors.swift in Sources */, 1AE989302ACAF14B00392315 /* TrackerCategoryModel.swift in Sources */, 1AC642722B383AD50072E081 /* AnalyticsService.swift in Sources */, 1A3ED44C2ACDDE4A0006EBBB /* ScheduleViewController.swift in Sources */, 1A2980BD2AC864CB007FE528 /* UIColor.swift in Sources */, + 1A06D93B2B66CCAC005227E1 /* Event.swift in Sources */, + 1A06D9412B66DD69005227E1 /* DataSource.swift in Sources */, 1AAD15652AF14E220080EB72 /* UserDefaultsBacked.swift in Sources */, 1A83156E2B2C786000097DE7 /* HandlerResultType.swift in Sources */, 1A14106A2ADD5185001D6DE6 /* ColorCollectionViewCell.swift in Sources */, + 1A06D93D2B66CCDF005227E1 /* Screen.swift in Sources */, + 1A06D9392B66CC00005227E1 /* Fixed.swift in Sources */, 1A8A576F2ACD65C400C060FA /* ChoiceParametrs.swift in Sources */, 1AE08AB82B2F5C250042CE59 /* FiltersViewModel.swift in Sources */, 1A4AA6D52B34BD8B00411C75 /* Colors.swift in Sources */, @@ -707,6 +758,7 @@ 1A6724112B2EBE3700719A9D /* FiltersViewController.swift in Sources */, 1A8ED50F2AEFEB5A00BA4EE4 /* OnboardingViewController.swift in Sources */, 1A272CE42B27922A00D39A44 /* PinnedCategoryStore.swift in Sources */, + 1A06D9352B66BA29005227E1 /* UpdateTracker.swift in Sources */, 1AD277712ACD4B1800F92219 /* UITextField.swift in Sources */, 1A0C3C192B388CC3008E5202 /* MetrycEvent.swift in Sources */, 1A52EFAF2ADC194C0002BD4B /* Date.swift in Sources */, @@ -719,10 +771,12 @@ 1AE989292ACAE13800392315 /* TrackerModel.swift in Sources */, 1AAC05C32ACC595800AA4B6B /* CreateTrackerViewController.swift in Sources */, 1A21DD952ADE5A6A00583A01 /* DaysValueTransformer.swift in Sources */, + 1A06D93F2B66CCFF005227E1 /* ItemClick.swift in Sources */, 1A2F9B652AC82FBF00914B60 /* EventSelectionViewController.swift in Sources */, 1A79C42F2B39FBCC009CE43A /* UserDefaultKeys.swift in Sources */, 1AA5B2372AD91D1800CB38A2 /* SelectCategoriesViewController.swift in Sources */, 1A79C42D2B39C3E4009CE43A /* StatisticViewModel.swift in Sources */, + 1A06D9332B66B9EF005227E1 /* Onboarding.swift in Sources */, 1A8A7FE82AC55D970038CB42 /* SceneDelegate.swift in Sources */, 1AE790232B399A1D00A2E9AB /* CGPoint.swift in Sources */, 1A27E6B92AD9871400ED3DF3 /* StringProtocol.swift in Sources */, diff --git a/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index e11d673..d5b3a8f 100644 --- a/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -3831,8 +3831,8 @@ filePath = "Tracker/Models/Enum/WeekDay.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "31" - endingLineNumber = "31" + startingLineNumber = "29" + endingLineNumber = "29" landmarkName = "day" landmarkType = "24"> @@ -4145,8 +4145,8 @@ filePath = "Tracker/Domain/Store/TrackerCategoryStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "43" - endingLineNumber = "43" + startingLineNumber = "42" + endingLineNumber = "42" landmarkName = "fetchedCategoryResultController" landmarkType = "24"> @@ -4493,8 +4493,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "141" - endingLineNumber = "141" + startingLineNumber = "168" + endingLineNumber = "168" landmarkName = "addNewTracker(_:nameCategory:)" landmarkType = "7"> @@ -4525,8 +4525,8 @@ filePath = "Tracker/Domain/Store/TrackerRecordStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "176" - endingLineNumber = "176" + startingLineNumber = "167" + endingLineNumber = "167" landmarkName = "deleteOrCreateTrackerRecord(id:date:)" landmarkType = "7"> @@ -4618,8 +4618,8 @@ filePath = "Tracker/Domain/Store/TrackerRecordStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "96" - endingLineNumber = "96" + startingLineNumber = "92" + endingLineNumber = "92" landmarkName = "addNewTrackerRecord(id:date:)" landmarkType = "7"> @@ -4837,8 +4837,8 @@ filePath = "Tracker/Domain/Store/TrackerCategoryStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "136" - endingLineNumber = "136" + startingLineNumber = "127" + endingLineNumber = "127" landmarkName = "controllerDidChangeContent(_:)" landmarkType = "7"> @@ -4853,8 +4853,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "225" - endingLineNumber = "225" + startingLineNumber = "223" + endingLineNumber = "223" landmarkName = "addPinnedCategory(_:pinnedCategory:)" landmarkType = "7"> @@ -5550,8 +5550,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "113" - endingLineNumber = "113" + startingLineNumber = "109" + endingLineNumber = "109" landmarkName = "searchTracker(id:)" landmarkType = "7"> @@ -5598,8 +5598,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "111" - endingLineNumber = "111" + startingLineNumber = "107" + endingLineNumber = "107" landmarkName = "searchTracker(id:)" landmarkType = "7"> @@ -5738,8 +5738,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "240" - endingLineNumber = "240" + startingLineNumber = "238" + endingLineNumber = "238" landmarkName = "controllerDidChangeContent(_:)" landmarkType = "7"> @@ -6109,177 +6109,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Void = { field in +struct AnalyticsService: AnalyticsServiceProtocol { + static func reportEvent(field: MetricEvent) { YMMYandexMetrica.reportEvent(field.event.rawValue, parameters: field.params, onFailure: { error in @@ -28,59 +21,3 @@ struct AnalyticsService { }) } } - -extension AnalyticsService: AnalyticsServiceProtocol { - func open() - { - let params = [EventFields.screen.rawValue: Screen.Main.rawValue] - let metricEvent = MetricEvent(event: .open, params: params) - handler(metricEvent) - } - - func close() - { - let params = [EventFields.screen.rawValue: Screen.Main.rawValue] - let metricEvent = MetricEvent(event: .close, params: params) - handler(metricEvent) - } - - func addTracker() - { - let params = [EventFields.screen.rawValue: Screen.Main.rawValue, - EventFields.item.rawValue: ItemClick.add_track.rawValue] - let metricEvent = MetricEvent(event: .click, params: params) - handler(metricEvent) - } - - func track() - { - let params = [EventFields.screen.rawValue: Screen.Main.rawValue, - EventFields.item.rawValue: ItemClick.track.rawValue] - let metricEvent = MetricEvent(event: .click, params: params) - handler(metricEvent) - } - - func edit() - { - let params = [EventFields.screen.rawValue: Screen.Main.rawValue, - EventFields.item.rawValue: ItemClick.edit.rawValue] - let metricEvent = MetricEvent(event: .click, params: params) - handler(metricEvent) - } - - func delete() - { - let params = [EventFields.screen.rawValue: Screen.Main.rawValue, - EventFields.item.rawValue: ItemClick.delete.rawValue] - let metricEvent = MetricEvent(event: .click, params: params) - handler(metricEvent) - } - - func filter() - { - let params = [EventFields.screen.rawValue: Screen.Main.rawValue, - EventFields.item.rawValue: ItemClick.filter.rawValue] - let metricEvent = MetricEvent(event: .click, params: params) - handler(metricEvent) - } -} diff --git a/Tracker/Application/AppDelegate.swift b/Tracker/Application/AppDelegate.swift index e8073ac..7193512 100644 --- a/Tracker/Application/AppDelegate.swift +++ b/Tracker/Application/AppDelegate.swift @@ -9,8 +9,6 @@ import UIKit import CoreData import YandexMobileMetrica -private let apiKeyYMM = "4a0432b5-adf6-45f2-800c-b160a4f7c44a" - @main class AppDelegate: UIResponder, UIApplicationDelegate { static var container: NSPersistentContainer { @@ -18,28 +16,25 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } func application(_ application: UIApplication, - didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool - { + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { DaysValueTransformer.register() - guard let configuration = YMMYandexMetricaConfiguration(apiKey: apiKeyYMM) else { return true } + guard let configuration = YMMYandexMetricaConfiguration(apiKey: YandexApiKey.apiKeyYMM) else { return true } YMMYandexMetrica.activate(with: configuration) return true } - + // MARK: UISceneSession Lifecycle - + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, - options: UIScene.ConnectionOptions) -> UISceneConfiguration - { + options: UIScene.ConnectionOptions) -> UISceneConfiguration { // Called when a new scene session is being created. // Use this method to select a configuration to create the new scene with. return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) } - + func application(_ application: UIApplication, - didDiscardSceneSessions sceneSessions: Set) - { + didDiscardSceneSessions sceneSessions: Set) { // Called when the user discards a scene session. // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. // Use this method to release any resources that were specific to the discarded scenes, as they will not return. @@ -57,7 +52,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { if let error = error as NSError? { // Replace this implementation with code to handle the error appropriately. // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. - + /* Typical reasons for an error here include: * The parent directory does not exist, cannot be created, or disallows writing. diff --git a/Tracker/Application/SceneDelegate.swift b/Tracker/Application/SceneDelegate.swift index 0c615cc..c719088 100644 --- a/Tracker/Application/SceneDelegate.swift +++ b/Tracker/Application/SceneDelegate.swift @@ -15,8 +15,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { func scene(_ scene: UIScene, willConnectTo session: UISceneSession, - options connectionOptions: UIScene.ConnectionOptions) - { + options connectionOptions: UIScene.ConnectionOptions) { guard let scene = (scene as? UIWindowScene) else { return } window = UIWindow(windowScene: scene) let tabBarViewController = TabBarController() @@ -25,8 +24,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { let onboardingVC = OnboardingPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal) let oneWisibleVc = OnboardingViewController() - let textLable = NSLocalizedString("textLableOneVC", comment: "") - let model = Onboarding(imageName: "onePage", textLable: textLable) + let model = Onboarding(imageName: "onePage", textLable: Translate.textLable) oneWisibleVc.config(model: model) onboardingVC.setViewControllers([oneWisibleVc], direction: .forward, animated: true) rootViewController = onboardingVC diff --git a/Tracker/Domain/Helpers/DaysValueTransformer.swift b/Tracker/Domain/Helpers/DaysValueTransformer.swift index 52f53b0..75fe5d7 100644 --- a/Tracker/Domain/Helpers/DaysValueTransformer.swift +++ b/Tracker/Domain/Helpers/DaysValueTransformer.swift @@ -12,22 +12,18 @@ final class DaysValueTransformer: ValueTransformer { override class func transformedValueClass() -> AnyClass { NSData.self } override class func allowsReverseTransformation() -> Bool { true } - static func register() - { - ValueTransformer.setValueTransformer(DaysValueTransformer(), - forName: NSValueTransformerName(rawValue: - String(describing: - DaysValueTransformer.self))) + static func register() { + ValueTransformer.setValueTransformer( + DaysValueTransformer(), + forName: NSValueTransformerName(rawValue: String(describing: DaysValueTransformer.self))) } - override func transformedValue(_ value: Any?) -> Any? - { + override func transformedValue(_ value: Any?) -> Any? { guard let days = value as? [WeekDay] else { return nil } return try? JSONEncoder().encode(days) } - override func reverseTransformedValue(_ value: Any?) -> Any? - { + override func reverseTransformedValue(_ value: Any?) -> Any? { guard let data = value as? NSData else { return nil } return try? JSONDecoder().decode([WeekDay].self, from: data as Data) } diff --git a/Tracker/Domain/Helpers/UIColorMarshalling.swift b/Tracker/Domain/Helpers/UIColorMarshalling.swift index 87b5c89..8f0a478 100644 --- a/Tracker/Domain/Helpers/UIColorMarshalling.swift +++ b/Tracker/Domain/Helpers/UIColorMarshalling.swift @@ -20,7 +20,7 @@ final class UIColorMarshalling { lroundf(Float(b * 255)) ) } - + func color(from hex: String) -> UIColor { var rgbValue:UInt64 = 0 Scanner(string: hex).scanHexInt64(&rgbValue) diff --git a/Tracker/Domain/Store/PinnedCategoryStore.swift b/Tracker/Domain/Store/PinnedCategoryStore.swift index 025a4c6..99797e5 100644 --- a/Tracker/Domain/Store/PinnedCategoryStore.swift +++ b/Tracker/Domain/Store/PinnedCategoryStore.swift @@ -10,6 +10,8 @@ import CoreData protocol PinnedCategoryStoreProtocol { func addPinnedCategory(_ nameCategory: String) -> Result + func editPinnedCategory(_ id: UUID, newPinnedCat: String) -> Result + func getPinnedCategory(_ id: UUID) -> Result func deleteAndGetPinnedCategory(_ id: UUID) -> Result } @@ -34,8 +36,7 @@ final class PinnedCategoryStore: NSObject { } private extension PinnedCategoryStore { - func save() -> Result - { + func save() -> Result { do { try context.save() return .success(()) @@ -44,8 +45,7 @@ private extension PinnedCategoryStore { } } - func searchCategoryById(id: UUID) throws -> PinnCategoryCoreData? - { + func searchCategoryById(id: UUID) throws -> PinnCategoryCoreData? { let request = NSFetchRequest(entityName: "\(PinnCategoryCoreData.self)") request.returnsObjectsAsFaults = false guard let keyPath = (\PinnCategoryCoreData.trackerId)._kvcKeyPathString @@ -57,8 +57,7 @@ private extension PinnedCategoryStore { //MARK: - PinnedCategoryStoreProtocol extension PinnedCategoryStore: PinnedCategoryStoreProtocol { - func addPinnedCategory(_ nameCategory: String) -> Result - { + func addPinnedCategory(_ nameCategory: String) -> Result { let pinnCategoryCoreData = PinnCategoryCoreData(context: context) pinnCategoryCoreData.nameCategory = nameCategory do { @@ -68,8 +67,31 @@ extension PinnedCategoryStore: PinnedCategoryStoreProtocol { catch { return .failure(error) } } - func deleteAndGetPinnedCategory(_ id: UUID) -> Result - { + func editPinnedCategory(_ id: UUID, newPinnedCat: String) -> Result { + do { + if let pinnCategoryCoreData = try searchCategoryById(id: id) { + pinnCategoryCoreData.nameCategory = newPinnedCat + return save() + } + } catch { + return .failure(error) + } + return .failure(StoreErrors.TrackrerStoreError.getTrackerError) + } + + func getPinnedCategory(_ id: UUID) -> Result { + do { + if let pinnCategoryCoreData = try searchCategoryById(id: id) { + let nameCategory = pinnCategoryCoreData.nameCategory + return .success(nameCategory) + } + } catch { + return .failure(error) + } + return .failure(StoreErrors.TrackrerStoreError.getTrackerError) + } + + func deleteAndGetPinnedCategory(_ id: UUID) -> Result { do { if let pinnCategoryCoreData = try searchCategoryById(id: id) { let nameCategory = pinnCategoryCoreData.nameCategory diff --git a/Tracker/Domain/Store/TrackerCategoryStore.swift b/Tracker/Domain/Store/TrackerCategoryStore.swift index 368bfc8..53788b4 100644 --- a/Tracker/Domain/Store/TrackerCategoryStore.swift +++ b/Tracker/Domain/Store/TrackerCategoryStore.swift @@ -23,7 +23,6 @@ protocol TrackerCategoryStoreDelegate: AnyObject { //MARK: - TrackerCategoryStore final class TrackerCategoryStore: NSObject { - private let textFixed = NSLocalizedString("textFixed", comment: "") weak var delegate: TrackerCategoryStoreDelegate? @UserDefaultsBacked(key: UserDefaultKeys.isTracker.rawValue) @@ -57,8 +56,7 @@ final class TrackerCategoryStore: NSObject { } private extension TrackerCategoryStore { - func save() -> Result - { + func save() -> Result { do { try context.save() return .success(()) @@ -67,8 +65,7 @@ private extension TrackerCategoryStore { } } - func searchTrackerCategoryCD(nameCategory: String) throws -> TrackerCategoryCoreData? - { + func searchTrackerCategoryCD(nameCategory: String) throws -> TrackerCategoryCoreData? { let request = NSFetchRequest(entityName: "\(TrackerCategoryCoreData.self)") request.returnsObjectsAsFaults = false request.predicate = NSPredicate(format: "%K == %@", #keyPath(TrackerCategoryCoreData.nameCategory), @@ -76,8 +73,7 @@ private extension TrackerCategoryStore { return try context.fetch(request).first } - func update(trackerCategoryCD: TrackerCategoryCoreData, newNameCategory: String) -> Result - { + func update(trackerCategoryCD: TrackerCategoryCoreData, newNameCategory: String) -> Result { trackerCategoryCD.nameCategory = newNameCategory return save() } @@ -85,23 +81,20 @@ private extension TrackerCategoryStore { //MARK: - TrackerCategoryStoreProtocol extension TrackerCategoryStore: TrackerCategoryStoreProtocol { - func addCategory(_ nameCategory: String) -> Result - { + func addCategory(_ nameCategory: String) -> Result { let categoryCoreData = TrackerCategoryCoreData(context: context) categoryCoreData.nameCategory = nameCategory - categoryCoreData.isPinned = nameCategory == textFixed ? false : true + categoryCoreData.isPinned = nameCategory == Translate.textFixed ? false : true return save() } - func getListTrackerCategoryCoreData() -> [TrackerCategoryCoreData]? - { + func getListTrackerCategoryCoreData() -> [TrackerCategoryCoreData]? { let trackCatCD = fetchedCategoryResultController.fetchedObjects isTracker = trackCatCD?.first { $0.trakers?.count ?? 0 > 0 } == nil return trackCatCD } - func deleteCategory(nameCategory: String) -> Result - { + func deleteCategory(nameCategory: String) -> Result { do { if let category = try searchTrackerCategoryCD(nameCategory: nameCategory) { context.delete(category) @@ -114,8 +107,7 @@ extension TrackerCategoryStore: TrackerCategoryStoreProtocol { } func updateNameCategory(newNameCategory: String, - oldNameCategory: String) -> Result - { + oldNameCategory: String) -> Result { do { if let trackCatCD = try searchTrackerCategoryCD(nameCategory: oldNameCategory) { return update(trackerCategoryCD: trackCatCD, newNameCategory: newNameCategory) @@ -129,8 +121,7 @@ extension TrackerCategoryStore: TrackerCategoryStoreProtocol { //MARK: - NSFetchedResultsControllerDelegate extension TrackerCategoryStore: NSFetchedResultsControllerDelegate { - func controllerDidChangeContent(_ controller: NSFetchedResultsController) - { + func controllerDidChangeContent(_ controller: NSFetchedResultsController) { guard let delegate else { return } delegate.storeCategory(trackerCategoryStore: self) diff --git a/Tracker/Domain/Store/TrackerRecordStore.swift b/Tracker/Domain/Store/TrackerRecordStore.swift index 9f89f55..cf19f37 100644 --- a/Tracker/Domain/Store/TrackerRecordStore.swift +++ b/Tracker/Domain/Store/TrackerRecordStore.swift @@ -57,8 +57,7 @@ final class TrackerRecordStore: NSObject { //MARK: - private extension private extension TrackerRecordStore { - func save() -> Result - { + func save() -> Result { do { try context.save() return .success(()) @@ -67,8 +66,7 @@ private extension TrackerRecordStore { } } - func trackerRecord(from trackerRecordCoreDate: TrackerRecordCoreData) throws -> TrackerRecord - { + func trackerRecord(from trackerRecordCoreDate: TrackerRecordCoreData) throws -> TrackerRecord { guard let id = trackerRecordCoreDate.trackerId else { throw StoreErrors.TrackrerRecordStoreError.decodingErrorInvalidId } @@ -82,14 +80,12 @@ private extension TrackerRecordStore { func updateExistingTrackerRecord(_ trackerRecordCoreData: TrackerRecordCoreData, id: UUID, - date: Date) - { + date: Date) { trackerRecordCoreData.trackerId = id trackerRecordCoreData.date = date } - func addNewTrackerRecord(id: UUID, date: Date) -> Result - { + func addNewTrackerRecord(id: UUID, date: Date) -> Result { let trackerRecordCoreData = TrackerRecordCoreData(context: context) updateExistingTrackerRecord(trackerRecordCoreData, id: id, date: date) @@ -114,8 +110,7 @@ private extension TrackerRecordStore { return trackerRecord.first } - func getLisTrackersRecord(id: UUID) throws -> [TrackerRecordCoreData] - { + func getLisTrackersRecord(id: UUID) throws -> [TrackerRecordCoreData] { let request = NSFetchRequest(entityName: "\(TrackerRecordCoreData.self)") request.returnsObjectsAsFaults = false request.predicate = NSPredicate(format: "%K == %@", @@ -127,8 +122,7 @@ private extension TrackerRecordStore { //MARK: - TrackerRecordStoreProtocol extension TrackerRecordStore: TrackerRecordStoreProtocol { - func treckersRecordsResult() -> Result, Error> - { + func treckersRecordsResult() -> Result, Error> { let trackerRecordsCoreData = fetchedTrackerRecordResultController.fetchedObjects guard let trackerRecordsCoreData else { return .failure(StoreErrors.TrackrerRecordStoreError.loadTrackerRecord) } @@ -140,8 +134,7 @@ extension TrackerRecordStore: TrackerRecordStoreProtocol { } } - func deleteTrackerRecord(id: UUID) -> Result - { + func deleteTrackerRecord(id: UUID) -> Result { do { let trackerRecord = try getLisTrackersRecord(id: id) trackerRecord.forEach { context.delete($0) } @@ -153,8 +146,7 @@ extension TrackerRecordStore: TrackerRecordStoreProtocol { return save() } - func deleteTrackerRecords(trackers: [Tracker]) -> Result - { + func deleteTrackerRecords(trackers: [Tracker]) -> Result { do { try trackers.forEach { let trackerRecords = try getLisTrackersRecord(id: $0.id) @@ -167,8 +159,7 @@ extension TrackerRecordStore: TrackerRecordStoreProtocol { return save() } - func deleteOrCreateTrackerRecord(id: UUID, date: Date) -> Result - { + func deleteOrCreateTrackerRecord(id: UUID, date: Date) -> Result { flag = true do { if let trackerRecord = try searchTrackerRecordForDate(id: id, @@ -183,8 +174,7 @@ extension TrackerRecordStore: TrackerRecordStoreProtocol { } } - func getCountTrackerRecord(id: UUID) -> Result - { + func getCountTrackerRecord(id: UUID) -> Result { do { let countTrackers = try getLisTrackersRecord(id: id).count return .success(countTrackers) @@ -197,8 +187,7 @@ extension TrackerRecordStore: TrackerRecordStoreProtocol { fetchedTrackerRecordResultController.fetchedObjects?.count } - func getIsTrackerRecord(id: UUID, date: Date) throws -> Bool - { + func getIsTrackerRecord(id: UUID, date: Date) throws -> Bool { do { let tracker = try searchTrackerRecordForDate(id: id, date: date) return tracker != nil @@ -210,8 +199,7 @@ extension TrackerRecordStore: TrackerRecordStoreProtocol { //MARK: - NSFetchedResultsControllerDelegate extension TrackerRecordStore: NSFetchedResultsControllerDelegate { - func controllerDidChangeContent(_ controller: NSFetchedResultsController) - { + func controllerDidChangeContent(_ controller: NSFetchedResultsController) { guard let delegate else { return } delegate.trackerRecordStore(trackerRecordStore: self, flag: flag) } diff --git a/Tracker/Domain/Store/TrackerStore.swift b/Tracker/Domain/Store/TrackerStore.swift index 277fc24..efec79f 100644 --- a/Tracker/Domain/Store/TrackerStore.swift +++ b/Tracker/Domain/Store/TrackerStore.swift @@ -12,7 +12,7 @@ import CoreData protocol TrackerStoreProtocol { func addNewTracker(_ tracker: Tracker, nameCategory: String) -> Result func getTrackers(_ objects: [TrackerCoreData]) -> Result<[Tracker], Error> - func updateTracker(tracker: Tracker, nameCategory: String) -> Result + func updateTracker(tracker: Tracker, nameCategory: String?) -> Result func deleteTracker(_ id: UUID) -> Result func deleteTrackers(trackers: [Tracker]) -> Result func addPinnedCategory(_ id: UUID, pinnedCategory: PinnCategoryCoreData) -> Result @@ -56,8 +56,7 @@ final class TrackerStore: NSObject { //MARK: - Private extension private extension TrackerStore { - func tracker(from trackerCoreData: TrackerCoreData) throws -> Tracker - { + func tracker(from trackerCoreData: TrackerCoreData) throws -> Tracker { guard let id = trackerCoreData.id else { assertionFailure("not found ID") throw StoreErrors.TrackrerStoreError.decodingErrorInvalidId @@ -86,8 +85,7 @@ private extension TrackerStore { schedule: schedul) } - func updateExistingTrackerRecord(trackerCoreData: TrackerCoreData, tracker: Tracker) - { + func updateExistingTrackerRecord(trackerCoreData: TrackerCoreData, tracker: Tracker) { trackerCoreData.id = tracker.id trackerCoreData.colorHex = colorMarshalling.hexString(from: tracker.color) trackerCoreData.emoji = tracker.emoji @@ -95,8 +93,7 @@ private extension TrackerStore { trackerCoreData.schedule = tracker.schedule as NSObject } - func save() -> Result - { + func save() -> Result { do { try context.save() return .success(()) @@ -105,8 +102,7 @@ private extension TrackerStore { } } - func searchTracker(id: UUID) throws -> TrackerCoreData? - { + func searchTracker(id: UUID) throws -> TrackerCoreData? { let request = NSFetchRequest(entityName: "\(TrackerCoreData.self)") request.returnsObjectsAsFaults = false guard let keyPath = (\TrackerCoreData.id)._kvcKeyPathString @@ -116,8 +112,7 @@ private extension TrackerStore { return trackerCD.first } - func searchCategory(name: String) throws -> TrackerCategoryCoreData? - { + func searchCategory(name: String) throws -> TrackerCategoryCoreData? { let request = NSFetchRequest(entityName: "\(TrackerCategoryCoreData.self)") request.returnsObjectsAsFaults = false request.predicate = NSPredicate(format: "%K == %@", @@ -125,13 +120,45 @@ private extension TrackerStore { name as CVarArg) return try context.fetch(request).first } + + func updateTrackerAndCategory(tracker: Tracker, nameCategory: String) -> Result { + do { + if let categoryCD = try searchCategory(name: nameCategory) { + do { + if let trackerCD = try searchTracker(id: tracker.id) { + trackerCD.category = categoryCD + updateExistingTrackerRecord(trackerCoreData: trackerCD, + tracker: tracker) + return save() + } + } catch { + return .failure(error) + } + } + } catch { + return .failure(error) + } + return save() + } + + func updateTracker(tracker: Tracker) -> Result { + do { + if let trackerCD = try searchTracker(id: tracker.id) { + updateExistingTrackerRecord(trackerCoreData: trackerCD, + tracker: tracker) + return save() + } + } catch { + return .failure(error) + } + return .failure(StoreErrors.TrackrerStoreError.getTrackerError) + } } //MARK: - TrackerStoreProtocol extension TrackerStore: TrackerStoreProtocol { func addNewTracker(_ tracker: Tracker, - nameCategory: String) -> Result - { + nameCategory: String) -> Result { do { if let category = try searchCategory(name: nameCategory) { let trackerCoreData = TrackerCoreData(context: context) @@ -146,43 +173,17 @@ extension TrackerStore: TrackerStoreProtocol { return save() } - func updateTracker(tracker: Tracker, - nameCategory: String) -> Result - { + func updateTracker(tracker: Tracker, nameCategory: String?) -> Result { do { - if let categoryCD = try searchCategory(name: nameCategory) { - do { - if let trackerCD = try searchTracker(id: tracker.id) { - trackerCD.category = categoryCD - updateExistingTrackerRecord(trackerCoreData: trackerCD, - tracker: tracker) - return save() - } - } catch { - return .failure(error) - } + if let nameCategory { + return updateTrackerAndCategory(tracker: tracker, nameCategory: nameCategory) } else { - let catCD = TrackerCategoryCoreData(context: context) - catCD.nameCategory = nameCategory - do { - if let trackerCD = try searchTracker(id: tracker.id) { - trackerCD.category = catCD - updateExistingTrackerRecord(trackerCoreData: trackerCD, - tracker: tracker) - return save() - } - } catch { - return .failure(error) - } + return updateTracker(tracker: tracker) } - } catch { - return .failure(error) } - return save() } - func deleteTracker(_ id: UUID) -> Result - { + func deleteTracker(_ id: UUID) -> Result { do { if let trackerCoreData = try searchTracker(id: id) { context.delete(trackerCoreData) @@ -194,8 +195,7 @@ extension TrackerStore: TrackerStoreProtocol { return save() } - func deleteTrackers(trackers: [Tracker]) -> Result - { + func deleteTrackers(trackers: [Tracker]) -> Result { do { try trackers .forEach { if let tracker = try searchTracker(id: $0.id) { context.delete(tracker) @@ -207,8 +207,7 @@ extension TrackerStore: TrackerStoreProtocol { } } - func getTrackers(_ objects: [TrackerCoreData]) -> Result<[Tracker], Error> - { + func getTrackers(_ objects: [TrackerCoreData]) -> Result<[Tracker], Error> { let _ = fetchedTrackerResultController do { let trackers = try objects.map({ try tracker(from: $0) }).sorted { $0.name < $1.name } @@ -218,8 +217,7 @@ extension TrackerStore: TrackerStoreProtocol { } } - func addPinnedCategory(_ id: UUID, pinnedCategory: PinnCategoryCoreData) -> Result - { + func addPinnedCategory(_ id: UUID, pinnedCategory: PinnCategoryCoreData) -> Result { do { if let trackerCD = try searchTracker(id: id) { pinnedCategory.addToTracker(trackerCD) diff --git a/Tracker/Extension/UITextField.swift b/Tracker/Extension/UITextField.swift index c585fb9..cf9f281 100644 --- a/Tracker/Extension/UITextField.swift +++ b/Tracker/Extension/UITextField.swift @@ -8,8 +8,7 @@ import UIKit extension UITextField { - func indent(size:CGFloat) - { + func indent(size:CGFloat) { self.leftView = UIView(frame: CGRect(x: self.frame.minX, y: self.frame.minY, width: size, diff --git a/Tracker/Helpers/HandlerResultType.swift b/Tracker/Helpers/HandlerResultType.swift index b55acf6..c1caee6 100644 --- a/Tracker/Helpers/HandlerResultType.swift +++ b/Tracker/Helpers/HandlerResultType.swift @@ -10,8 +10,7 @@ import UIKit final class HandlerResultType { func resultTypeHandler(_ listCategory: Result, vc: UIViewController, - handler: (Value) -> Void) - { + handler: (Value) -> Void) { switch listCategory { case .success(let newValue): handler(newValue) @@ -22,8 +21,7 @@ final class HandlerResultType { } func resultTypeHandlerGetValue(_ value: Result, - vc: UIViewController) -> Value? - { + vc: UIViewController) -> Value? { switch value { case .success(let newValue): return newValue @@ -34,8 +32,7 @@ final class HandlerResultType { } } - private func showMessageErrorAlert(message: String, viewController: UIViewController) - { + private func showMessageErrorAlert(message: String, viewController: UIViewController) { let alert = UIAlertController(title: nil, message: message, preferredStyle: .alert) diff --git a/Tracker/Models/ConfigModels.swift b/Tracker/Models/ConfigModels.swift deleted file mode 100644 index 615324e..0000000 --- a/Tracker/Models/ConfigModels.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// ConfigModels.swift -// Tracker -// -// Created by Григорий Машук on 8.11.23. -// - -import UIKit - -struct CustomCellModel { - let text: String - let color: UIColor -} - -struct Onboarding { - let imageName: String - let textLable: String -} - -struct UpdateTracker { - let count: Int - let flag: Bool -} - -struct StatisticViewModel { - let item: Int - let secondaryText: String -} diff --git a/Tracker/Models/CustomCellModel.swift b/Tracker/Models/CustomCellModel.swift new file mode 100644 index 0000000..b026979 --- /dev/null +++ b/Tracker/Models/CustomCellModel.swift @@ -0,0 +1,15 @@ +// +// CustomCellModel.swift +// Tracker +// +// Created by Григорий Машук on 8.11.23. +// + +import UIKit + +struct CustomCellModel { + let text: String + let color: UIColor +} + + diff --git a/Tracker/Models/Enum/ChoiceParametrs.swift b/Tracker/Models/Enum/ChoiceParametrs.swift index cb54cef..8f25905 100644 --- a/Tracker/Models/Enum/ChoiceParametrs.swift +++ b/Tracker/Models/Enum/ChoiceParametrs.swift @@ -5,8 +5,6 @@ // Created by Григорий Машук on 4.10.23. // -import Foundation - enum ChoiceParametrs: String { case category case schedule @@ -15,9 +13,9 @@ enum ChoiceParametrs: String { var name: String switch self { case .category: - name = NSLocalizedString(self.rawValue, comment: "") + name = Translate.category case .schedule: - name = NSLocalizedString(self.rawValue, comment: "") + name = Translate.schedule } return name } diff --git a/Tracker/Models/Enum/DataSource.swift b/Tracker/Models/Enum/DataSource.swift new file mode 100644 index 0000000..e8b0d31 --- /dev/null +++ b/Tracker/Models/Enum/DataSource.swift @@ -0,0 +1,23 @@ +// +// DataSours.swift +// Tracker +// +// Created by Григорий Машук on 28.01.24. +// + +import UIKit + +enum DataSource { + static let emojies: [String] = ["🙂", "😻", "🌺", "🐶", "❤️", "😱", "😇", "😡", "🥶", + "🤔", "🙌", "🍔", "🥦", "🏓", "🥇", "🎸", "🏝️", "😪"] + + static let colors: [UIColor] = [.colorSelection1, .colorSelection2, .colorSelection3,.colorSelection4, + .colorSelection5, .colorSelection6, .colorSelection7, .colorSelection8, + .colorSelection9, .colorSelection10, .colorSelection11, .colorSelection12, + .colorSelection13, .colorSelection14, .colorSelection15, .colorSelection16, + .colorSelection17, .colorSelection18] + + static let dataSection: [Header] = [.emoji, .color] + + static let regular: [WeekDay] = [.monday, .tuesday, .wednesday, .thursday, .friday, .saturday, .sunday] +} diff --git a/Tracker/Models/Enum/Errors.swift b/Tracker/Models/Enum/Errors.swift index de21c2a..0162dc1 100644 --- a/Tracker/Models/Enum/Errors.swift +++ b/Tracker/Models/Enum/Errors.swift @@ -5,7 +5,6 @@ // Created by Григорий Машук on 16.11.23. // -import Foundation enum StoreErrors { enum TrackrerCategoryStoreError: Error { case decodingErrorInvalidNameCategory @@ -15,9 +14,9 @@ enum StoreErrors { var localizedDescription = "" switch self { case .decodingErrorInvalidNameCategory: - localizedDescription = NSLocalizedString("decodingErrorInvalidNameCategory", comment: "") + localizedDescription = Translate.decodingErrorInvalidNameCategory case .getCategoryCoreDataError: - localizedDescription = NSLocalizedString("getCategoryCoreDataError", comment: "") + localizedDescription = Translate.getCategoryCoreDataError } return localizedDescription } @@ -35,17 +34,17 @@ enum StoreErrors { var localizedDescription = "" switch self { case .getTrackerError: - localizedDescription = NSLocalizedString("getTrackerError", comment: "") + localizedDescription = Translate.getTrackerError case .decodingErrorInvalidName: - localizedDescription = NSLocalizedString("decodingErrorInvalidName", comment: "") + localizedDescription = Translate.decodingErrorInvalidName case .decodingErrorInvalidId: - localizedDescription = NSLocalizedString("decodingErrorInvalidId", comment: "") + localizedDescription = Translate.decodingErrorInvalidId case .decodingErrorInvalidColor: - localizedDescription = NSLocalizedString("decodingErrorInvalidColor", comment: "") + localizedDescription = Translate.decodingErrorInvalidColor case .decodingErrorInvalidEmoji: - localizedDescription = NSLocalizedString("decodingErrorInvalidEmoji", comment: "") + localizedDescription = Translate.decodingErrorInvalidEmoji case .decodingErrorInvalidSchedul: - localizedDescription = NSLocalizedString("decodingErrorInvalidSchedul", comment: "") + localizedDescription = Translate.decodingErrorInvalidSchedul } return localizedDescription @@ -56,7 +55,7 @@ enum StoreErrors { case transformationErrorInvalid var localizedDescription: String { - NSLocalizedString("transformationErrorInvalid", comment: "") + Translate.transformationErrorInvalid } } @@ -70,13 +69,13 @@ enum StoreErrors { var localizedDescription = "" switch self { case .decodingErrorInvalidId: - localizedDescription = NSLocalizedString(self.rawValue, comment: "") + localizedDescription = Translate.decodingErrorInvalidId case .decodingErrorInvalidDate: - localizedDescription = NSLocalizedString(self.rawValue, comment: "") + localizedDescription = Translate.decodingErrorInvalidDate case .loadTrackerRecord: - localizedDescription = NSLocalizedString(self.rawValue, comment: "") + localizedDescription = Translate.loadTrackerRecord case .getTrackerRecord: - localizedDescription = NSLocalizedString(self.rawValue, comment: "") + localizedDescription = Translate.getTrackerRecord } return localizedDescription diff --git a/Tracker/Models/Enum/Event.swift b/Tracker/Models/Enum/Event.swift new file mode 100644 index 0000000..72af584 --- /dev/null +++ b/Tracker/Models/Enum/Event.swift @@ -0,0 +1,12 @@ +// +// Event.swift +// Tracker +// +// Created by Григорий Машук on 28.01.24. +// + +enum Event: String { + case open + case close + case click +} diff --git a/Tracker/Models/Enum/EventAnalytics.swift b/Tracker/Models/Enum/EventAnalytics.swift index 18bddc7..31028ad 100644 --- a/Tracker/Models/Enum/EventAnalytics.swift +++ b/Tracker/Models/Enum/EventAnalytics.swift @@ -1,32 +1,19 @@ // -// EventFields.swift +// EventAnalytics.swift // Tracker // // Created by Григорий Машук on 24.12.23. // -import Foundation -enum EventFields: String { +enum EventAnalytics: String { case event case screen case item } -enum Event: String { - case open - case close - case click -} -enum Screen: String { - case Main -} -enum ItemClick: String { - case add_track - case track - case filter - case edit - case delete -} + + + diff --git a/Tracker/Models/Enum/FiltersState.swift b/Tracker/Models/Enum/FiltersState.swift index 5ff6d5e..32314c1 100644 --- a/Tracker/Models/Enum/FiltersState.swift +++ b/Tracker/Models/Enum/FiltersState.swift @@ -5,8 +5,6 @@ // Created by Григорий Машук on 17.12.23. // -import Foundation - enum FiltersState: String { case allTrackers case toDayTrackers @@ -17,13 +15,13 @@ enum FiltersState: String { var name = "" switch self { case .allTrackers: - name = NSLocalizedString(self.rawValue, comment: "") + name = Translate.allTrackers case .toDayTrackers: - name = NSLocalizedString(self.rawValue, comment: "") + name = Translate.toDayTrackers case .completed: - name = NSLocalizedString(self.rawValue, comment: "") + name = Translate.completed case .notCompleted: - name = NSLocalizedString(self.rawValue, comment: "") + name = Translate.notCompleted } return name } diff --git a/Tracker/Models/Enum/Fixed.swift b/Tracker/Models/Enum/Fixed.swift new file mode 100644 index 0000000..ece941f --- /dev/null +++ b/Tracker/Models/Enum/Fixed.swift @@ -0,0 +1,11 @@ +// +// Fixed.swift +// Tracker +// +// Created by Григорий Машук on 28.01.24. +// + +enum Fixed: String { + case fixedEng = "Fixed" + case fixedRu = "Закрепленные" +} diff --git a/Tracker/Models/Enum/Header.swift b/Tracker/Models/Enum/Header.swift index 726d1c5..1fb6c80 100644 --- a/Tracker/Models/Enum/Header.swift +++ b/Tracker/Models/Enum/Header.swift @@ -5,19 +5,17 @@ // Created by Григорий Машук on 15.10.23. // -import Foundation - -enum Header { +enum Header: String { case color - case emoji + case emoji = "Emoji" var name: String { var name: String switch self { case .color: - name = NSLocalizedString("stringColor", comment: "") + name = Translate.colorHeader case .emoji: - name = "Emoji" + name = self.rawValue } return name } diff --git a/Tracker/Models/Enum/ItemClick.swift b/Tracker/Models/Enum/ItemClick.swift new file mode 100644 index 0000000..9138a71 --- /dev/null +++ b/Tracker/Models/Enum/ItemClick.swift @@ -0,0 +1,14 @@ +// +// ItemClick.swift +// Tracker +// +// Created by Григорий Машук on 28.01.24. +// + +enum ItemClick: String { + case add_track + case track + case filter + case edit + case delete +} diff --git a/Tracker/Models/Enum/NameEvent.swift b/Tracker/Models/Enum/NameEvent.swift index 168806a..fee12ff 100644 --- a/Tracker/Models/Enum/NameEvent.swift +++ b/Tracker/Models/Enum/NameEvent.swift @@ -5,8 +5,6 @@ // Created by Григорий Машук on 10.10.23. // -import Foundation - enum NameEvent { case habit case irregularEvent @@ -15,9 +13,9 @@ enum NameEvent { var name: String switch self { case .habit: - name = NSLocalizedString("nameHabit", comment: "") + name = Translate.nameHabit case .irregularEvent: - name = NSLocalizedString("nameIrregularEvent", comment: "") + name = Translate.nameIrregularEvent } return name } diff --git a/Tracker/Models/Enum/Screen.swift b/Tracker/Models/Enum/Screen.swift new file mode 100644 index 0000000..1c1534a --- /dev/null +++ b/Tracker/Models/Enum/Screen.swift @@ -0,0 +1,10 @@ +// +// Screen.swift +// Tracker +// +// Created by Григорий Машук on 28.01.24. +// + +enum Screen: String { + case Main +} diff --git a/Tracker/Models/Enum/StatisticsEvent.swift b/Tracker/Models/Enum/StatisticsEvent.swift index 2326846..c03a3f7 100644 --- a/Tracker/Models/Enum/StatisticsEvent.swift +++ b/Tracker/Models/Enum/StatisticsEvent.swift @@ -5,8 +5,6 @@ // Created by Григорий Машук on 5.01.24. // -import Foundation - enum StatisticsEvent: String { case bestPeriod case perfectDays @@ -17,13 +15,13 @@ enum StatisticsEvent: String { var statisticName = "" switch self { case .bestPeriod: - statisticName = NSLocalizedString(self.rawValue, comment: "") + statisticName = Translate.bestPeriod case .perfectDays: - statisticName = NSLocalizedString(self.rawValue, comment: "") + statisticName = Translate.perfectDays case .trackersCompleted: - statisticName = NSLocalizedString(self.rawValue, comment: "") + statisticName = Translate.trackersCompleted case .averageValue: - statisticName = NSLocalizedString(self.rawValue, comment: "") + statisticName = Translate.averageValue } return statisticName diff --git a/Tracker/Models/Enum/UserDefaultKeys.swift b/Tracker/Models/Enum/UserDefaultKeys.swift index 6861a6e..cf144b6 100644 --- a/Tracker/Models/Enum/UserDefaultKeys.swift +++ b/Tracker/Models/Enum/UserDefaultKeys.swift @@ -5,8 +5,6 @@ // Created by Григорий Машук on 25.12.23. // -import Foundation - enum UserDefaultKeys: String { case isTracker case selectNameCategory @@ -14,7 +12,4 @@ enum UserDefaultKeys: String { case isOnboarding } -enum Fixed: String { - case fixedEng = "Fixed" - case fixedRu = "Закрепленные" -} + diff --git a/Tracker/Models/Enum/WeekDay.swift b/Tracker/Models/Enum/WeekDay.swift index 2d68e20..700a92e 100644 --- a/Tracker/Models/Enum/WeekDay.swift +++ b/Tracker/Models/Enum/WeekDay.swift @@ -5,8 +5,6 @@ // Created by Григорий Машук on 2.10.23. // -import Foundation - enum WeekDay: Int { case monday = 1 case tuesday @@ -20,19 +18,19 @@ enum WeekDay: Int { var day: String switch self { case .monday: - day = NSLocalizedString("monday.complete", comment: "") + day = Translate.monday case .tuesday: - day = NSLocalizedString("tuesday.complete", comment: "") + day = Translate.tuesday case .wednesday: - day = NSLocalizedString("wednesday.complete", comment: "") + day = Translate.wednesday case .thursday: - day = NSLocalizedString("thursday.complete", comment: "") + day = Translate.thursday case .friday: - day = NSLocalizedString("friday.complete", comment: "") + day = Translate.friday case .saturday: - day = NSLocalizedString("saturday.complete", comment: "") + day = Translate.saturday case .sunday: - day = NSLocalizedString("sunday.complete", comment: "") + day = Translate.sunday } return day } @@ -41,19 +39,19 @@ enum WeekDay: Int { var briefWordDay: String switch self { case .monday: - briefWordDay = NSLocalizedString("monday", comment: "") + briefWordDay = Translate.briefMonday case .tuesday: - briefWordDay = NSLocalizedString("tuesday", comment: "") + briefWordDay = Translate.briefTuesday case .wednesday: - briefWordDay = NSLocalizedString("wednesday", comment: "") + briefWordDay = Translate.briefWednesday case .thursday: - briefWordDay = NSLocalizedString("thursday", comment: "") + briefWordDay = Translate.briefThursday case .friday: - briefWordDay = NSLocalizedString("friday", comment: "") + briefWordDay = Translate.briefFriday case .saturday: - briefWordDay = NSLocalizedString("saturday", comment: "") + briefWordDay = Translate.briefSaturday case .sunday: - briefWordDay = NSLocalizedString("sunday", comment: "") + briefWordDay = Translate.briefSunday } return briefWordDay } diff --git a/Tracker/Models/Enum/YandexApiKey.swift b/Tracker/Models/Enum/YandexApiKey.swift new file mode 100644 index 0000000..852f62d --- /dev/null +++ b/Tracker/Models/Enum/YandexApiKey.swift @@ -0,0 +1,10 @@ +// +// YandexApiKey.swift +// Tracker +// +// Created by Григорий Машук on 28.01.24. +// + +enum YandexApiKey { + static let apiKeyYMM = "4a0432b5-adf6-45f2-800c-b160a4f7c44a" +} diff --git a/Tracker/Models/MetrycEvent.swift b/Tracker/Models/MetrycEvent.swift index f64e52a..fe3202c 100644 --- a/Tracker/Models/MetrycEvent.swift +++ b/Tracker/Models/MetrycEvent.swift @@ -5,8 +5,6 @@ // Created by Григорий Машук on 24.12.23. // -import Foundation - struct MetricEvent { let event: Event let params: [String: Any] diff --git a/Tracker/Models/Onboarding.swift b/Tracker/Models/Onboarding.swift new file mode 100644 index 0000000..0912298 --- /dev/null +++ b/Tracker/Models/Onboarding.swift @@ -0,0 +1,11 @@ +// +// Onboarding.swift +// Tracker +// +// Created by Григорий Машук on 28.01.24. +// + +struct Onboarding { + let imageName: String + let textLable: String +} diff --git a/Tracker/Models/PinnTrackerModel.swift b/Tracker/Models/PinnTrackerModel.swift index c4459e8..71d992a 100644 --- a/Tracker/Models/PinnTrackerModel.swift +++ b/Tracker/Models/PinnTrackerModel.swift @@ -12,11 +12,9 @@ struct PinnTracker { let nameCategory: String } -extension PinnTracker: Equatable { +extension PinnTracker: Equatable, Hashable { static func == (lhs: PinnTracker, rhs: PinnTracker) -> Bool { lhs.id == rhs.id } } - -extension PinnTracker: Hashable {} diff --git a/Tracker/Models/TrackerCategoryModel.swift b/Tracker/Models/TrackerCategoryModel.swift index 472a685..f229e4a 100644 --- a/Tracker/Models/TrackerCategoryModel.swift +++ b/Tracker/Models/TrackerCategoryModel.swift @@ -5,8 +5,6 @@ // Created by Григорий Машук on 2.10.23. // -import Foundation - struct TrackerCategory { let nameCategory: String let arrayTrackers: [Tracker] diff --git a/Tracker/Models/TrackerModel.swift b/Tracker/Models/TrackerModel.swift index f998e1d..be05323 100644 --- a/Tracker/Models/TrackerModel.swift +++ b/Tracker/Models/TrackerModel.swift @@ -18,8 +18,7 @@ struct Tracker { name: String, color: UIColor, emoji: String, - schedule: [WeekDay]) - { + schedule: [WeekDay]) { self.id = id self.name = name self.color = color @@ -27,5 +26,5 @@ struct Tracker { self.schedule = schedule } } - + diff --git a/Tracker/Models/Translate.swift b/Tracker/Models/Translate.swift new file mode 100644 index 0000000..268d1a6 --- /dev/null +++ b/Tracker/Models/Translate.swift @@ -0,0 +1,88 @@ +// +// Translate.swift +// Tracker +// +// Created by Григорий Машук on 28.01.24. +// + +import Foundation + +struct Translate { + static let tabBarTitleTrecker = NSLocalizedString("trackers", comment: "") + static let tabBarTitleStatistic = NSLocalizedString("statistics", comment: "") + static let textLableTwoVC = NSLocalizedString("textLableTwoVC", comment: "") + static let textButton = NSLocalizedString("textButton", comment: "") + static let headerText = NSLocalizedString("headerText", comment: "") + static let labelStabText = NSLocalizedString("labelStabText", comment: "") + static let labelNothingFoundText = NSLocalizedString("labelNothingFoundText", comment: "") + static let placeholderSearch = NSLocalizedString("placeholderSearch", comment: "") + static let textCancel = NSLocalizedString("textCancel", comment: "") + static let textFix = NSLocalizedString("textFix", comment: "") + static let textUnpin = NSLocalizedString("textUnpin", comment: "") + static let textEdit = NSLocalizedString("textEdit", comment: "") + static let textDelete = NSLocalizedString("textDelete", comment: "") + static let textFixed = NSLocalizedString("textFixed", comment: "") + static let sureDeleteTracker = NSLocalizedString("sureDeleteTracker", comment: "") + static let filters = NSLocalizedString("filters", comment: "") + static let headerStatisticText = NSLocalizedString("headerStatisticText", comment: "") + static let secondaryStatisticText = NSLocalizedString("secondaryStatisticText", comment: "") + static let labelStubStatisticsText = NSLocalizedString("labelStubStatisticsText", comment: "") + static let newHabit = NSLocalizedString("newHabit", comment: "") + static let newIrregular = NSLocalizedString("newIrregular", comment: "") + static let numberOfDays = NSLocalizedString("numberOfDays", tableName: "LocalizableDict", comment: "") + static let placeholderTrackerTextField = NSLocalizedString("placeholderTrackerTextField", comment: "") + static let textCancelButton = NSLocalizedString("textCancelButton", comment: "") + static let textGreateButton = NSLocalizedString("textGreateButton", comment: "") + static let characterLimit = NSLocalizedString("characterLimit", comment: "") + static let everyDay = NSLocalizedString("everyDay", comment: "") + static let scheduleLableText = NSLocalizedString("scheduleLableText", comment: "") + static let doneButtonText = NSLocalizedString("doneButtonText", comment: "") + static let categoryLabelText = NSLocalizedString("categoriLabelText", comment: "") + static let categoryAddButtonText = NSLocalizedString("categoriAddButtonText", comment: "") + static let alertDeleteCategoryMessage = NSLocalizedString("deleteCategoryMessage", comment: "") + static let texGreatetLabelName = NSLocalizedString("texGreatetLabelName", comment: "") + static let newcategoriLabelText = NSLocalizedString("newcategoriLabelText", comment: "") + static let buttonName = NSLocalizedString("buttonName", comment: "") + static let placeholderCategoryTextField = NSLocalizedString("placeholderCategoryTextField", comment: "") + static let filtersLableText = NSLocalizedString("filters", comment: "") + static let textLable = NSLocalizedString("textLableOneVC", comment: "") + static let monday = NSLocalizedString("monday.complete", comment: "") + static let tuesday = NSLocalizedString("tuesday.complete", comment: "") + static let wednesday = NSLocalizedString("wednesday.complete", comment: "") + static let thursday = NSLocalizedString("thursday.complete", comment: "") + static let friday = NSLocalizedString("friday.complete", comment: "") + static let saturday = NSLocalizedString("saturday.complete", comment: "") + static let sunday = NSLocalizedString("sunday.complete", comment: "") + static let briefMonday = NSLocalizedString("monday", comment: "") + static let briefTuesday = NSLocalizedString("tuesday", comment: "") + static let briefWednesday = NSLocalizedString("wednesday", comment: "") + static let briefThursday = NSLocalizedString("thursday", comment: "") + static let briefFriday = NSLocalizedString("friday", comment: "") + static let briefSaturday = NSLocalizedString("saturday", comment: "") + static let briefSunday = NSLocalizedString("sunday", comment: "") + static let category = NSLocalizedString("category", comment: "") + static let schedule = NSLocalizedString("schedule", comment: "") + static let nameHabit = NSLocalizedString("nameHabit", comment: "") + static let nameIrregularEvent = NSLocalizedString("nameIrregularEvent", comment: "") + static let colorHeader = NSLocalizedString("stringColor", comment: "") + static let decodingErrorInvalidNameCategory = NSLocalizedString("decodingErrorInvalidNameCategory", comment: "") + static let getCategoryCoreDataError = NSLocalizedString("getCategoryCoreDataError", comment: "") + static let getTrackerError = NSLocalizedString("getTrackerError", comment: "") + static let decodingErrorInvalidName = NSLocalizedString("decodingErrorInvalidName", comment: "") + static let decodingErrorInvalidId = NSLocalizedString("decodingErrorInvalidId", comment: "") + static let decodingErrorInvalidColor = NSLocalizedString("decodingErrorInvalidColor", comment: "") + static let decodingErrorInvalidEmoji = NSLocalizedString("decodingErrorInvalidEmoji", comment: "") + static let decodingErrorInvalidSchedul = NSLocalizedString("decodingErrorInvalidSchedul", comment: "") + static let transformationErrorInvalid = NSLocalizedString("transformationErrorInvalid", comment: "") + static let decodingErrorInvalidDate = NSLocalizedString("decodingErrorInvalidDate", comment: "") + static let loadTrackerRecord = NSLocalizedString("loadTrackerRecord", comment: "") + static let getTrackerRecord = NSLocalizedString("getTrackerRecord", comment: "") + static let allTrackers = NSLocalizedString("allTrackers", comment: "") + static let toDayTrackers = NSLocalizedString("toDayTrackers", comment: "") + static let completed = NSLocalizedString("completed", comment: "") + static let notCompleted = NSLocalizedString("notCompleted", comment: "") + static let bestPeriod = NSLocalizedString("bestPeriod", comment: "") + static let perfectDays = NSLocalizedString("perfectDays", comment: "") + static let trackersCompleted = NSLocalizedString("trackersCompleted", comment: "") + static let averageValue = NSLocalizedString("averageValue", comment: "") +} diff --git a/Tracker/Models/UpdateTracker.swift b/Tracker/Models/UpdateTracker.swift new file mode 100644 index 0000000..7834c82 --- /dev/null +++ b/Tracker/Models/UpdateTracker.swift @@ -0,0 +1,11 @@ +// +// UpdateTracker.swift +// Tracker +// +// Created by Григорий Машук on 28.01.24. +// + +struct UpdateTracker { + let count: Int + let flag: Bool +} diff --git a/Tracker/Resources/en.lproj/Localizable.strings b/Tracker/Resources/en.lproj/Localizable.strings index defcda2..12eba59 100644 --- a/Tracker/Resources/en.lproj/Localizable.strings +++ b/Tracker/Resources/en.lproj/Localizable.strings @@ -1,9 +1,9 @@ /* -/* - Localizable.strings - Tracker - - Created by Григорий Машук on 12.11.23. + /* + Localizable.strings + Tracker + + Created by Григорий Машук on 12.11.23. */ @@ -21,7 +21,7 @@ "toDayTrackers" = "Trackers for today"; "completed" = "Completed"; "notCompleted" = "Not Completed"; - + //MARK: - StatisticViewController "headerStatisticText" = "Statistics"; "labelStubStatisticsText" = "There is nothing to analyze yet"; diff --git a/Tracker/Resources/ru.lproj/Localizable.strings b/Tracker/Resources/ru.lproj/Localizable.strings index 0112121..f81ca61 100644 --- a/Tracker/Resources/ru.lproj/Localizable.strings +++ b/Tracker/Resources/ru.lproj/Localizable.strings @@ -1,10 +1,10 @@ /* - Localizable.strings - Tracker - - Created by Григорий Машук on 12.11.23. - -*/ + Localizable.strings + Tracker + + Created by Григорий Машук on 12.11.23. + + */ //MARK: - Onboarding "textLableOneVC" = "Отслеживайте только то, что хотите"; diff --git a/Tracker/ViewControllers/OnboardingViewControllers/OnboardingPageViewController.swift b/Tracker/ViewControllers/OnboardingViewControllers/OnboardingPageViewController.swift index af76dd6..8fe3d1c 100644 --- a/Tracker/ViewControllers/OnboardingViewControllers/OnboardingPageViewController.swift +++ b/Tracker/ViewControllers/OnboardingViewControllers/OnboardingPageViewController.swift @@ -10,7 +10,6 @@ import UIKit //MARK: - OnboardingPageViewController final class OnboardingPageViewController: UIPageViewController { private struct ConstantsOnboarding { - static let textLableTwoVC = NSLocalizedString("textLableTwoVC", comment: "") static let imageNameTwoVC = "twoPage" static let numberOfPages = 2 static let currentPage = 0 @@ -29,8 +28,7 @@ final class OnboardingPageViewController: UIPageViewController { return pageControl }() - override func viewDidLoad() - { + override func viewDidLoad() { super.viewDidLoad() dataSource = self delegate = self @@ -39,8 +37,7 @@ final class OnboardingPageViewController: UIPageViewController { } private extension OnboardingPageViewController { - func setupPageControl() - { + func setupPageControl() { view.addSubview(pageControl) pageControl.translatesAutoresizingMaskIntoConstraints = false pageControl.backgroundColor = .clear @@ -56,17 +53,15 @@ private extension OnboardingPageViewController { //MARK: - UIPageViewControllerDataSource extension OnboardingPageViewController: UIPageViewControllerDataSource { func pageViewController(_ pageViewController: UIPageViewController, - viewControllerBefore viewController: UIViewController) -> UIViewController? - { + viewControllerBefore viewController: UIViewController) -> UIViewController? { return nil } func pageViewController(_ pageViewController: UIPageViewController, - viewControllerAfter viewController: UIViewController) -> UIViewController? - { + viewControllerAfter viewController: UIViewController) -> UIViewController? { let twoViewControllers = OnboardingViewController() let onboardingModelTwoVC = Onboarding(imageName: ConstantsOnboarding.imageNameTwoVC, - textLable: ConstantsOnboarding.textLableTwoVC) + textLable: Translate.textLableTwoVC) twoViewControllers.config(model: onboardingModelTwoVC) if currentIndex == ConstantsOnboarding.numberOfPages { return nil diff --git a/Tracker/ViewControllers/OnboardingViewControllers/OnboardingViewController.swift b/Tracker/ViewControllers/OnboardingViewControllers/OnboardingViewController.swift index c4df263..19c6491 100644 --- a/Tracker/ViewControllers/OnboardingViewControllers/OnboardingViewController.swift +++ b/Tracker/ViewControllers/OnboardingViewControllers/OnboardingViewController.swift @@ -10,7 +10,6 @@ import UIKit //MARK: - OnboardingViewController final class OnboardingViewController: UIViewController { private struct ConstantsOnboardingViewController { - static let textButton = NSLocalizedString("textButton", comment: "") static let cornerRadiusButton = CGFloat(16) static let fontTextLable = UIFont.systemFont(ofSize: 32, weight: .bold) static let fontTextButton = UIFont.systemFont(ofSize: 16, weight: .medium) @@ -35,7 +34,7 @@ final class OnboardingViewController: UIViewController { private lazy var startButton: UIButton = { let startButton = UIButton() - startButton.setTitle(ConstantsOnboardingViewController.textButton, for: .normal) + startButton.setTitle(Translate.textButton, for: .normal) startButton.titleLabel?.font = ConstantsOnboardingViewController.fontTextButton startButton.titleLabel?.textColor = .whiteDay startButton.addTarget(self, action: #selector(showViewController), for: .touchUpInside) @@ -43,8 +42,7 @@ final class OnboardingViewController: UIViewController { return startButton }() - override func viewDidLoad() - { + override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .clear setupSubViews() @@ -53,8 +51,7 @@ final class OnboardingViewController: UIViewController { extension OnboardingViewController { //MARK: - SetupUI - private func setupSubViews() - { + private func setupSubViews() { [imageView, onboardingLableView].forEach { view.addSubview($0) $0.translatesAutoresizingMaskIntoConstraints = false @@ -65,8 +62,7 @@ extension OnboardingViewController { setupOnboardingLableView() } - private func setupContentImageView() - { + private func setupContentImageView() { NSLayoutConstraint.activate ([ imageView.topAnchor.constraint(equalTo: view.topAnchor, constant: -1), imageView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 1), @@ -75,8 +71,7 @@ extension OnboardingViewController { ]) } - private func setupOnboardingLableView() - { + private func setupOnboardingLableView() { NSLayoutConstraint.activate([ onboardingLableView.bottomAnchor.constraint(equalTo: startButton.topAnchor, constant: -160), onboardingLableView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16), @@ -84,8 +79,7 @@ extension OnboardingViewController { ]) } - private func setupStartButton() - { + private func setupStartButton() { view.addSubview(startButton) startButton.translatesAutoresizingMaskIntoConstraints = false startButton.backgroundColor = .blackDay @@ -101,16 +95,14 @@ extension OnboardingViewController { } //MARK: - Configure - func config(model: Onboarding) - { + func config(model: Onboarding) { guard let image = UIImage(named: model.imageName) else { return } imageView.image = image onboardingLableView.text = model.textLable } @objc - private func showViewController() - { + private func showViewController() { guard let window = UIApplication.shared.windows.first else { return } let vc = TabBarController() window.rootViewController = vc diff --git a/Tracker/ViewControllers/TabBarController.swift b/Tracker/ViewControllers/TabBarController.swift index b15a028..f641c23 100644 --- a/Tracker/ViewControllers/TabBarController.swift +++ b/Tracker/ViewControllers/TabBarController.swift @@ -13,16 +13,12 @@ final class TabBarController: UITabBarController { static let tabBarImageTrecker = "tabBarTracker" static let tabBarImageStatistic = "tabBarStatistic" - static let tabBarTitleTrecker = NSLocalizedString("trackers", comment: "") - static let tabBarTitleStatistic = NSLocalizedString("statistics", comment: "") - static let insertImage = UIEdgeInsets(top: 5, left: 0, bottom: 0, right: 0) } private let colors = Colors() - override func viewDidLoad() - { + override func viewDidLoad() { super.viewDidLoad() tabBar.layer.borderWidth = 1 tabBar.clipsToBounds = true @@ -35,23 +31,21 @@ final class TabBarController: UITabBarController { viewControllers = [ generateViewController(vc: trackerNavigationController, imageName: ConstantsTabBar.tabBarImageTrecker, - title: ConstantsTabBar.tabBarTitleTrecker, + title: Translate.tabBarTitleTrecker, insert: ConstantsTabBar.insertImage), generateViewController(vc: statisticsNavigationController, imageName: ConstantsTabBar.tabBarImageStatistic, - title: ConstantsTabBar.tabBarTitleStatistic, + title: Translate.tabBarTitleStatistic, insert: ConstantsTabBar.insertImage) ] } - override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) - { + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { super.traitCollectionDidChange(previousTraitCollection) if #available(iOS 13.0, *), // Проверяем только изменение цветовой схемы - traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) - { + traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) { if traitCollection.userInterfaceStyle == .dark { tabBar.layer.borderColor = UIColor.black.cgColor } else { @@ -61,7 +55,8 @@ final class TabBarController: UITabBarController { } deinit { - AnalyticsService.shared.close() + AnalyticsService.reportEvent(field: MetricEvent(event: .close, + params: [EventAnalytics.screen.rawValue: Screen.Main.rawValue])) } } @@ -70,8 +65,7 @@ private extension TabBarController { func generateViewController(vc: UIViewController, imageName: String, title: String, - insert: UIEdgeInsets ) -> UIViewController - { + insert: UIEdgeInsets ) -> UIViewController { vc.tabBarItem.image = UIImage(named: imageName) vc.tabBarItem.title = title vc.tabBarItem.imageInsets = insert diff --git a/Tracker/ViewControllers/ViewControllers/CreateTrackerViewController.swift b/Tracker/ViewControllers/ViewControllers/CreateTrackerViewController.swift index abeafb9..faff781 100644 --- a/Tracker/ViewControllers/ViewControllers/CreateTrackerViewController.swift +++ b/Tracker/ViewControllers/ViewControllers/CreateTrackerViewController.swift @@ -9,7 +9,9 @@ import UIKit //MARK: - UpdateTrackerViewControllerDelegate protocol UpdateTrackerViewControllerDelegate: AnyObject { - func trackerViewController(vc: UIViewController, nameCategory: String, tracker: Tracker) + func trackerViewController(vc: UIViewController, + nameCategory: String?, + tracker: Tracker) func trackerViewControllerDidCancel(_ vc: CreateTrackerViewController) } @@ -22,14 +24,6 @@ protocol CreateTrackerViewControllerDelegate: AnyObject { //MARK: - CreateTrackerViewController final class CreateTrackerViewController: UIViewController { private struct ConstantsCreateVc { - static let newHabit = NSLocalizedString("newHabit", comment: "") - static let newIrregular = NSLocalizedString("newIrregular", comment: "") - static let numberOfDays = NSLocalizedString("numberOfDays", tableName: "LocalizableDict", comment: "") - static let placeholderTrackerTextField = NSLocalizedString("placeholderTrackerTextField", comment: "") - static let textCancelButton = NSLocalizedString("textCancelButton", comment: "") - static let textGreateButton = NSLocalizedString("textGreateButton", comment: "") - static let characterLimit = NSLocalizedString("characterLimit", comment: "") - static let everyDay = NSLocalizedString("everyDay", comment: "") static let emptyString = "" static let restrictionsTextField = 38 @@ -53,10 +47,11 @@ final class CreateTrackerViewController: UIViewController { private let viewModel: EditTrackerModelProtocol private let colors = Colors() + private let handler = HandlerResultType() private lazy var newHabitLabel: UILabel = { let newHabitLabel = UILabel() - newHabitLabel.text = viewModel.isSchedul ? ConstantsCreateVc.newIrregular : ConstantsCreateVc.newHabit + newHabitLabel.text = viewModel.isSchedul ? Translate.newIrregular : Translate.newHabit newHabitLabel.font = ConstantsCreateVc.textFont newHabitLabel.textAlignment = .center newHabitLabel.backgroundColor = .clear @@ -74,7 +69,7 @@ final class CreateTrackerViewController: UIViewController { private lazy var createNameTextField: UITextField = { let createNameTextField = UITextField() - createNameTextField.placeholder = ConstantsCreateVc.placeholderTrackerTextField + createNameTextField.placeholder = Translate.placeholderTrackerTextField createNameTextField.font = ConstantsCreateVc.createNameTextFieldFont createNameTextField.indent(size: ConstantsCreateVc.leftIndentTextField) createNameTextField.backgroundColor = .backgroundNight @@ -91,7 +86,7 @@ final class CreateTrackerViewController: UIViewController { let characterRestrictionsView = UILabel() characterRestrictionsView.backgroundColor = .clear characterRestrictionsView.font = ConstantsCreateVc.createNameTextFieldFont - characterRestrictionsView.text = ConstantsCreateVc.characterLimit + characterRestrictionsView.text = Translate.characterLimit characterRestrictionsView.textColor = .redDay characterRestrictionsView.numberOfLines = ConstantsCreateVc.numberOfLinesRestrictionsTextField characterRestrictionsView.textAlignment = .center @@ -171,7 +166,7 @@ final class CreateTrackerViewController: UIViewController { cancelButton.layer.borderColor = UIColor.redDay.cgColor cancelButton.layer.masksToBounds = true cancelButton.backgroundColor = .clear - cancelButton.setTitle(ConstantsCreateVc.textCancelButton , for: .normal) + cancelButton.setTitle(Translate.textCancelButton , for: .normal) cancelButton.setTitleColor(.redDay, for: .normal) cancelButton.titleLabel?.font = ConstantsCreateVc.textFont cancelButton.addTarget(self, action: #selector(didTapCancelButton), for: .touchUpInside) @@ -185,7 +180,7 @@ final class CreateTrackerViewController: UIViewController { createButton.layer.masksToBounds = true createButton.backgroundColor = .grayDay createButton.setTitleColor(.whiteDay, for: .normal) - createButton.setTitle(ConstantsCreateVc.textGreateButton, for: .normal) + createButton.setTitle(Translate.textGreateButton, for: .normal) createButton.titleLabel?.font = ConstantsCreateVc.textFont createButton.isEnabled = false createButton.addTarget(self, action: #selector(didTapGreateButton) , for: .touchUpInside) @@ -220,21 +215,18 @@ final class CreateTrackerViewController: UIViewController { init(viewModel: EditTrackerModelProtocol, updateTrackerDelegate: UpdateTrackerViewControllerDelegate? = nil, - createTrackerDelegate: CreateTrackerViewControllerDelegate? = nil) - { + createTrackerDelegate: CreateTrackerViewControllerDelegate? = nil) { self.viewModel = viewModel self.updateTrackerDelegate = updateTrackerDelegate self.createTrackerDelegate = createTrackerDelegate super.init(nibName: nil, bundle: nil) } - required init?(coder: NSCoder) - { + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - override func viewDidLoad() - { + override func viewDidLoad() { super.viewDidLoad() bind() setupUIElement() @@ -246,8 +238,7 @@ final class CreateTrackerViewController: UIViewController { } extension CreateTrackerViewController { - private func bind() - { + private func bind() { guard let viewModel = viewModel as? EditTrackerViewModel else { return } viewModel.$schedule.bind { [weak self] _ in guard let self else { return } @@ -278,22 +269,19 @@ extension CreateTrackerViewController { } } - private func chengeActivButton() - { + private func chengeActivButton() { let flag = self.viewModel.checkingForEmptiness() isActivCreateButton(flag: flag) } //MARK: - Обработка событий @objc - private func didTapCancelButton() - { + private func didTapCancelButton() { dismiss(animated: true) } @objc - private func didTapGreateButton() - { + private func didTapGreateButton() { guard let viewModel = viewModel as? EditTrackerViewModel, let color = viewModel.color else { return } @@ -303,29 +291,36 @@ extension CreateTrackerViewController { name: viewModel.nameTracker, color: color, emoji: viewModel.emoji, - schedule: viewModel.isSchedul ? viewModel.regular : viewModel.schedule) + schedule: viewModel.isSchedul ? DataSource.regular : viewModel.schedule) + let nameCategory = viewModel.isPinned ? viewModel.nameNewCategory : nil updateTrackerDelegate.trackerViewController(vc: self, - nameCategory: viewModel.nameNewCategory, + nameCategory: nameCategory, tracker: tracker) updateTrackerDelegate.trackerViewControllerDidCancel(self) + if !viewModel.isPinned { + self.handler.resultTypeHandler(viewModel.editPinnedCategory(tracker.id, + newPinnedCat: viewModel.nameNewCategory), + vc: self) {} + } } if let createTrackerDelegate { let tracker = Tracker( name: viewModel.nameTracker, color: color, emoji: viewModel.emoji, - schedule: viewModel.isSchedul ? viewModel.regular : viewModel.schedule) + schedule: viewModel.isSchedul ? DataSource.regular : viewModel.schedule) createTrackerDelegate.trackerViewController(vc: self, nameCategory: viewModel.nameNewCategory, tracker: tracker) createTrackerDelegate.trackerViewControllerDidCancel(self) } - AnalyticsService.shared.addTracker() + AnalyticsService.reportEvent(field: MetricEvent(event: .click, + params: [EventAnalytics.screen.rawValue: Screen.Main.rawValue, + EventAnalytics.item.rawValue: ItemClick.add_track.rawValue])) } @objc - private func textFieldDidChange(_ textField: UITextField) - { + private func textFieldDidChange(_ textField: UITextField) { guard let text = textField.text, let viewModel = viewModel as? EditTrackerViewModel else { return } @@ -340,38 +335,32 @@ extension CreateTrackerViewController { } @objc - private func hideKeyboard() - { + private func hideKeyboard() { view.endEditing(true) } - private func isActivCreateButton(flag: Bool) - { + private func isActivCreateButton(flag: Bool) { createButton.isEnabled = flag createButton.backgroundColor = flag ? colors.buttonDisabledColor : .grayDay createButton.setTitleColor( flag ? .textEventColor : .whiteDay, for: .normal) } - func reverseIsSchedul() - { + func reverseIsSchedul() { viewModel.reverseIsSchedul() } - func setTextCountLable(count: Int) -> String - { - String.localizedStringWithFormat(ConstantsCreateVc.numberOfDays, + func setTextCountLable(count: Int) -> String { + String.localizedStringWithFormat(Translate.numberOfDays, count) } - func presentViewController(vc: UIViewController, modalStyle: UIModalPresentationStyle) - { + func presentViewController(vc: UIViewController, modalStyle: UIModalPresentationStyle) { vc.modalPresentationStyle = modalStyle present(vc, animated: true) } //MARK: - SetupUI - func setupUIElement() - { + func setupUIElement() { setupNewHabitLabel() if updateTrackerDelegate != nil { setupCountLabel() @@ -381,8 +370,7 @@ extension CreateTrackerViewController { setupStackView() } - func setupCountLabel() - { + func setupCountLabel() { view.addSubview(countСompletedTrackers) countСompletedTrackers.translatesAutoresizingMaskIntoConstraints = false @@ -393,8 +381,7 @@ extension CreateTrackerViewController { ]) } - func setupNewHabitLabel() - { + func setupNewHabitLabel() { view.addSubview(newHabitLabel) newHabitLabel.translatesAutoresizingMaskIntoConstraints = false @@ -405,8 +392,7 @@ extension CreateTrackerViewController { ]) } - func setupButtonStack() - { + func setupButtonStack() { view.addSubview(buttonStack) [cancelButton, createButton].forEach { $0.translatesAutoresizingMaskIntoConstraints = false @@ -424,8 +410,7 @@ extension CreateTrackerViewController { ]) } - func setupScrollView() - { + func setupScrollView() { view.addSubview(scrollView) scrollView.translatesAutoresizingMaskIntoConstraints = false @@ -444,8 +429,7 @@ extension CreateTrackerViewController { ]) } - func setupStackView() - { + func setupStackView() { scrollView.addSubview(contentStackView) [createNameTextField, characterRestrictionsView, selectionTableView, emojiCollectionView, colorCollectionView].forEach { contentStackView.addArrangedSubview($0) @@ -469,20 +453,18 @@ extension CreateTrackerViewController { //MARK: - UITableViewDataSource extension CreateTrackerViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, - numberOfRowsInSection section: Int) -> Int - { + numberOfRowsInSection section: Int) -> Int { return viewModel.listSettings.count } func tableView(_ tableView: UITableView, - cellForRowAt indexPath: IndexPath) -> UITableViewCell - { + cellForRowAt indexPath: IndexPath) -> UITableViewCell { guard let cell = tableView.dequeueReusableCell(withIdentifier: "\(CreateTrackerTableViewCell.self)") as? CreateTrackerTableViewCell, let viewModel = viewModel as? EditTrackerViewModel else { return UITableViewCell() } let secondarySchedulText = viewModel.jonedSchedule(schedule: viewModel.schedule, - stringArrayDay: ConstantsCreateVc.everyDay) + stringArrayDay: Translate.everyDay) let listSettings = viewModel.listSettings[indexPath.row] switch listSettings { case .category: @@ -524,14 +506,12 @@ extension CreateTrackerViewController: UITableViewDataSource { //MARK: - UITableViewDelegate extension CreateTrackerViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, - heightForRowAt indexPath: IndexPath) -> CGFloat - { + heightForRowAt indexPath: IndexPath) -> CGFloat { 75 } func tableView(_ tableView: UITableView, - didSelectRowAt indexPath: IndexPath) - { + didSelectRowAt indexPath: IndexPath) { switch viewModel.listSettings[indexPath.row] { case .category: let selectCategoriesViewController = SelectCategoriesViewController(delegate: self, @@ -558,40 +538,36 @@ extension CreateTrackerViewController: UITextFieldDelegate { //MARK: - UICollectionViewDataSource extension CreateTrackerViewController: UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, - numberOfItemsInSection section: Int) -> Int - { + numberOfItemsInSection section: Int) -> Int { var count: Int - switch viewModel.dataSection[section] { + switch DataSource.dataSection[section] { case .color: - count = viewModel.colors.count + count = DataSource.colors.count case .emoji: - count = viewModel.emojies.count + count = DataSource.emojies.count } return count } - func numberOfSections(in collectionView: UICollectionView) -> Int - { - viewModel.dataSection.count + func numberOfSections(in collectionView: UICollectionView) -> Int { + DataSource.dataSection.count } func collectionView(_ collectionView: UICollectionView, - cellForItemAt indexPath: IndexPath) -> UICollectionViewCell - { + cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { var returnCell = UICollectionViewCell() if collectionView == colorCollectionView, let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "\(ColorCollectionViewCell.self)", for: indexPath) as? ColorCollectionViewCell { - let color = viewModel.colors[indexPath.row] + let color = DataSource.colors[indexPath.row] cell.configColor(color: color) returnCell = cell } if collectionView == emojiCollectionView, let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "\(EmojiCollectionViewCell.self)", - for: indexPath) as? EmojiCollectionViewCell - { - let emoji = viewModel.emojies[indexPath.row] + for: indexPath) as? EmojiCollectionViewCell { + let emoji = DataSource.emojies[indexPath.row] cell.configEmoji(emoji: emoji) returnCell = cell } @@ -602,27 +578,25 @@ extension CreateTrackerViewController: UICollectionViewDataSource { //MARK: - UICollectionViewDelegate extension CreateTrackerViewController: UICollectionViewDelegate { func collectionView(_ collectionView: UICollectionView, - didSelectItemAt indexPath: IndexPath) - { + didSelectItemAt indexPath: IndexPath) { if collectionView == colorCollectionView { guard let cell = collectionView.cellForItem(at: indexPath) as? ColorCollectionViewCell else { return } - let color = viewModel.colors[indexPath.row] + let color = DataSource.colors[indexPath.row] cell.colorSelection(color: color, flag: true) viewModel.setColor(self, color: color) } if collectionView == emojiCollectionView { guard let cell = collectionView.cellForItem(at: indexPath) as? EmojiCollectionViewCell else { return } - let emoji = viewModel.emojies[indexPath.row] + let emoji = DataSource.emojies[indexPath.row] cell.emojiSelection(isBackground: true) viewModel.setEmojiTracker(self, emoji: emoji) } } func collectionView(_ collectionView: UICollectionView, - didDeselectItemAt indexPath: IndexPath) - { + didDeselectItemAt indexPath: IndexPath) { if collectionView == colorCollectionView { guard let cell = collectionView.cellForItem(at: indexPath) as? ColorCollectionViewCell else { return } @@ -638,12 +612,10 @@ extension CreateTrackerViewController: UICollectionViewDelegate { } //MARK: - UICollectionViewDelegateFlowLayout -extension CreateTrackerViewController: UICollectionViewDelegateFlowLayout -{ +extension CreateTrackerViewController: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, - sizeForItemAt indexPath: IndexPath) -> CGSize - { + sizeForItemAt indexPath: IndexPath) -> CGSize { let availableWidth = collectionView.frame.width - emojiCollectionView.params.paddingWidth let cellWidth = availableWidth / CGFloat(emojiCollectionView.params.cellCount) let sizeCell = CGSize(width: cellWidth, height: ConstantsCreateVc.heightCollectionView) @@ -653,8 +625,7 @@ extension CreateTrackerViewController: UICollectionViewDelegateFlowLayout func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, - insetForSectionAt section: Int) -> UIEdgeInsets - { + insetForSectionAt section: Int) -> UIEdgeInsets { UIEdgeInsets(top: 10, left: emojiCollectionView.params.leftInset, bottom: 16, @@ -663,8 +634,7 @@ extension CreateTrackerViewController: UICollectionViewDelegateFlowLayout func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, - referenceSizeForHeaderInSection section: Int) -> CGSize - { + referenceSizeForHeaderInSection section: Int) -> CGSize { var systemLayoutSize: CGSize let indexPath = IndexPath(row: 0, section: section) let headerView = self.collectionView(collectionView, @@ -679,14 +649,11 @@ extension CreateTrackerViewController: UICollectionViewDelegateFlowLayout func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, - at indexPath: IndexPath) -> UICollectionReusableView - { + at indexPath: IndexPath) -> UICollectionReusableView { guard let supplementary = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "\(HeaderReusableView.self)", for: indexPath) as? HeaderReusableView - else { return UICollectionReusableView() - - } + else { return UICollectionReusableView() } switch kind { case UICollectionView.elementKindSectionHeader: if collectionView == colorCollectionView { @@ -704,16 +671,14 @@ extension CreateTrackerViewController: UICollectionViewDelegateFlowLayout //MARK: - ScheduleViewControllerDelegate extension CreateTrackerViewController: ScheduleViewControllerDelegate { - func daysOfWeek(viewController: UIViewController, listDays: [WeekDay]) - { + func daysOfWeek(viewController: UIViewController, listDays: [WeekDay]) { viewModel.setListWeekDay(listWeekDay: listDays) } } //MARK: - GreateCategoriesViewControllerDelegate extension CreateTrackerViewController: SelectCategoriesViewControllerDelegate { - func categoriesViewController(vc: UIViewController, nameCategory: String) - { + func categoriesViewController(vc: UIViewController, nameCategory: String) { viewModel.setNameNewCategory(self, nameCategory: nameCategory) vc.dismiss(animated: true) } @@ -725,8 +690,8 @@ extension CreateTrackerViewController: TrackersViewControllerDelegate { editTracker: Tracker, count: Int, nameCategory: String, - indexPath: IndexPath) - { + indexPath: IndexPath, + isPinned: Bool) { let colorRow = viewModel.getColorRow(color: editTracker.color) let emojiRow = viewModel.getEmojiRow(emoji: editTracker.emoji) countСompletedTrackers.text = setTextCountLable(count: count) @@ -738,7 +703,8 @@ extension CreateTrackerViewController: TrackersViewControllerDelegate { else { return } viewModel.editTracker(vc: self, editTracker: editTracker, - nameCategory: nameCategory) + nameCategory: nameCategory, + isPinned: isPinned) emojiCell.emojiSelection(isBackground: true) colorCell.colorSelection(color: editTracker.color, flag: true) emojiCollectionView.selectItem(at: IndexPath(row: emojiRow, section: 0), diff --git a/Tracker/ViewControllers/ViewControllers/EventSelectionViewController.swift b/Tracker/ViewControllers/ViewControllers/EventSelectionViewController.swift index eccec9a..a9efa44 100644 --- a/Tracker/ViewControllers/ViewControllers/EventSelectionViewController.swift +++ b/Tracker/ViewControllers/ViewControllers/EventSelectionViewController.swift @@ -18,7 +18,6 @@ protocol EventSelectionViewControllerDelegate: AnyObject { //MARK: - EventSelectionViewController final class EventSelectionViewController: UIViewController { private struct ConstantsEventVc { - static let texGreatetLabelName = NSLocalizedString("texGreatetLabelName", comment: "") static let textFont = UIFont.systemFont(ofSize: 16, weight: .medium) static let cornerRadius = CGFloat(16) static let stackViewSpacing = CGFloat(10) @@ -29,7 +28,7 @@ final class EventSelectionViewController: UIViewController { private lazy var labelCreate: UILabel = { let labelCreate = UILabel() - labelCreate.text = ConstantsEventVc.texGreatetLabelName + labelCreate.text = Translate.texGreatetLabelName labelCreate.font = ConstantsEventVc.textFont labelCreate.backgroundColor = .clear labelCreate.translatesAutoresizingMaskIntoConstraints = false @@ -67,8 +66,7 @@ final class EventSelectionViewController: UIViewController { return irregularEventButton }() - override func viewDidLoad() - { + override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = colors.viewBackground setupLableGreate() @@ -79,22 +77,19 @@ final class EventSelectionViewController: UIViewController { private extension EventSelectionViewController { //MARK: - Обработка событий @objc - func didTapHabitButton() - { + func didTapHabitButton() { let greateVC = createTrackerVC() greateVC.reverseIsSchedul() present(greateVC, animated: true) } @objc - func didTapIrregularEventButton() - { + func didTapIrregularEventButton() { let greateVC = createTrackerVC() present(greateVC, animated: true) } - func createTrackerVC() -> CreateTrackerViewController - { + func createTrackerVC() -> CreateTrackerViewController { let viewModel = EditTrackerViewModel() let createVC = CreateTrackerViewController(viewModel: viewModel, updateTrackerDelegate: nil, @@ -106,8 +101,7 @@ private extension EventSelectionViewController { //MARK: - SetupUI func setupButton(text: NameEvent, font: UIFont, - cornerRadius: CGFloat? = nil) -> UIButton - { + cornerRadius: CGFloat? = nil) -> UIButton { let button = UIButton() button.setTitle(text.name, for: .normal) button.backgroundColor = colors.buttonEventColor @@ -120,8 +114,7 @@ private extension EventSelectionViewController { return button } - func setupLableGreate() - { + func setupLableGreate() { view.addSubview(labelCreate) NSLayoutConstraint.activate([ @@ -132,8 +125,7 @@ private extension EventSelectionViewController { ]) } - func setupStackView() - { + func setupStackView() { view.addSubview(stackView) [habitButton, irregularEventButton].forEach { stackView.addArrangedSubview($0) @@ -157,15 +149,13 @@ private extension EventSelectionViewController { extension EventSelectionViewController: CreateTrackerViewControllerDelegate { func trackerViewController(vc: UIViewController, nameCategory: String, - tracker: Tracker) - { + tracker: Tracker) { delegate?.eventSelectionViewController(vc: self, nameCategories: nameCategory, tracker: tracker) } - func trackerViewControllerDidCancel(_ vc: CreateTrackerViewController) - { + func trackerViewControllerDidCancel(_ vc: CreateTrackerViewController) { vc.dismiss(animated: false) delegate?.eventSelectionViewControllerDidCancel(self) } diff --git a/Tracker/ViewControllers/ViewControllers/FiltersViewController.swift b/Tracker/ViewControllers/ViewControllers/FiltersViewController.swift index 383c424..d422fee 100644 --- a/Tracker/ViewControllers/ViewControllers/FiltersViewController.swift +++ b/Tracker/ViewControllers/ViewControllers/FiltersViewController.swift @@ -13,7 +13,6 @@ protocol FiltersViewControllerDelegate: AnyObject { final class FiltersViewController: UIViewController { private struct ConstantsFilters { - static let filtersLableText = NSLocalizedString("filters", comment: "") static let filterLabelFont = UIFont.systemFont(ofSize: 16, weight: .medium) static let cornerRadiusCell = CGFloat(16) static let heightCell = CGFloat(75) @@ -29,7 +28,7 @@ final class FiltersViewController: UIViewController { private lazy var filtersLable: UILabel = { let filtersLable = UILabel() - filtersLable.text = ConstantsFilters.filtersLableText + filtersLable.text = Translate.filtersLableText filtersLable.textColor = .blackDay filtersLable.font = ConstantsFilters.filterLabelFont filtersLable.textAlignment = .center @@ -60,8 +59,7 @@ final class FiltersViewController: UIViewController { } init(viewModel: FiltersViewModelProtocol, - delegate: FiltersViewControllerDelegate) - { + delegate: FiltersViewControllerDelegate) { self.viewModel = viewModel self.delegate = delegate super.init(nibName: nil, bundle: nil) @@ -84,8 +82,7 @@ private extension FiltersViewController { ]) } - private func setupTableView() - { + private func setupTableView() { view.addSubview(filterTableView) filterTableView.translatesAutoresizingMaskIntoConstraints = false @@ -104,20 +101,17 @@ private extension FiltersViewController { //MARK: - UITableViewDataSource extension FiltersViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, - numberOfRowsInSection section: Int) -> Int - { + numberOfRowsInSection section: Int) -> Int { viewModel.filtersState.count } func tableView(_ tableView: UITableView, - heightForRowAt indexPath: IndexPath) -> CGFloat - { + heightForRowAt indexPath: IndexPath) -> CGFloat { ConstantsFilters.heightCell } func tableView(_ tableView: UITableView, - cellForRowAt indexPath: IndexPath) -> UITableViewCell - { + cellForRowAt indexPath: IndexPath) -> UITableViewCell { guard let cell = filterTableView.dequeueReusableCell(withIdentifier: "\(CustomTableViewCell.self)", for: indexPath) as? CustomTableViewCell else { return UITableViewCell() } @@ -147,8 +141,7 @@ extension FiltersViewController: UITableViewDataSource { //MARK: - UITableViewDelegate extension FiltersViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, - didSelectRowAt indexPath: IndexPath) - { + didSelectRowAt indexPath: IndexPath) { guard let delegate else { return } let selectFilter = viewModel.filtersState[indexPath.row] viewModel.setSelectFilter(selectFilter: selectFilter.rawValue) diff --git a/Tracker/ViewControllers/ViewControllers/NewCategoriViewController.swift b/Tracker/ViewControllers/ViewControllers/NewCategoriViewController.swift index d5f9e36..87cf584 100644 --- a/Tracker/ViewControllers/ViewControllers/NewCategoriViewController.swift +++ b/Tracker/ViewControllers/ViewControllers/NewCategoriViewController.swift @@ -20,10 +20,6 @@ protocol UpdateCategoriViewControllerDelegate: AnyObject { //MARK: - NewCategoriViewController class NewCategoriViewController: UIViewController { private struct ConstantsNewCatVc { - static let newcategoriLabelText = NSLocalizedString("newcategoriLabelText", comment: "") - static let buttonName = NSLocalizedString("buttonName", comment: "") - static let placeholderCategoryTextField = NSLocalizedString("placeholderCategoryTextField", comment: "") - static let cornerRadius = CGFloat(16) static let leftIndentTextField = CGFloat(12) static let font = UIFont.systemFont(ofSize: 16, weight: .medium) @@ -38,7 +34,7 @@ class NewCategoriViewController: UIViewController { private lazy var newCategoriLabel: UILabel = { let newCategoriLabel = UILabel() - newCategoriLabel.text = ConstantsNewCatVc.newcategoriLabelText + newCategoriLabel.text = Translate.newcategoriLabelText newCategoriLabel.font = ConstantsNewCatVc.font newCategoriLabel.textAlignment = .center newCategoriLabel.backgroundColor = .clear @@ -48,7 +44,7 @@ class NewCategoriViewController: UIViewController { private lazy var readyButton: UIButton = { let readyButton = UIButton() - readyButton.setTitle(ConstantsNewCatVc.buttonName, for: .normal) + readyButton.setTitle(Translate.buttonName, for: .normal) readyButton.setTitleColor(.textEventColor, for: .normal) readyButton.isEnabled = false readyButton.backgroundColor = .grayDay @@ -64,7 +60,7 @@ class NewCategoriViewController: UIViewController { private lazy var createNameTextField: UITextField = { let createNameTextField = UITextField() createNameTextField.delegate = self - createNameTextField.placeholder = ConstantsNewCatVc.placeholderCategoryTextField + createNameTextField.placeholder = Translate.placeholderCategoryTextField createNameTextField.font = ConstantsNewCatVc.textFieldFont createNameTextField.indent(size: ConstantsNewCatVc.leftIndentTextField) createNameTextField.backgroundColor = .backgroundNight @@ -76,20 +72,17 @@ class NewCategoriViewController: UIViewController { }() init(createCategorydelegate: NewCategoriViewControllerDelegate? = nil, - updateCategoryrDelegate: UpdateCategoriViewControllerDelegate? = nil) - { + updateCategoryrDelegate: UpdateCategoriViewControllerDelegate? = nil) { self.createCategorydelegate = createCategorydelegate self.updateCategoryrDelegate = updateCategoryrDelegate super.init(nibName: nil, bundle: nil) } - required init?(coder: NSCoder) - { + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - override func viewDidLoad() - { + override func viewDidLoad() { super.viewDidLoad() viewModel = NewCategoryViewModel() bind() @@ -103,8 +96,7 @@ class NewCategoriViewController: UIViewController { } private extension NewCategoriViewController { - func bind() - { + func bind() { guard let viewModel = viewModel as? NewCategoryViewModel else { return } viewModel.$newNameCategory.bind { [weak self] text in guard let self else { return } @@ -116,8 +108,7 @@ private extension NewCategoriViewController { //MARK: - Обработка событий @objc - func didTapNewСategoriButton() - { + func didTapNewСategoriButton() { guard let viewModel else { return } if let createCategorydelegate { createCategorydelegate.didNewCategoryName(self, @@ -132,13 +123,11 @@ private extension NewCategoriViewController { } @objc - private func hideKeyboard() - { + private func hideKeyboard() { view.endEditing(true) } - func chengeHiddenButton(flag: Bool) - { + func chengeHiddenButton(flag: Bool) { readyButton.isEnabled = !flag readyButton.backgroundColor = !flag ? .blackDay : .grayDay readyButton.backgroundColor = flag ? colors.buttonDisabledColor : .grayDay @@ -146,15 +135,13 @@ private extension NewCategoriViewController { } //MARK: - SetupUI - func setupUIElement() - { + func setupUIElement() { setupCategoriButton() setupCategoriLabel() setupCreateNameTextField() } - func setupCategoriButton() - { + func setupCategoriButton() { view.addSubview(readyButton) NSLayoutConstraint.activate([ @@ -168,8 +155,7 @@ private extension NewCategoriViewController { ]) } - func setupCategoriLabel() - { + func setupCategoriLabel() { view.addSubview(newCategoriLabel) newCategoriLabel.translatesAutoresizingMaskIntoConstraints = false @@ -180,8 +166,7 @@ private extension NewCategoriViewController { ]) } - func setupCreateNameTextField() - { + func setupCreateNameTextField() { view.addSubview(createNameTextField) createNameTextField.translatesAutoresizingMaskIntoConstraints = false @@ -199,15 +184,13 @@ private extension NewCategoriViewController { //MARK: - UITextFieldDelegate extension NewCategoriViewController: UITextFieldDelegate { - func textFieldDidEndEditing(_ textField: UITextField) - { + func textFieldDidEndEditing(_ textField: UITextField) { guard let text = textField.text, let viewModel else { return } viewModel.setNewNameCategory(text: text) } - func textFieldShouldReturn(_ textField: UITextField) -> Bool - { + func textFieldShouldReturn(_ textField: UITextField) -> Bool { createNameTextField.resignFirstResponder() return true } @@ -215,8 +198,7 @@ extension NewCategoriViewController: UITextFieldDelegate { extension NewCategoriViewController: SelectCategoryEditViewControllerDelegate { func editCategoriesViewController(vc: UIViewController, - oldNameCategory: String) - { + oldNameCategory: String) { guard let viewModel else { return } createNameTextField.text = oldNameCategory viewModel.setNewNameCategory(text: oldNameCategory) diff --git a/Tracker/ViewControllers/ViewControllers/ScheduleViewController.swift b/Tracker/ViewControllers/ViewControllers/ScheduleViewController.swift index 4889b44..b7dd9da 100644 --- a/Tracker/ViewControllers/ViewControllers/ScheduleViewController.swift +++ b/Tracker/ViewControllers/ViewControllers/ScheduleViewController.swift @@ -14,9 +14,6 @@ protocol ScheduleViewControllerDelegate: AnyObject { //MARK: - ScheduleViewController final class ScheduleViewController: UIViewController { private struct ConstantsShedulVc { - static let scheduleLableText = NSLocalizedString("scheduleLableText", comment: "") - static let doneButtonText = NSLocalizedString("doneButtonText", comment: "") - static let spacingVerticalStack = CGFloat(20) static let cornerRadiusUIElement = CGFloat(16) static let heightCell = CGFloat(75) @@ -30,7 +27,7 @@ final class ScheduleViewController: UIViewController { private lazy var scheduleLable: UILabel = { let scheduleLable = UILabel() - scheduleLable.text = ConstantsShedulVc.scheduleLableText + scheduleLable.text = Translate.scheduleLableText scheduleLable.font = ConstantsShedulVc.scheduleLableFont scheduleLable.textColor = colors.buttonEventColor scheduleLable.backgroundColor = .clear @@ -61,7 +58,7 @@ final class ScheduleViewController: UIViewController { doneButton.layer.cornerRadius = ConstantsShedulVc.cornerRadiusUIElement doneButton.layer.masksToBounds = true doneButton.backgroundColor = colors.buttonDisabledColor - doneButton.setTitle(ConstantsShedulVc.doneButtonText, for: .normal) + doneButton.setTitle(Translate.doneButtonText, for: .normal) doneButton.setTitleColor(.textEventColor, for: .normal) return doneButton @@ -76,19 +73,16 @@ final class ScheduleViewController: UIViewController { return contentStackView }() - init(viewModel: SheduleViewModelProtocol) - { + init(viewModel: SheduleViewModelProtocol) { self.viewModel = viewModel super.init(nibName: nil, bundle: nil) } - required init?(coder: NSCoder) - { + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - override func viewDidLoad() - { + override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = colors.viewBackground setupContentSteck() @@ -99,8 +93,7 @@ final class ScheduleViewController: UIViewController { private extension ScheduleViewController { //MARK: - Обработка событий @objc - func didTapDoneButton() - { + func didTapDoneButton() { guard let delegate else { return } //передаем список дней для поля "schedule" при создании трекера @@ -111,8 +104,7 @@ private extension ScheduleViewController { //MARK: - Логика //метод обновления списка listWeekDay в зависимости от положения переключателя в яцейке weekDayTableView - func updateListWeekDay(flag: Bool, day: WeekDay) - { + func updateListWeekDay(flag: Bool, day: WeekDay) { if flag { var listDay: [WeekDay] = [] listDay = viewModel.listWeekDay @@ -128,8 +120,7 @@ private extension ScheduleViewController { } //MARK: - SetupUI - func setupContentSteck() - { + func setupContentSteck() { view.addSubview(contentStackView) [scheduleLable, weekDayTableView, doneButton].forEach { $0.translatesAutoresizingMaskIntoConstraints = false @@ -154,14 +145,12 @@ private extension ScheduleViewController { //MARK: - UITableViewDataSource extension ScheduleViewController: UITableViewDataSource { - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int - { + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { viewModel.weekDay.count } func tableView(_ tableView: UITableView, - cellForRowAt indexPath: IndexPath) -> UITableViewCell - { + cellForRowAt indexPath: IndexPath) -> UITableViewCell { guard let cell = tableView.dequeueReusableCell(withIdentifier: "\(WeekDayTableViewCell.self)") as? WeekDayTableViewCell else { return UITableViewCell() } @@ -188,19 +177,16 @@ extension ScheduleViewController: UITableViewDataSource { } //MARK: - UITableViewDelegate -extension ScheduleViewController: UITableViewDelegate -{ +extension ScheduleViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, - heightForRowAt indexPath: IndexPath) -> CGFloat - { + heightForRowAt indexPath: IndexPath) -> CGFloat { ConstantsShedulVc.heightCell } } //MARK: - WeekDayTableViewCellDelegate extension ScheduleViewController: WeekDayTableViewCellDelegate { - func addDayInListkDay(cell: UITableViewCell, flag: Bool) - { + func addDayInListkDay(cell: UITableViewCell, flag: Bool) { guard let cell = cell as? WeekDayTableViewCell, let indexPath = weekDayTableView.indexPath(for: cell) else { return } diff --git a/Tracker/ViewControllers/ViewControllers/SelectCategoriesViewController.swift b/Tracker/ViewControllers/ViewControllers/SelectCategoriesViewController.swift index 427fb4c..2b9db07 100644 --- a/Tracker/ViewControllers/ViewControllers/SelectCategoriesViewController.swift +++ b/Tracker/ViewControllers/ViewControllers/SelectCategoriesViewController.swift @@ -21,17 +21,6 @@ protocol SelectCategoryEditViewControllerDelegate: AnyObject { final class SelectCategoriesViewController: UIViewController { private struct ConstantsCreateCatVc { static let imageViewImageStab = "Star" - - static let categoryLabelText = NSLocalizedString("categoriLabelText", comment: "") - static let categoryAddButtonText = NSLocalizedString("categoriAddButtonText", comment: "") - static let labelStabText = NSLocalizedString("labelStabText", comment: "") - static let textEdit = NSLocalizedString("textEdit", comment: "") - static let textDelete = NSLocalizedString("textDelete", comment: "") - static let textCancel = NSLocalizedString("textCancel", comment: "") - static let textFixed = NSLocalizedString("textFixed", comment: "") - static let sureDeleteTracker = NSLocalizedString("sureDeleteTracker", comment: "") - static let alertDeleteCategoryMessage = NSLocalizedString("deleteCategoryMessage", comment: "") - static let lableTextStabNumberOfLines = 2 static let one = 1 static let selectionCategoryTableViewRowHeight = CGFloat(75) @@ -51,7 +40,7 @@ final class SelectCategoriesViewController: UIViewController { private lazy var categoryLabel: UILabel = { let categoryLabel = UILabel() - categoryLabel.text = ConstantsCreateCatVc.categoryLabelText + categoryLabel.text = Translate.categoryLabelText categoryLabel.textColor = .blackDay categoryLabel.font = ConstantsCreateCatVc.categoryLabelFont categoryLabel.textAlignment = .center @@ -70,7 +59,7 @@ final class SelectCategoriesViewController: UIViewController { private lazy var lableTextStab: UILabel = { let lableTextStab = UILabel() - lableTextStab.text = ConstantsCreateCatVc.labelStabText + lableTextStab.text = Translate.labelStabText lableTextStab.font = ConstantsCreateCatVc.labelStabTextFont lableTextStab.numberOfLines = ConstantsCreateCatVc.lableTextStabNumberOfLines lableTextStab.textAlignment = .center @@ -103,7 +92,7 @@ final class SelectCategoriesViewController: UIViewController { private lazy var createCategoryButton: UIButton = { let createCategoryButton = UIButton() - createCategoryButton.setTitle(ConstantsCreateCatVc.categoryAddButtonText, for: .normal) + createCategoryButton.setTitle(Translate.categoryAddButtonText, for: .normal) createCategoryButton.setTitleColor(.textEventColor, for: .normal) createCategoryButton.backgroundColor = colors.buttonDisabledColor createCategoryButton.titleLabel?.font = ConstantsCreateCatVc.lableFont @@ -117,20 +106,17 @@ final class SelectCategoriesViewController: UIViewController { }() init(delegate: SelectCategoriesViewControllerDelegate, - viewModel: CategoryViewModelProtocol) - { + viewModel: CategoryViewModelProtocol) { self.delegate = delegate self.viewModel = viewModel super.init(nibName: nil, bundle: nil) } - required init?(coder: NSCoder) - { + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - override func viewDidLoad() - { + override func viewDidLoad() { super.viewDidLoad() guard let viewModel = viewModel as? CategoryViewModel else { return } setupUIElement() @@ -150,20 +136,17 @@ final class SelectCategoriesViewController: UIViewController { extension SelectCategoriesViewController { //MARK: - обработка событий @objc - private func didTapСategoryButton() - { + private func didTapСategoryButton() { let newCategoryVC = NewCategoriViewController(createCategorydelegate: self) newCategoryVC.modalPresentationStyle = .formSheet present(newCategoryVC, animated: true) } - private func showStabView(flag: Bool) - { + private func showStabView(flag: Bool) { conteinerStabView.isHidden = flag } - private func bind() - { + private func bind() { guard let viewModel = viewModel as? CategoryViewModel else { return } viewModel.$category.bind { [weak self] _ in guard let self else { return } @@ -175,8 +158,7 @@ extension SelectCategoriesViewController { } private func editAction(text: String, - category: TrackerCategory) -> UIAction - { + category: TrackerCategory) -> UIAction { UIAction(title: text) { [weak self] _ in guard let self else { return } let newCategoryVC = NewCategoriViewController(updateCategoryrDelegate: self) @@ -192,14 +174,13 @@ extension SelectCategoriesViewController { private func deleteAction(text: String, category: TrackerCategory, index: Int, - trackers: [Tracker]) -> UIAction - { + trackers: [Tracker]) -> UIAction { UIAction(title: text, attributes: .destructive) { [weak self] _ in guard let self else { return } let alert = UIAlertController(title: nil, - message: ConstantsCreateCatVc.alertDeleteCategoryMessage, + message: Translate.alertDeleteCategoryMessage, preferredStyle: .actionSheet) - let deleteAction = UIAlertAction(title: ConstantsCreateCatVc.textDelete, + let deleteAction = UIAlertAction(title: Translate.textDelete, style: .destructive) { _ in if let isCategorySelected = self.handler.resultTypeHandlerGetValue(self.viewModel.isCategorySelected(at: index), vc: self), @@ -215,7 +196,7 @@ extension SelectCategoriesViewController { } } } - let cancelAction = UIAlertAction(title: ConstantsCreateCatVc.textCancel, + let cancelAction = UIAlertAction(title: Translate.textCancel, style: .cancel) { _ in alert.dismiss(animated: true) } @@ -226,16 +207,14 @@ extension SelectCategoriesViewController { } //MARK: - SetupUI - private func setupUIElement() - { + private func setupUIElement() { setupCategoryButton() setupCategoryLabel() setupTableView() setupStabView() } - private func setupCategoryButton() - { + private func setupCategoryButton() { view.addSubview(createCategoryButton) NSLayoutConstraint.activate([ @@ -246,8 +225,7 @@ extension SelectCategoriesViewController { ]) } - private func setupCategoryLabel() - { + private func setupCategoryLabel() { view.addSubview(categoryLabel) categoryLabel.translatesAutoresizingMaskIntoConstraints = false @@ -258,8 +236,7 @@ extension SelectCategoriesViewController { ]) } - private func setupTableView() - { + private func setupTableView() { view.addSubview(selectionCategoryTableView) selectionCategoryTableView.translatesAutoresizingMaskIntoConstraints = false @@ -274,8 +251,7 @@ extension SelectCategoriesViewController { ]) } - private func setupStabView() - { + private func setupStabView() { view.addSubview(conteinerStabView) [imageViewStab, lableTextStab].forEach { conteinerStabView.addSubview($0) @@ -300,14 +276,12 @@ extension SelectCategoriesViewController { //MARK: - UITableViewDataSource extension SelectCategoriesViewController: UITableViewDataSource { - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int - { + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { handler.resultTypeHandlerGetValue(viewModel.сategoryExcludingFixed(), vc: self)?.count ?? 0 } - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell - { + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { guard let cell = tableView.dequeueReusableCell(withIdentifier: "\(CustomTableViewCell.self)") as? CustomTableViewCell, let isSelected = handler.resultTypeHandlerGetValue(viewModel.isCategorySelected(at: indexPath.row), @@ -358,8 +332,7 @@ extension SelectCategoriesViewController: UITableViewDataSource { //MARK: - UITableViewDelegate extension SelectCategoriesViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, - didSelectRowAt indexPath: IndexPath) - { + didSelectRowAt indexPath: IndexPath) { guard let cell = tableView.cellForRow(at: indexPath) as? CustomTableViewCell, let delegate else { return } @@ -375,18 +348,16 @@ extension SelectCategoriesViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, - point: CGPoint - ) -> UIContextMenuConfiguration? - { + point: CGPoint) -> UIContextMenuConfiguration? { return UIContextMenuConfiguration(actionProvider: { [weak self] _ in guard let self, let category = handler.resultTypeHandlerGetValue(viewModel.сategoryExcludingFixed(), vc: self)?[indexPath.row] else { return UIMenu()} return UIMenu(children: [ - editAction(text: ConstantsCreateCatVc.textEdit, + editAction(text: Translate.textEdit, category: category), - deleteAction(text: ConstantsCreateCatVc.textDelete, + deleteAction(text: Translate.textDelete, category: category, index: indexPath.row, trackers: category.arrayTrackers) @@ -397,8 +368,7 @@ extension SelectCategoriesViewController: UITableViewDelegate { //MARK: - NewCategoriViewControllerDelegate extension SelectCategoriesViewController: NewCategoriViewControllerDelegate { - func didNewCategoryName(_ vc: UIViewController, nameCategory: String) - { + func didNewCategoryName(_ vc: UIViewController, nameCategory: String) { guard let category = handler.resultTypeHandlerGetValue(viewModel.getCategory(), vc: self) else { return } @@ -419,8 +389,7 @@ extension SelectCategoriesViewController: NewCategoriViewControllerDelegate { extension SelectCategoriesViewController: UpdateCategoriViewControllerDelegate { func didUpdateCategoryName(_ vc: UIViewController, newNameCategory: String, - oldNameCategory: String) - { + oldNameCategory: String) { handler.resultTypeHandler(viewModel.updateNameCategory(newNameCategory: newNameCategory, oldNameCaetegory: oldNameCategory), vc: self) {} diff --git a/Tracker/ViewControllers/ViewControllers/StatisticsViewController.swift b/Tracker/ViewControllers/ViewControllers/StatisticsViewController.swift index fca1d92..42fd390 100644 --- a/Tracker/ViewControllers/ViewControllers/StatisticsViewController.swift +++ b/Tracker/ViewControllers/ViewControllers/StatisticsViewController.swift @@ -8,11 +8,8 @@ import UIKit //MARK: - StatisticsViewController -class StatisticsViewController: UIViewController { +final class StatisticsViewController: UIViewController { private struct ConstantsStatisticVC { - static let headerStatisticText = NSLocalizedString("headerStatisticText", comment: "") - static let secondaryStatisticText = NSLocalizedString("secondaryStatisticText", comment: "") - static let labelStubStatisticsText = NSLocalizedString("labelStubStatisticsText", comment: "") static let imageNothingFound = "NothingFound" static let borderRadius = CGFloat(16) @@ -26,7 +23,7 @@ class StatisticsViewController: UIViewController { private lazy var labelHeader: UILabel = { let labelHeader = UILabel() - labelHeader.text = ConstantsStatisticVC.headerStatisticText + labelHeader.text = Translate.headerStatisticText labelHeader.font = ConstantsStatisticVC.fontLabelHeader labelHeader.translatesAutoresizingMaskIntoConstraints = false labelHeader.backgroundColor = .clear @@ -36,7 +33,7 @@ class StatisticsViewController: UIViewController { private lazy var cardStatysticsView: StatisticsView = { let cardStatistics = StatisticsView() - cardStatistics.setSecondaryTextLable(text: ConstantsStatisticVC.secondaryStatisticText) + cardStatistics.setSecondaryTextLable(text: Translate.secondaryStatisticText) cardStatistics.translatesAutoresizingMaskIntoConstraints = false cardStatistics.backgroundColor = .clear @@ -58,14 +55,13 @@ class StatisticsViewController: UIViewController { private lazy var lableTextStab: UILabel = { let lableTextStab = UILabel() - lableTextStab.text = ConstantsStatisticVC.labelStubStatisticsText + lableTextStab.text = Translate.labelStubStatisticsText lableTextStab.font = ConstantsStatisticVC.fontLabelTextStub return lableTextStab }() - override func viewDidLoad() - { + override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = colors.viewBackground viewModel = StatisticsViewModel() @@ -75,8 +71,7 @@ class StatisticsViewController: UIViewController { setupSubView() } - override func viewDidLayoutSubviews() - { + override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() addBorderGradient(views: [cardStatysticsView], colors: [UIColor.redGradient.cgColor, @@ -88,16 +83,14 @@ class StatisticsViewController: UIViewController { cornerRadius: ConstantsStatisticVC.borderRadius) } - override func viewWillAppear(_ animated: Bool) - { + override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) showStabView(flag: viewModel?.getIsTracker()) } } private extension StatisticsViewController { - func bind() - { + func bind() { guard let viewModel = viewModel as? StatisticsViewModel else { return } viewModel.$countTrackerComplet.bind { [weak self] count in guard let self else { return } @@ -110,8 +103,7 @@ private extension StatisticsViewController { borderWidth: CGFloat, startPoint: CGPoint, endPoint: CGPoint, - cornerRadius: CGFloat) - { + cornerRadius: CGFloat) { views.forEach { $0.layer.cornerRadius = cornerRadius $0.clipsToBounds = true @@ -134,8 +126,7 @@ private extension StatisticsViewController { } } - func showStabView(flag: Bool?) - { + func showStabView(flag: Bool?) { guard let flag else { imageViewStab.isHidden = flag == nil lableTextStab.isHidden = flag == nil @@ -146,15 +137,13 @@ private extension StatisticsViewController { cardStatysticsView.isHidden = flag } - func setupSubView() - { + func setupSubView() { setupLabelHeader() setupStabView() setupCardStatysticsView() } - func setupLabelHeader() - { + func setupLabelHeader() { view.addSubview(labelHeader) NSLayoutConstraint.activate([ labelHeader.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16), @@ -162,8 +151,7 @@ private extension StatisticsViewController { ]) } - func setupStabView() - { + func setupStabView() { [imageViewStab, lableTextStab].forEach { view.addSubview($0) $0.translatesAutoresizingMaskIntoConstraints = false @@ -179,8 +167,7 @@ private extension StatisticsViewController { ]) } - func setupCardStatysticsView() - { + func setupCardStatysticsView() { view.addSubview(cardStatysticsView) NSLayoutConstraint.activate([ cardStatysticsView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16), diff --git a/Tracker/ViewControllers/ViewControllers/TrackersViewController.swift b/Tracker/ViewControllers/ViewControllers/TrackersViewController.swift index a90d34c..e15cd56 100644 --- a/Tracker/ViewControllers/ViewControllers/TrackersViewController.swift +++ b/Tracker/ViewControllers/ViewControllers/TrackersViewController.swift @@ -8,13 +8,13 @@ import UIKit //MARK: - TrackersViewControllerDelegate -protocol TrackersViewControllerDelegate: AnyObject -{ +protocol TrackersViewControllerDelegate: AnyObject { func editTracker(vc: TrackersViewController, editTracker: Tracker, count: Int, nameCategory: String, - indexPath: IndexPath) + indexPath: IndexPath, + isPinned: Bool) } //MARK: - TrackersViewController @@ -26,19 +26,6 @@ final class TrackersViewController: UIViewController { static let imageNothingFound = "NothingFound" static let forKeyTextCancel = "cancelButtonText" - static let headerText = NSLocalizedString("headerText", comment: "") - static let labelStabText = NSLocalizedString("labelStabText", comment: "") - static let labelNothingFoundText = NSLocalizedString("labelNothingFoundText", comment: "") - static let placeholderSearch = NSLocalizedString("placeholderSearch", comment: "") - static let textCancel = NSLocalizedString("textCancel", comment: "") - static let textFix = NSLocalizedString("textFix", comment: "") - static let textUnpin = NSLocalizedString("textUnpin", comment: "") - static let textEdit = NSLocalizedString("textEdit", comment: "") - static let textDelete = NSLocalizedString("textDelete", comment: "") - static let textFixed = NSLocalizedString("textFixed", comment: "") - static let sureDeleteTracker = NSLocalizedString("sureDeleteTracker", comment: "") - static let filters = NSLocalizedString("filters", comment: "") - static let datePickerCornerRadius = CGFloat(8) static let buttonFilterCornerRadius = CGFloat(16) static let heightCollectionView = CGFloat(148) @@ -100,7 +87,7 @@ final class TrackersViewController: UIViewController { private lazy var lableHeader: UILabel = { let lableHeader = UILabel() - lableHeader.text = ConstantsTrackerVc.headerText + lableHeader.text = Translate.headerText lableHeader.font = ConstantsTrackerVc.fontLabelHeader lableHeader.translatesAutoresizingMaskIntoConstraints = false lableHeader.backgroundColor = .clear @@ -131,7 +118,7 @@ final class TrackersViewController: UIViewController { private lazy var lableTextStab: UILabel = { let lableTextStab = UILabel() - lableTextStab.text = ConstantsTrackerVc.labelStabText + lableTextStab.text = Translate.labelStabText lableTextStab.font = ConstantsTrackerVc.fontLableTextStab return lableTextStab @@ -148,7 +135,7 @@ final class TrackersViewController: UIViewController { let buttonFilter = UIButton() buttonFilter.backgroundColor = .blueFilterButton buttonFilter.translatesAutoresizingMaskIntoConstraints = false - buttonFilter.setTitle(ConstantsTrackerVc.filters, for: .normal) + buttonFilter.setTitle(Translate.filters, for: .normal) buttonFilter.addTarget(self, action: #selector(actionButtonFilter), for: .touchUpInside) buttonFilter.titleLabel?.font = ConstantsTrackerVc.fontButtonFilter buttonFilter.layer.cornerRadius = ConstantsTrackerVc.buttonFilterCornerRadius @@ -157,19 +144,16 @@ final class TrackersViewController: UIViewController { return buttonFilter }() - init(viewModel: TrackerViewModelProtocol) - { + init(viewModel: TrackerViewModelProtocol) { super.init(nibName: nil, bundle: nil) } - required init?(coder: NSCoder) - { + required init?(coder: NSCoder) { super.init(coder: coder) assertionFailure("init(coder:) has not been implemented") } - override func viewDidLoad() - { + override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = colors.viewBackground setupUIElement() @@ -186,15 +170,14 @@ final class TrackersViewController: UIViewController { } } - override func viewWillAppear(_ animated: Bool) - { - AnalyticsService.shared.open() + override func viewWillAppear(_ animated: Bool) { + AnalyticsService.reportEvent(field: MetricEvent(event: .open, + params: [EventAnalytics.screen.rawValue: Screen.Main.rawValue])) } } private extension TrackersViewController { - func bind() - { + func bind() { guard let viewModel = viewModel as? TrackerViewModel else { return } viewModel.$category.bind { [weak self] _ in guard let self else { return } @@ -242,9 +225,10 @@ private extension TrackersViewController { //MARK: - обработка событий @objc - func actionButtonFilter() - { - AnalyticsService.shared.filter() + func actionButtonFilter() { + AnalyticsService.reportEvent(field: MetricEvent(event: .click, + params: [EventAnalytics.screen.rawValue: Screen.Main.rawValue, + EventAnalytics.item.rawValue: ItemClick.filter.rawValue])) let filtersVC = FiltersViewController(viewModel: FiltersViewModel(), delegate: self) filtersVC.modalPresentationStyle = .formSheet @@ -252,34 +236,29 @@ private extension TrackersViewController { } @objc - func actionForTapDatePicker() - { + func actionForTapDatePicker() { guard let viewModel else { return } filterState(state: viewModel.getFilterState()) } @objc - func didTapLeftNavBarItem() - { + func didTapLeftNavBarItem() { let greateVC = EventSelectionViewController() greateVC.delegate = self greateVC.modalPresentationStyle = .formSheet present(greateVC, animated: true) } - func chengeStab(text: String, nameImage: String) - { + func chengeStab(text: String, nameImage: String) { lableTextStab.text = text imageViewStab.image = UIImage(named: nameImage) } - func showStabView(flag: Bool) - { + func showStabView(flag: Bool) { [imageViewStab, lableTextStab].forEach { $0.isHidden = flag } } - func filterState(state: FiltersState) - { + func filterState(state: FiltersState) { guard let viewModel else { return } switch state { case .allTrackers: @@ -301,13 +280,11 @@ private extension TrackersViewController { } } - func setColorButtonFilter(state: FiltersState, button: UIButton) - { + func setColorButtonFilter(state: FiltersState, button: UIButton) { button.setTitleColor(state == .allTrackers ? .colorSelection9 : .colorSelection1, for: .normal) } - func greateTrackerCollectionView() -> TrackerCollectionView - { + func greateTrackerCollectionView() -> TrackerCollectionView { let params = GeometricParams(cellCount: ConstantsTrackerVc.cellCount, leftInset: .zero, rightInset: .zero, @@ -319,8 +296,7 @@ private extension TrackersViewController { return trackerCollectionView } - func registerCellAndHeader(collectionView: TrackerCollectionView) - { + func registerCellAndHeader(collectionView: TrackerCollectionView) { collectionView.register(TrackersCollectionViewCell.self, forCellWithReuseIdentifier: "\(TrackersCollectionViewCell.self)") collectionView.register(HeaderReusableView.self, @@ -329,8 +305,7 @@ private extension TrackersViewController { } //MARK: - Menu Action - func pinnedAction(text: String, tracker: Tracker, category: TrackerCategory) -> UIAction - { + func pinnedAction(text: String, tracker: Tracker, category: TrackerCategory) -> UIAction { UIAction(title: text) { [weak self] _ in guard let self, let viewModel, @@ -344,16 +319,16 @@ private extension TrackersViewController { self.handler.resultTypeHandler(viewModel.deleteTracker(id: tracker.id), vc: self) {} } - let filterNameCategory = cat.filter { $0.nameCategory == ConstantsTrackerVc.textFixed } + let filterNameCategory = cat.filter { $0.nameCategory == Translate.textFixed } if let _ = filterNameCategory.first?.nameCategory { self.handler.resultTypeHandler(viewModel.addNewTracker(tracker, - nameCategory: ConstantsTrackerVc.textFixed), + nameCategory: Translate.textFixed), vc: self) {} } else { - self.handler.resultTypeHandler(viewModel.addCategory(nameCategory: ConstantsTrackerVc.textFixed), + self.handler.resultTypeHandler(viewModel.addCategory(nameCategory: Translate.textFixed), vc: self) { self.handler.resultTypeHandler(viewModel.addNewTracker(tracker, - nameCategory: ConstantsTrackerVc.textFixed), + nameCategory: Translate.textFixed), vc: self) {} } } @@ -382,8 +357,7 @@ private extension TrackersViewController { func editTrackerAction(text: String, tracker: Tracker, category: TrackerCategory, - indexPath: IndexPath) -> UIAction - { + indexPath: IndexPath) -> UIAction { UIAction(title: text) { [weak self] _ in guard let self, let viewModel @@ -392,41 +366,49 @@ private extension TrackersViewController { let createTrackerVC = CreateTrackerViewController(viewModel: editViewModel, updateTrackerDelegate: self) delegate = createTrackerVC - if tracker.schedule.count != editViewModel.regular.count { + if tracker.schedule.count != DataSource.regular.count { createTrackerVC.reverseIsSchedul() } createTrackerVC.modalPresentationStyle = .formSheet present(createTrackerVC, animated: true) { let count = self.handler.resultTypeHandlerGetValue(viewModel.getCountTrackerCompleted(id: tracker.id), vc: self) - guard let count else { return } + let pinnedCategory = self.handler.resultTypeHandlerGetValue(viewModel.getPinnedCategory(tracker.id), + vc: self) + let nameCategory = category.isPinned ? category.nameCategory : pinnedCategory + guard let count + else { return } self.delegate?.editTracker(vc: self, editTracker: tracker, count: count, - nameCategory: category.nameCategory, - indexPath: indexPath) + nameCategory: (nameCategory ?? category.nameCategory) ?? category.nameCategory, + indexPath: indexPath, + isPinned: category.isPinned) } - AnalyticsService.shared.edit() + AnalyticsService.reportEvent(field: MetricEvent(event: .click, + params: [EventAnalytics.screen.rawValue: Screen.Main.rawValue, + EventAnalytics.item.rawValue: ItemClick.edit.rawValue])) } } - func deleteAction(text: String, tracker: Tracker) -> UIAction - { + func deleteAction(text: String, tracker: Tracker) -> UIAction { UIAction(title: text, attributes: .destructive) { [weak self] _ in guard let self, let viewModel else { return } let alert = UIAlertController(title: nil, - message: ConstantsTrackerVc.sureDeleteTracker, + message: Translate.sureDeleteTracker, preferredStyle: .actionSheet) - let deleteAction = UIAlertAction(title: ConstantsTrackerVc.textDelete, + let deleteAction = UIAlertAction(title: Translate.textDelete, style: .destructive) { _ in self.handler.resultTypeHandler(viewModel.deleteTrackersRecord(id: tracker.id), vc: self) {} self.handler.resultTypeHandler(viewModel.deleteTracker(id: tracker.id), vc: self) {} - AnalyticsService.shared.delete() + AnalyticsService.reportEvent(field: MetricEvent(event: .click, + params: [EventAnalytics.screen.rawValue: Screen.Main.rawValue, + EventAnalytics.item.rawValue: ItemClick.edit.rawValue])) } - let cancelAction = UIAlertAction(title: ConstantsTrackerVc.textCancel, + let cancelAction = UIAlertAction(title: Translate.textCancel, style: .cancel) { _ in alert.dismiss(animated: true) } @@ -445,8 +427,7 @@ private extension TrackersViewController { setupButtonFilter() } - func setupNavBarItem() - { + func setupNavBarItem() { guard let navBar = navigationController?.navigationBar, let topItem = navBar.topItem else { return } @@ -459,7 +440,7 @@ private extension TrackersViewController { topItem.leftBarButtonItem?.tintColor = colors.whiteBlackItemColor navBar.backgroundColor = .clear - navigationItem.title = ConstantsTrackerVc.headerText + navigationItem.title = Translate.headerText navigationController?.navigationBar.prefersLargeTitles = true let searcheController = UISearchController(searchResultsController: nil) @@ -467,15 +448,14 @@ private extension TrackersViewController { searcheController.obscuresBackgroundDuringPresentation = false searcheController.hidesNavigationBarDuringPresentation = false navigationItem.searchController = searcheController - searcheController.searchBar.placeholder = ConstantsTrackerVc.placeholderSearch + searcheController.searchBar.placeholder = Translate.placeholderSearch searcheController.searchBar.resignFirstResponder() searcheController.searchBar.returnKeyType = .search - searcheController.searchBar.setValue(ConstantsTrackerVc.textCancel, + searcheController.searchBar.setValue(Translate.textCancel, forKey: ConstantsTrackerVc.forKeyTextCancel) } - func setupContentView() - { + func setupContentView() { trackerCollectionView.translatesAutoresizingMaskIntoConstraints = false contentView.addSubview(trackerCollectionView) @@ -487,8 +467,7 @@ private extension TrackersViewController { ]) } - func setupVerticalSteck() - { + func setupVerticalSteck() { view.addSubview(verticalStack) contentView.translatesAutoresizingMaskIntoConstraints = false contentView.backgroundColor = .clear @@ -504,8 +483,7 @@ private extension TrackersViewController { ]) } - func setupStabView() - { + func setupStabView() { [imageViewStab, lableTextStab].forEach { view.addSubview($0) $0.translatesAutoresizingMaskIntoConstraints = false @@ -522,8 +500,7 @@ private extension TrackersViewController { ]) } - func setupButtonFilter() - { + func setupButtonFilter() { view.addSubview(buttonFilter) NSLayoutConstraint.activate([ @@ -539,8 +516,7 @@ private extension TrackersViewController { //MARK: - UICollectionViewDataSource extension TrackersViewController: UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, - numberOfItemsInSection section: Int) -> Int - { + numberOfItemsInSection section: Int) -> Int { guard let viewModel = viewModel as? TrackerViewModel, let itemsCount = handler.resultTypeHandlerGetValue(viewModel.visibleCategory, vc: self)?[section].arrayTrackers.count @@ -548,8 +524,7 @@ extension TrackersViewController: UICollectionViewDataSource { return itemsCount } - func numberOfSections(in collectionView: UICollectionView) -> Int - { + func numberOfSections(in collectionView: UICollectionView) -> Int { guard let viewModel = viewModel as? TrackerViewModel, let sectionCount = handler.resultTypeHandlerGetValue(viewModel.visibleCategory, vc: self)?.count @@ -558,8 +533,7 @@ extension TrackersViewController: UICollectionViewDataSource { } func collectionView(_ collectionView: UICollectionView, - cellForItemAt indexPath: IndexPath) -> UICollectionViewCell - { + cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "\(TrackersCollectionViewCell.self)", for: indexPath) as? TrackersCollectionViewCell, let viewModel = viewModel as? TrackerViewModel, @@ -603,8 +577,7 @@ extension TrackersViewController: UICollectionViewDataSource { extension TrackersViewController: UICollectionViewDelegate { func collectionView(_ collectionView: UICollectionView, contextMenuConfiguration configuration: UIContextMenuConfiguration, - highlightPreviewForItemAt indexPath: IndexPath) -> UITargetedPreview? - { + highlightPreviewForItemAt indexPath: IndexPath) -> UITargetedPreview? { guard let cell = trackerCollectionView.cellForItem(at: indexPath) as? TrackersCollectionViewCell else { return nil } let previewTarget = UIPreviewTarget(container: self.trackerCollectionView, @@ -614,8 +587,7 @@ extension TrackersViewController: UICollectionViewDelegate { target: previewTarget) } - func scrollViewDidScroll(_ scrollView: UIScrollView) - { + func scrollViewDidScroll(_ scrollView: UIScrollView) { guard let flag = viewModel?.getIsCategoryForDay(), flag else { return } let offsetY = scrollView.contentOffset.y if offsetY > 0 { @@ -630,8 +602,7 @@ extension TrackersViewController: UICollectionViewDelegate { func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemsAt indexPaths: [IndexPath], - point: CGPoint) -> UIContextMenuConfiguration? - { + point: CGPoint) -> UIContextMenuConfiguration? { guard let indexPath = indexPaths.first, let viewModel, let visibleCategories = handler.resultTypeHandlerGetValue(viewModel.visibleCategory, @@ -639,9 +610,9 @@ extension TrackersViewController: UICollectionViewDelegate { else { return nil } let selectorCategory = visibleCategories[indexPath.section] let selectedTracker = selectorCategory.arrayTrackers[indexPath.row] - let firstContextText = !selectorCategory.isPinned ? ConstantsTrackerVc.textUnpin : ConstantsTrackerVc.textFix - let secondContentText = ConstantsTrackerVc.textEdit - let thirdContentText = ConstantsTrackerVc.textDelete + let firstContextText = !selectorCategory.isPinned ? Translate.textUnpin : Translate.textFix + let secondContentText = Translate.textEdit + let thirdContentText = Translate.textDelete return UIContextMenuConfiguration(actionProvider: { [weak self] _ in guard let self else { return UIMenu() } return UIMenu(children: [ @@ -662,8 +633,7 @@ extension TrackersViewController: UICollectionViewDelegate { extension TrackersViewController: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, - sizeForItemAt indexPath: IndexPath) -> CGSize - { + sizeForItemAt indexPath: IndexPath) -> CGSize { let availableWidth = collectionView.frame.width - trackerCollectionView.params.paddingWidth let cellWidth = availableWidth / CGFloat(trackerCollectionView.params.cellCount) let sizeCell = CGSize(width: cellWidth, height: ConstantsTrackerVc.heightCollectionView) @@ -673,16 +643,14 @@ extension TrackersViewController: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, - insetForSectionAt section: Int) -> UIEdgeInsets - { + insetForSectionAt section: Int) -> UIEdgeInsets { UIEdgeInsets(top: 10, left: trackerCollectionView.params.leftInset, bottom: 16, right: trackerCollectionView.params.rightInset) } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, - referenceSizeForHeaderInSection section: Int) -> CGSize - { + referenceSizeForHeaderInSection section: Int) -> CGSize { guard let viewModel = viewModel as? TrackerViewModel, let visibleCategories = handler.resultTypeHandlerGetValue(viewModel.visibleCategory, vc: self) @@ -700,8 +668,7 @@ extension TrackersViewController: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, - at indexPath: IndexPath) -> UICollectionReusableView - { + at indexPath: IndexPath) -> UICollectionReusableView { switch kind { case UICollectionView.elementKindSectionHeader: guard let supplementary = collectionView.dequeueReusableSupplementaryView(ofKind: kind, @@ -712,7 +679,7 @@ extension TrackersViewController: UICollectionViewDelegateFlowLayout { else { return UICollectionReusableView() } let nameCategory = visibleCategories[indexPath.section].nameCategory if nameCategory == Fixed.fixedRu.rawValue || nameCategory == Fixed.fixedEng.rawValue { - supplementary.setTextLable(text: ConstantsTrackerVc.textFixed) + supplementary.setTextLable(text: Translate.textFixed) return supplementary } supplementary.setTextLable(text: nameCategory) @@ -725,8 +692,7 @@ extension TrackersViewController: UICollectionViewDelegateFlowLayout { //MARK: - UISearchResultsUpdating extension TrackersViewController: UISearchResultsUpdating { - func updateSearchResults(for searchController: UISearchController) - { + func updateSearchResults(for searchController: UISearchController) { guard let word = searchController.searchBar.text, let viewModel else { return } @@ -740,21 +706,20 @@ extension TrackersViewController: UISearchResultsUpdating { if word.isEmpty { let _ = viewModel.getShowListTrackersForDay(date: datePicker.date) - chengeStab(text: ConstantsTrackerVc.labelNothingFoundText, + chengeStab(text: Translate.labelNothingFoundText, nameImage: ConstantsTrackerVc.imageNothingFound) } if !searchController.isActive { let _ = viewModel.getShowListTrackersForDay(date: datePicker.date) - chengeStab(text: ConstantsTrackerVc.labelStabText, + chengeStab(text: Translate.labelStabText, nameImage: ConstantsTrackerVc.imageStar) } } } //MARK: - TrackersCollectionViewCellDelegate -extension TrackersViewController: TrackersCollectionViewCellDelegate -{ +extension TrackersViewController: TrackersCollectionViewCellDelegate { func didTrackerCompleted(_ cell: UICollectionViewCell) { guard let trackerCell = cell as? TrackersCollectionViewCell, let indexPath = trackerCollectionView.indexPath(for: trackerCell), @@ -767,7 +732,9 @@ extension TrackersViewController: TrackersCollectionViewCellDelegate handler.resultTypeHandler(viewModel.updateCompletedTrackers(tracker: tracker, date: datePicker.date), vc: self) {} - AnalyticsService.shared.track() + AnalyticsService.reportEvent(field: MetricEvent(event: .click, + params: [EventAnalytics.screen.rawValue: Screen.Main.rawValue, + EventAnalytics.item.rawValue: ItemClick.track.rawValue])) } } @@ -775,25 +742,22 @@ extension TrackersViewController: TrackersCollectionViewCellDelegate extension TrackersViewController: EventSelectionViewControllerDelegate { func eventSelectionViewController(vc: UIViewController, nameCategories: String, - tracker: Tracker) - { + tracker: Tracker) { guard let viewModel else { return } handler.resultTypeHandler(viewModel.addNewTracker(tracker, nameCategory: nameCategories), vc: self) {} } - func eventSelectionViewControllerDidCancel(_ vc: EventSelectionViewController) - { + func eventSelectionViewControllerDidCancel(_ vc: EventSelectionViewController) { vc.dismiss(animated: false) } } //MARK: - UpdateTrackerViewControllerDelegate -extension TrackersViewController: UpdateTrackerViewControllerDelegate -{ +extension TrackersViewController: UpdateTrackerViewControllerDelegate { func trackerViewController(vc: UIViewController, - nameCategory: String, + nameCategory: String?, tracker: Tracker) { guard let viewModel else { return } handler.resultTypeHandler(viewModel.updateTracker(tracker: tracker, @@ -801,15 +765,13 @@ extension TrackersViewController: UpdateTrackerViewControllerDelegate vc: self) {} } - func trackerViewControllerDidCancel(_ vc: CreateTrackerViewController) - { + func trackerViewControllerDidCancel(_ vc: CreateTrackerViewController) { vc.dismiss(animated: true) } } //MARK: - FiltersViewControllerDelegate -extension TrackersViewController: FiltersViewControllerDelegate -{ +extension TrackersViewController: FiltersViewControllerDelegate { func filter(vc: FiltersViewController, filterState: FiltersState) { guard let viewModel else { return } viewModel.setFilterState(state: filterState) diff --git a/Tracker/ViewModels/CategoryViewModel.swift b/Tracker/ViewModels/CategoryViewModel.swift index 5404ea7..d2f159e 100644 --- a/Tracker/ViewModels/CategoryViewModel.swift +++ b/Tracker/ViewModels/CategoryViewModel.swift @@ -51,8 +51,7 @@ final class CategoryViewModel { init(trackerCategoryStore: TrackerCategoryStoreProtocol, trackerStore: TrackerStoreProtocol, trackerRecordStore: TrackerRecordStoreProtocol, - category: Result<[TrackerCategory], Error>) - { + category: Result<[TrackerCategory], Error>) { self.trackerCategoryStore = trackerCategoryStore self.trackerStore = trackerStore self.trackerRecordStore = trackerRecordStore @@ -61,8 +60,7 @@ final class CategoryViewModel { } //MARK: - Extension private extension CategoryViewModel { - func category(from trackerCategoryCoreData: TrackerCategoryCoreData) throws -> TrackerCategory - { + func category(from trackerCategoryCoreData: TrackerCategoryCoreData) throws -> TrackerCategory { guard let nameCategory = trackerCategoryCoreData.nameCategory else { throw StoreErrors.TrackrerCategoryStoreError.decodingErrorInvalidNameCategory } @@ -93,8 +91,7 @@ extension CategoryViewModel: CategoryViewModelProtocol { oldNameCategory: oldNameCaetegory) } - func isCategorySelected(at index: Int) -> Result - { + func isCategorySelected(at index: Int) -> Result { switch сategoryExcludingFixed() { case .success(let category): return .success(category[index].nameCategory != selectNameCategory) @@ -103,8 +100,7 @@ extension CategoryViewModel: CategoryViewModelProtocol { } } - func getCategory() -> Result<[TrackerCategory], Error> - { + func getCategory() -> Result<[TrackerCategory], Error> { let trackerCategoryCoreData = trackerCategoryStore.getListTrackerCategoryCoreData() guard let trackerCategoryCoreData else { return .failure(StoreErrors.TrackrerStoreError.getTrackerError) } @@ -116,8 +112,7 @@ extension CategoryViewModel: CategoryViewModelProtocol { } } - func сategoryExcludingFixed() -> Result<[TrackerCategory], Error> - { + func сategoryExcludingFixed() -> Result<[TrackerCategory], Error> { switch category { case .success(let category): let filterCat = category.filter { @@ -130,8 +125,7 @@ extension CategoryViewModel: CategoryViewModelProtocol { } } - func createNameCategory(at index: Int) -> Result - { + func createNameCategory(at index: Int) -> Result { switch сategoryExcludingFixed() { case .success(let category): let name = category[index].nameCategory @@ -153,8 +147,7 @@ extension CategoryViewModel: CategoryViewModelProtocol { trackerRecordStore.deleteTrackerRecords(trackers: trackers) } - func selectСategory(at index: Int) -> Result - { + func selectСategory(at index: Int) -> Result { switch сategoryExcludingFixed() { case .success(let category): print(category[index].nameCategory) diff --git a/Tracker/ViewModels/EditTrackerViewModel.swift b/Tracker/ViewModels/EditTrackerViewModel.swift index 5951308..c856fbe 100644 --- a/Tracker/ViewModels/EditTrackerViewModel.swift +++ b/Tracker/ViewModels/EditTrackerViewModel.swift @@ -10,9 +10,6 @@ import UIKit protocol EditTrackerModelProtocol { var isSchedul: Bool { get } var listSettings: [ChoiceParametrs] { get } - var dataSection: [Header] { get } - var emojies: [String] { get } - var colors: [UIColor] { get } func reverseIsSchedul() func checkingForEmptiness() -> Bool @@ -26,50 +23,43 @@ protocol EditTrackerModelProtocol { func setNameNewCategory(_ vc: CreateTrackerViewController, nameCategory: String) func setNameTracker(_ vc: CreateTrackerViewController, nameTracker: String) func setEmojiTracker(_ vc: CreateTrackerViewController, emoji: String) - func editTracker(vc: CreateTrackerViewController, editTracker: Tracker, nameCategory: String) + func editPinnedCategory(_ id: UUID, newPinnedCat: String) -> Result + func editTracker(vc: CreateTrackerViewController, editTracker: Tracker, nameCategory: String, isPinned: Bool) } //MARK: - EditTrackerViewModel final class EditTrackerViewModel { var isSchedul: Bool = true var listSettings: [ChoiceParametrs] { isSchedul ? [.category] : [.category, .schedule] } - let dataSection: [Header] = [.emoji, .color] - let emojies: [String] = ["🙂", "😻", "🌺", "🐶", "❤️", "😱", "😇", "😡", "🥶", - "🤔", "🙌", "🍔", "🥦", "🏓", "🥇", "🎸", "🏝️", "😪"] - - let colors: [UIColor] = [.colorSelection1, .colorSelection2, .colorSelection3,.colorSelection4, - .colorSelection5, .colorSelection6, .colorSelection7, .colorSelection8, - .colorSelection9, .colorSelection10, .colorSelection11, .colorSelection12, - .colorSelection13, .colorSelection14, .colorSelection15, .colorSelection16, - .colorSelection17, .colorSelection18] - - let regular: [WeekDay] = [.monday, .tuesday, .wednesday, .thursday, .friday, .saturday, .sunday] @Observable<[WeekDay]> private(set) var schedule: [WeekDay] = [] @Observable private(set) var color: UIColor? @Observable private(set) var nameNewCategory: String = "" @Observable private(set) var nameTracker: String = "" @Observable private(set) var emoji: String = "" + private(set) var isPinned: Bool = false private(set) var id: UUID? private let marshalling: UIColorMarshalling + private let pinnedCategoryStore: PinnedCategoryStoreProtocol - convenience init() - { + convenience init() { let marshalling = UIColorMarshalling() - self.init(marshalling: marshalling) + let pinnedCategoryStore = PinnedCategoryStore() + self.init(marshalling: marshalling, + pinnedCategoryStore: pinnedCategoryStore) } - init(marshalling: UIColorMarshalling) - { + init(marshalling: UIColorMarshalling, + pinnedCategoryStore: PinnedCategoryStoreProtocol) { self.marshalling = marshalling + self.pinnedCategoryStore = pinnedCategoryStore } } //MARK: - EditTrackerViewModel extension EditTrackerViewModel: EditTrackerModelProtocol { - func checkingForEmptiness() -> Bool - { + func checkingForEmptiness() -> Bool { var flag: Bool if isSchedul { flag = !nameTracker.isEmpty && @@ -92,10 +82,9 @@ extension EditTrackerViewModel: EditTrackerModelProtocol { } func jonedSchedule(schedule: [WeekDay], - stringArrayDay: String) -> String - { + stringArrayDay: String) -> String { var stringListDay: String - if schedule.count == regular.count { + if schedule.count == DataSource.regular.count { stringListDay = stringArrayDay return stringListDay } @@ -109,10 +98,9 @@ extension EditTrackerViewModel: EditTrackerModelProtocol { schedule = listWeekDay } - func getColorRow(color: UIColor) -> Int - { + func getColorRow(color: UIColor) -> Int { var colorRow: Int = 0 - for (index, value) in colors.enumerated() { + for (index, value) in DataSource.colors.enumerated() { let colorMarshalling = marshalling.color(from: marshalling.hexString(from: value)) if colorMarshalling == color { colorRow = index @@ -121,10 +109,9 @@ extension EditTrackerViewModel: EditTrackerModelProtocol { return colorRow } - func getEmojiRow(emoji: String) -> Int - { + func getEmojiRow(emoji: String) -> Int { var emojiRow: Int = 0 - for (index, value) in emojies.enumerated() { + for (index, value) in DataSource.emojies.enumerated() { if value == emoji { emojiRow = index } @@ -152,16 +139,21 @@ extension EditTrackerViewModel: EditTrackerModelProtocol { self.emoji = emoji } + func editPinnedCategory(_ id: UUID, newPinnedCat: String) -> Result { + pinnedCategoryStore.editPinnedCategory(id, newPinnedCat: newPinnedCat) + } + func editTracker(vc: CreateTrackerViewController, editTracker: Tracker, - nameCategory: String) - { + nameCategory: String, + isPinned: Bool) { schedule = editTracker.schedule color = editTracker.color nameNewCategory = nameCategory nameTracker = editTracker.name emoji = editTracker.emoji id = editTracker.id + self.isPinned = isPinned } } diff --git a/Tracker/ViewModels/StatisticViewModel.swift b/Tracker/ViewModels/StatisticViewModel.swift index be25198..792e2d4 100644 --- a/Tracker/ViewModels/StatisticViewModel.swift +++ b/Tracker/ViewModels/StatisticViewModel.swift @@ -17,11 +17,10 @@ final class StatisticsViewModel { @UserDefaultsBacked(key: UserDefaultKeys.isTracker.rawValue) private(set) var isTracker: Bool? - + let trackerRecordStore: TrackerRecordStoreProtocol - convenience init() - { + convenience init() { let trackerRecordStore = TrackerRecordStore() self.init(trackerRecordStore: trackerRecordStore) countTrackerComplet = getCountTrackerComplet() diff --git a/Tracker/ViewModels/TrackerViewModel.swift b/Tracker/ViewModels/TrackerViewModel.swift index 4a5cd6f..807f331 100644 --- a/Tracker/ViewModels/TrackerViewModel.swift +++ b/Tracker/ViewModels/TrackerViewModel.swift @@ -18,7 +18,7 @@ protocol TrackerViewModelProtocol { func deleteTrackersRecord(id: UUID) -> Result func addNewTracker(_ tracker: Tracker, nameCategory: String) -> Result - func updateTracker(tracker: Tracker, nameCategory: String) -> Result + func updateTracker(tracker: Tracker, nameCategory: String?) -> Result func deleteTracker(id: UUID) -> Result func getShowListTrackersForDay(date: Date) @@ -26,6 +26,8 @@ protocol TrackerViewModelProtocol { func filterListTrackersName(word: String) -> Result<[TrackerCategory], Error> func addPinnedCategory(id: UUID, nameCategory: String) -> Result + func editPinnedCategory(_ id: UUID, newPinnedCat: String) -> Result + func getPinnedCategory(_ id: UUID) -> Result func deleteAndGetPinnedCategory(id: UUID) -> Result func allTrackersByDate(date: Date) @@ -40,8 +42,6 @@ protocol TrackerViewModelProtocol { //MARK: - TrackerViewModel final class TrackerViewModel { - private let textFixed = NSLocalizedString("textFixed", comment: "") - @Observable> private(set) var category: Result<[TrackerCategory], Error> @@ -68,8 +68,7 @@ final class TrackerViewModel { private let trackerStore: TrackerStoreProtocol private let pinnedCategoryStore: PinnedCategoryStoreProtocol - convenience init() - { + convenience init() { let trackerCategoryStore = TrackerCategoryStore() let trackerRecordStore = TrackerRecordStore() let trackerStore = TrackerStore() @@ -102,8 +101,7 @@ final class TrackerViewModel { category: Result<[TrackerCategory], Error>, completedTrackers: Result, Error>, visibleCategory: Result<[TrackerCategory], Error>, - indexPath: IndexPath, filterState: FiltersState) - { + indexPath: IndexPath, filterState: FiltersState) { self.trackerCategoryStore = trackerCategoryStore self.trackerRecordStore = trackerRecordStore self.trackerStore = trackerStore @@ -117,8 +115,7 @@ final class TrackerViewModel { } //MARK: - Extension private extension TrackerViewModel { - func category(from trackerCategoryCoreData: TrackerCategoryCoreData) throws -> TrackerCategory - { + func category(from trackerCategoryCoreData: TrackerCategoryCoreData) throws -> TrackerCategory { guard let nameCategory = trackerCategoryCoreData.nameCategory else { throw StoreErrors.TrackrerCategoryStoreError.decodingErrorInvalidNameCategory } @@ -136,8 +133,7 @@ private extension TrackerViewModel { } } - func setVisibleCategory(date: Date, flag: Bool) - { + func setVisibleCategory(date: Date, flag: Bool) { switch category { case .success(let category): var listCategories: [TrackerCategory] = [] @@ -163,8 +159,7 @@ private extension TrackerViewModel { } func filterListTrackersWeekDay(trackerCategory: [TrackerCategory], - date: Date) -> [TrackerCategory] - { + date: Date) -> [TrackerCategory] { let calendar = Calendar.current let filter = calendar.component(.weekday, from: date) @@ -203,8 +198,7 @@ private extension TrackerViewModel { func trackerCompletedOrNotByCategory(category: TrackerCategory, date: Date, flag: Bool, - filter: (UUID, Date) throws -> Bool) throws -> TrackerCategory - { + filter: (UUID, Date) throws -> Bool) throws -> TrackerCategory { let trackers = category.arrayTrackers do { let trackers = try trackers.filter { try flag == filter($0.id, date) } @@ -233,8 +227,7 @@ extension TrackerViewModel: TrackerViewModelProtocol trackerCategoryStore.addCategory(nameCategory) } - func getCategory() -> Result<[TrackerCategory], Error> - { + func getCategory() -> Result<[TrackerCategory], Error> { let trackerCategoryCoreData = trackerCategoryStore.getListTrackerCategoryCoreData() guard let trackerCategoryCoreData else { return .failure(StoreErrors.TrackrerStoreError.getTrackerError) } @@ -255,19 +248,17 @@ extension TrackerViewModel: TrackerViewModelProtocol } func addNewTracker(_ tracker: Tracker, - nameCategory: String) -> Result - { + nameCategory: String) -> Result { let trackerCoreData = trackerStore.addNewTracker(tracker, nameCategory: nameCategory) return trackerCoreData } - func updateTracker(tracker: Tracker, nameCategory: String) -> Result{ + func updateTracker(tracker: Tracker, nameCategory: String?) -> Result { trackerStore.updateTracker(tracker: tracker, nameCategory: nameCategory) } - func getShowListTrackersForDay(date: Date) - { + func getShowListTrackersForDay(date: Date) { switch category { case .success(let trackerCategory): let filterList = filterListTrackersWeekDay(trackerCategory: trackerCategory, @@ -278,8 +269,7 @@ extension TrackerViewModel: TrackerViewModelProtocol } } - func filterListTrackersName(word: String) -> Result<[TrackerCategory], Error> - { + func filterListTrackersName(word: String) -> Result<[TrackerCategory], Error> { var listCategories: [TrackerCategory] = [] switch category { case .success(let cat): @@ -305,8 +295,7 @@ extension TrackerViewModel: TrackerViewModelProtocol visibleCategory = .success(searchCategory) } - func getIsComplited(tracker: Tracker, date: Date) -> Result - { + func getIsComplited(tracker: Tracker, date: Date) -> Result { switch completedTrackers { case .success(let compTrack): return .success(compTrack.contains (where: { $0.id == tracker.id && @@ -322,8 +311,7 @@ extension TrackerViewModel: TrackerViewModelProtocol self.indexPath = indexPath } - func addPinnedCategory(id: UUID, nameCategory: String) -> Result - { + func addPinnedCategory(id: UUID, nameCategory: String) -> Result { let pinnCategoryCoreData = pinnedCategoryStore.addPinnedCategory(nameCategory) switch pinnCategoryCoreData { case .success(let pinnCategoryCoreData): @@ -334,6 +322,14 @@ extension TrackerViewModel: TrackerViewModelProtocol } } + func editPinnedCategory(_ id: UUID, newPinnedCat: String) -> Result { + pinnedCategoryStore.editPinnedCategory(id, newPinnedCat: newPinnedCat) + } + + func getPinnedCategory(_ id: UUID) -> Result { + pinnedCategoryStore.getPinnedCategory(id) + } + func deleteAndGetPinnedCategory(id: UUID) -> Result { pinnedCategoryStore.deleteAndGetPinnedCategory(id) } diff --git a/Tracker/Views/CollectionViewCell/ColorCollectionViewCell.swift b/Tracker/Views/CollectionViewCell/ColorCollectionViewCell.swift index 62fa638..6aa8bf5 100644 --- a/Tracker/Views/CollectionViewCell/ColorCollectionViewCell.swift +++ b/Tracker/Views/CollectionViewCell/ColorCollectionViewCell.swift @@ -32,14 +32,12 @@ final class ColorCollectionViewCell: UICollectionViewCell { return colorView }() - override init(frame: CGRect) - { + override init(frame: CGRect) { super.init(frame: frame) setupColorView() } - required init?(coder: NSCoder) - { + required init?(coder: NSCoder) { super.init(coder: coder) assertionFailure("init(coder:) has not been implemented") } @@ -47,13 +45,11 @@ final class ColorCollectionViewCell: UICollectionViewCell { extension ColorCollectionViewCell { //MARK: - Config - func configColor(color: UIColor) - { + func configColor(color: UIColor) { colorView.backgroundColor = color } - func colorSelection(color: UIColor, flag: Bool) - { + func colorSelection(color: UIColor, flag: Bool) { contentColorView.layer.cornerRadius = flag ? ConstantsCell.cornerRadius : 0 contentColorView.layer.borderWidth = flag ? ConstantsCell.borderWidth : 0 contentColorView.layer.masksToBounds = flag @@ -61,8 +57,7 @@ extension ColorCollectionViewCell { } //MARK: - SetupUI - private func setupColorView() - { + private func setupColorView() { addSubview(contentColorView) contentColorView.addSubview(colorView) diff --git a/Tracker/Views/CollectionViewCell/EmojiCollectionViewCell.swift b/Tracker/Views/CollectionViewCell/EmojiCollectionViewCell.swift index 5597036..930454b 100644 --- a/Tracker/Views/CollectionViewCell/EmojiCollectionViewCell.swift +++ b/Tracker/Views/CollectionViewCell/EmojiCollectionViewCell.swift @@ -26,14 +26,12 @@ final class EmojiCollectionViewCell: UICollectionViewCell { return emojiView }() - override init(frame: CGRect) - { + override init(frame: CGRect) { super.init(frame: frame) setupColorView() } - required init?(coder: NSCoder) - { + required init?(coder: NSCoder) { super.init(coder: coder) assertionFailure("init(coder:) has not been implemented") } @@ -41,20 +39,17 @@ final class EmojiCollectionViewCell: UICollectionViewCell { extension EmojiCollectionViewCell { //MARK: - Config - func configEmoji(emoji: String) - { + func configEmoji(emoji: String) { emojiView.text = emoji } //MARK: - Selected - func emojiSelection(isBackground: Bool) - { + func emojiSelection(isBackground: Bool) { emojiView.backgroundColor = isBackground ? .grayDay : .clear } //MARK: - SetupUI - private func setupColorView() - { + private func setupColorView() { addSubview(emojiView) NSLayoutConstraint.activate([ diff --git a/Tracker/Views/CollectionViewCell/TrackersCollectionViewCell.swift b/Tracker/Views/CollectionViewCell/TrackersCollectionViewCell.swift index c25af2c..0fa5137 100644 --- a/Tracker/Views/CollectionViewCell/TrackersCollectionViewCell.swift +++ b/Tracker/Views/CollectionViewCell/TrackersCollectionViewCell.swift @@ -18,7 +18,6 @@ final class TrackersCollectionViewCell: UICollectionViewCell { static let addButtonImageAdd = "Add" static let adButttonImageDone = "Done" static let isPinnedImage = "IsPinned" - static let numberOfDays = NSLocalizedString("numberOfDays", tableName: "LocalizableDict", comment: "") static let numberOfLines = 2 @@ -125,14 +124,12 @@ final class TrackersCollectionViewCell: UICollectionViewCell { return verticalStack }() - override init(frame: CGRect) - { + override init(frame: CGRect) { super.init(frame: frame) setupUIElement() } - required init?(coder: NSCoder) - { + required init?(coder: NSCoder) { super.init(coder: coder) } } @@ -140,8 +137,7 @@ final class TrackersCollectionViewCell: UICollectionViewCell { extension TrackersCollectionViewCell { //MARK: - Обработка событий @objc - private func didTapAddButton() - { + private func didTapAddButton() { guard let delegate else { return } delegate.didTrackerCompleted(self) } @@ -149,9 +145,8 @@ extension TrackersCollectionViewCell { //MARK: - Configuration //метод обновления счетчика выполнения трекера и imageButton - func updateLableCountAndImageAddButton(_ updateModel: UpdateTracker) - { - let textLable = String.localizedStringWithFormat(ConstantsTrackerCell.numberOfDays, + func updateLableCountAndImageAddButton(_ updateModel: UpdateTracker) { + let textLable = String.localizedStringWithFormat(Translate.numberOfDays, updateModel.count) dayCounterLable.text = textLable switch updateModel.flag { @@ -164,50 +159,42 @@ extension TrackersCollectionViewCell { } } - func updateBackgraundAddButton(isHidden: Bool) - { + func updateBackgraundAddButton(isHidden: Bool) { backgroundAddButtonView.isHidden = isHidden } - func setPinned(flag: Bool) - { + func setPinned(flag: Bool) { isPinnedImageView.isHidden = flag } - func isEnableAddButton(flag: Bool) - { + func isEnableAddButton(flag: Bool) { addButton.isEnabled = flag } - private func setIsSelectedAddButton(flag: Bool) - { + private func setIsSelectedAddButton(flag: Bool) { isSelectedAddButton = flag } - func config(tracker: Tracker) - { + func config(tracker: Tracker) { colorView.backgroundColor = tracker.color emojiLable.text = tracker.emoji addButton.backgroundColor = tracker.color nameTrackerLabel.text = tracker.name } - func getView() -> UIView - { + func getView() -> UIView { colorView } //MARK: - SetupUI - private func setupUIElement() - { + private func setupUIElement() { setupColorView() setupVerticallStack() setupHorisontalStack() setupBackgroundAddButtonView() } - private func setupVerticallStack() - { + private func setupVerticallStack() { contentView.addSubview(verticalStack) [colorView, horisontalStack].forEach { $0.translatesAutoresizingMaskIntoConstraints = false @@ -222,8 +209,7 @@ extension TrackersCollectionViewCell { ]) } - private func setupHorisontalStack() - { + private func setupHorisontalStack() { [dayCounterLable, addButton].forEach { $0.translatesAutoresizingMaskIntoConstraints = false horisontalStack.addArrangedSubview($0) @@ -244,8 +230,7 @@ extension TrackersCollectionViewCell { ]) } - private func setupBackgroundAddButtonView() - { + private func setupBackgroundAddButtonView() { addButton.addSubview(backgroundAddButtonView) NSLayoutConstraint.activate([ @@ -256,8 +241,7 @@ extension TrackersCollectionViewCell { ]) } - private func setupColorView() - { + private func setupColorView() { [emojiLable, nameTrackerLabel, isPinnedImageView].forEach { $0.translatesAutoresizingMaskIntoConstraints = false colorView.addSubview($0) diff --git a/Tracker/Views/CustomViews/HeaderReusableView.swift b/Tracker/Views/CustomViews/HeaderReusableView.swift index aff515a..c99a793 100644 --- a/Tracker/Views/CustomViews/HeaderReusableView.swift +++ b/Tracker/Views/CustomViews/HeaderReusableView.swift @@ -24,23 +24,20 @@ final class HeaderReusableView: UICollectionReusableView { return label }() - override init(frame: CGRect) - { + override init(frame: CGRect) { super.init(frame: frame) backgroundColor = colors.viewBackground setupLable() } - required init?(coder: NSCoder) - { + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } //MARK: - SetupUI extension HeaderReusableView { - private func setupLable() - { + private func setupLable() { addSubview(label) label.translatesAutoresizingMaskIntoConstraints = false @@ -52,8 +49,7 @@ extension HeaderReusableView { ]) } - func setTextLable(text: String) - { + func setTextLable(text: String) { label.text = text } } diff --git a/Tracker/Views/CustomViews/StatisticView.swift b/Tracker/Views/CustomViews/StatisticView.swift index 2cb8dc8..bdfb9b2 100644 --- a/Tracker/Views/CustomViews/StatisticView.swift +++ b/Tracker/Views/CustomViews/StatisticView.swift @@ -31,23 +31,19 @@ final class StatisticsView: UIView { return secondaryTextLable }() - override init(frame: CGRect) - - { + override init(frame: CGRect) { super.init(frame: frame) setupView() backgroundColor = colors.viewBackground } - required init?(coder: NSCoder) - { + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } extension StatisticsView { - private func setupView() - { + private func setupView() { [lableHeader, secondaryTextLable].forEach { $0.backgroundColor = .clear $0.translatesAutoresizingMaskIntoConstraints = false @@ -68,13 +64,11 @@ extension StatisticsView { ]) } - func setCount(count: Int) - { + func setCount(count: Int) { lableHeader.text = String(count) } - func setSecondaryTextLable(text: String) - { + func setSecondaryTextLable(text: String) { secondaryTextLable.text = text } } diff --git a/Tracker/Views/CustomViews/TrackerCollectionView.swift b/Tracker/Views/CustomViews/TrackerCollectionView.swift index 90ea716..3e8eedd 100644 --- a/Tracker/Views/CustomViews/TrackerCollectionView.swift +++ b/Tracker/Views/CustomViews/TrackerCollectionView.swift @@ -13,14 +13,12 @@ final class TrackerCollectionView: UICollectionView { init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout, - params: GeometricParams) - { + params: GeometricParams) { self.params = params super.init(frame: frame, collectionViewLayout: layout) } - required init?(coder: NSCoder) - { + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } diff --git a/Tracker/Views/TableViewCell/CreateTrackerTableViewCell.swift b/Tracker/Views/TableViewCell/CreateTrackerTableViewCell.swift index d9caee4..150658a 100644 --- a/Tracker/Views/TableViewCell/CreateTrackerTableViewCell.swift +++ b/Tracker/Views/TableViewCell/CreateTrackerTableViewCell.swift @@ -49,17 +49,14 @@ class CreateTrackerTableViewCell: UITableViewCell { return clickImage }() - override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) - - { + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) selectionStyle = .none backgroundColor = colors.viewBackground setupUIElement() } - required init?(coder: NSCoder) - { + required init?(coder: NSCoder) { super.init(coder: coder) assertionFailure("init(coder:) has not been implemented") } @@ -68,14 +65,12 @@ class CreateTrackerTableViewCell: UITableViewCell { extension CreateTrackerTableViewCell { //MARK: - SetupUI - private func setupUIElement() - { + private func setupUIElement() { setupSelf() setupSteckView() } - private func setupSelf() - { + private func setupSelf() { backgroundColor = .backgroundNight layer.masksToBounds = true clickImage.translatesAutoresizingMaskIntoConstraints = false @@ -91,8 +86,7 @@ extension CreateTrackerTableViewCell { ]) } - private func setupSteckView() - { + private func setupSteckView() { contentView.addSubview(lableStackView) [lableView, secondaryTextLable].forEach { $0.backgroundColor = .clear @@ -108,21 +102,18 @@ extension CreateTrackerTableViewCell { } //MARK: - Configuration - func configCell(choice: ChoiceParametrs) - { + func configCell(choice: ChoiceParametrs) { lableView.text = choice.name } - func configSecondaryLableShedule(secondaryText: String) - { + func configSecondaryLableShedule(secondaryText: String) { if !secondaryText.isEmpty { secondaryTextLable.text = secondaryText secondaryTextLable.isHidden = false } } - func setupCornerRadius(cornerRadius: CGFloat, maskedCorners: CACornerMask) - { + func setupCornerRadius(cornerRadius: CGFloat, maskedCorners: CACornerMask) { layer.cornerRadius = cornerRadius layer.maskedCorners = maskedCorners } diff --git a/Tracker/Views/TableViewCell/CustomTableViewCell.swift b/Tracker/Views/TableViewCell/CustomTableViewCell.swift index 30af489..0058ba1 100644 --- a/Tracker/Views/TableViewCell/CustomTableViewCell.swift +++ b/Tracker/Views/TableViewCell/CustomTableViewCell.swift @@ -34,8 +34,7 @@ final class CustomTableViewCell: UITableViewCell { return selectedImage }() - override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) - { + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) selectionStyle = .none layer.masksToBounds = true @@ -44,13 +43,11 @@ final class CustomTableViewCell: UITableViewCell { setupSelectedImage() } - required init?(coder: NSCoder) - { + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - override func prepareForReuse() - { + override func prepareForReuse() { super.prepareForReuse() layer.cornerRadius = 0 layer.maskedCorners = [.layerMinXMinYCorner, @@ -62,27 +59,23 @@ final class CustomTableViewCell: UITableViewCell { extension CustomTableViewCell { //MARK: - Config - func config(model: CustomCellModel) - { + func config(model: CustomCellModel) { nameCategoriLableView.text = model.text backgroundColor = model.color } - func showSelectedImage(flag: Bool) - { + func showSelectedImage(flag: Bool) { selectedImage.isHidden = flag } - func setupCornerRadius(cornerRadius: CGFloat, maskedCorners: CACornerMask?) - { + func setupCornerRadius(cornerRadius: CGFloat, maskedCorners: CACornerMask?) { layer.cornerRadius = cornerRadius guard let maskedCorners else { return } layer.maskedCorners = maskedCorners } //MARK: - SetupUI - private func setupLable() - { + private func setupLable() { nameCategoriLableView.translatesAutoresizingMaskIntoConstraints = false nameCategoriLableView.backgroundColor = .clear contentView.addSubview(nameCategoriLableView) @@ -94,8 +87,7 @@ extension CustomTableViewCell { ]) } - private func setupSelectedImage() - { + private func setupSelectedImage() { contentView.addSubview(selectedImage) selectedImage.translatesAutoresizingMaskIntoConstraints = false diff --git a/Tracker/Views/TableViewCell/WeekDayTableViewCell.swift b/Tracker/Views/TableViewCell/WeekDayTableViewCell.swift index 952befd..b3ab8ac 100644 --- a/Tracker/Views/TableViewCell/WeekDayTableViewCell.swift +++ b/Tracker/Views/TableViewCell/WeekDayTableViewCell.swift @@ -36,8 +36,7 @@ final class WeekDayTableViewCell: UITableViewCell { return choiceDaySwitch }() - override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) - { + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) backgroundColor = .backgroundDay layer.masksToBounds = true @@ -45,8 +44,7 @@ final class WeekDayTableViewCell: UITableViewCell { setupElement() } - required init?(coder: NSCoder) - { + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } @@ -54,8 +52,7 @@ final class WeekDayTableViewCell: UITableViewCell { extension WeekDayTableViewCell { //MARK: - Обработка событий @objc - private func switching(_ sender: UISwitch) - { + private func switching(_ sender: UISwitch) { guard let delegate else { return } delegate.addDayInListkDay(cell: self, flag: choiceDaySwitch.isOn) @@ -63,8 +60,7 @@ extension WeekDayTableViewCell { //MARK: - Configuration func setupCornerRadius(cornerRadius: CGFloat, - maskedCorners: CACornerMask) - { + maskedCorners: CACornerMask) { layer.cornerRadius = cornerRadius layer.maskedCorners = maskedCorners } diff --git a/TrackerTests/TrackerTests.swift b/TrackerTests/TrackerTests.swift index a7e896d..6159f31 100644 --- a/TrackerTests/TrackerTests.swift +++ b/TrackerTests/TrackerTests.swift @@ -13,11 +13,13 @@ final class TrackerTests: XCTestCase { let vc = TrackersViewController(viewModel: TrackerViewModel()) func testTrackersViewControllerDark() { + sleep(1) assertSnapshot(matching: vc, as: .image(traits: .init(userInterfaceStyle: .dark)), record: false) } func testTrackersViewControllerLight() { + sleep(1) assertSnapshot(matching: vc, as: .image(traits: .init(userInterfaceStyle: .light)), record: false) } diff --git a/TrackerTests/__Snapshots__/TrackerTests/testTrackersViewControllerDark.1.png b/TrackerTests/__Snapshots__/TrackerTests/testTrackersViewControllerDark.1.png index 32d55d5cd056ef80bf61822b2d5c2640ffde17bd..469011c4350b7eb9d0909a37bd63687f58157bc3 100644 GIT binary patch literal 183031 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

F<3Qymgl|<7Q{5BG>vA#- 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 literal 97098 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 diff --git a/TrackerTests/__Snapshots__/TrackerTests/testTrackersViewControllerLight.1.png b/TrackerTests/__Snapshots__/TrackerTests/testTrackersViewControllerLight.1.png index 09e1a35f2f24122b36cc2500b0443eca10baf191..e8c79efa3d8bb84848a1d044c2028df0378528d6 100644 GIT binary patch literal 176485 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

5{A`!#5+Wx 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 diff --git a/TrackerTests/__Snapshots__/TrackerTests/testViewController.1.png b/TrackerTests/__Snapshots__/TrackerTests/testViewController.1.png deleted file mode 100644 index 32d55d5cd056ef80bf61822b2d5c2640ffde17bd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 97098 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 diff --git a/TrackerTests/__Snapshots__/TrackerTests/testViewController.2.png b/TrackerTests/__Snapshots__/TrackerTests/testViewController.2.png deleted file mode 100644 index 09e1a35f2f24122b36cc2500b0443eca10baf191..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 85463 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 From 2422ca39441fd0bfd736e50032485036ac6d3468 Mon Sep 17 00:00:00 2001 From: Grifon Date: Mon, 29 Jan 2024 20:47:53 +0300 Subject: [PATCH 20/24] separator setting tableView CreateTrackerViewController --- .../CreateTrackerViewController.swift | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/Tracker/ViewControllers/ViewControllers/CreateTrackerViewController.swift b/Tracker/ViewControllers/ViewControllers/CreateTrackerViewController.swift index faff781..b9be7e4 100644 --- a/Tracker/ViewControllers/ViewControllers/CreateTrackerViewController.swift +++ b/Tracker/ViewControllers/ViewControllers/CreateTrackerViewController.swift @@ -111,6 +111,7 @@ final class CreateTrackerViewController: UIViewController { selectionTableView.delegate = self selectionTableView.backgroundColor = .clear selectionTableView.isScrollEnabled = false + selectionTableView.separatorColor = .grayDay selectionTableView.register(CreateTrackerTableViewCell.self, forCellReuseIdentifier: "\(CreateTrackerTableViewCell.self)") @@ -458,7 +459,8 @@ extension CreateTrackerViewController: UITableViewDataSource { } func tableView(_ tableView: UITableView, - cellForRowAt indexPath: IndexPath) -> UITableViewCell { + cellForRowAt indexPath: IndexPath) -> UITableViewCell + { guard let cell = tableView.dequeueReusableCell(withIdentifier: "\(CreateTrackerTableViewCell.self)") as? CreateTrackerTableViewCell, let viewModel = viewModel as? EditTrackerViewModel @@ -473,31 +475,35 @@ extension CreateTrackerViewController: UITableViewDataSource { case .schedule: cell.configCell(choice: listSettings) cell.configSecondaryLableShedule(secondaryText: secondarySchedulText) - tableView.separatorInset = UIEdgeInsets(top: 0, - left: view.bounds.width, - bottom: 0, - right: 0) } if viewModel.isSchedul { cell.layer.cornerRadius = ConstantsCreateVc.cornerRadius } - + + if !viewModel.isSchedul && indexPath.row == 0 { + cell.setupCornerRadius(cornerRadius: ConstantsCreateVc.cornerRadius, + maskedCorners: [.layerMinXMinYCorner, + .layerMaxXMinYCorner]) + } + if !viewModel.isSchedul && indexPath.row == 1 { cell.setupCornerRadius(cornerRadius: ConstantsCreateVc.cornerRadius, maskedCorners: [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]) } - if !viewModel.isSchedul && indexPath.row == 0 { - cell.setupCornerRadius(cornerRadius: ConstantsCreateVc.cornerRadius, - maskedCorners: [.layerMinXMinYCorner, - .layerMaxXMinYCorner]) + if indexPath.row == 0 { + tableView.separatorInset = ConstantsCreateVc.insertSeparatorTableView + tableView.separatorStyle = viewModel.isSchedul ? .none : .singleLine } - tableView.separatorStyle = viewModel.isSchedul ? .none : .singleLine - tableView.separatorColor = .grayDay - tableView.separatorInset = ConstantsCreateVc.insertSeparatorTableView + if indexPath.row == 1 { + tableView.separatorInset = UIEdgeInsets(top: 0, + left: view.bounds.width, + bottom: 0, + right: 0) + } return cell } From c46fe97a29777a10224f5737ca9c6163cba4c540 Mon Sep 17 00:00:00 2001 From: Grifon Date: Thu, 1 Feb 2024 12:44:39 +0300 Subject: [PATCH 21/24] correction of comments --- .../xcdebugger/Breakpoints_v2.xcbkptlist | 20 ++--- .../AnalyticsService/AnalyticsService.swift | 3 +- Tracker/Application/AppDelegate.swift | 22 ------ Tracker/Application/SceneDelegate.swift | 28 ------- .../Contents.json | 12 +-- .../grayOpacity30.colorset/Contents.json | 4 +- .../seachBarColor.colorset/Contents.json | 56 ++++++++++++++ .../switchBgColor.colorset/Contents.json | 38 +++++++++ Tracker/Domain/Store/TrackerStore.swift | 4 - Tracker/Extension/UIColor.swift | 6 +- Tracker/Helpers/Colors.swift | 8 ++ Tracker/Models/Enum/Screen.swift | 2 +- Tracker/Models/Translate.swift | 1 + Tracker/Models/UpdateTracker.swift | 3 + .../Resources/en.lproj/Localizable.strings | 1 + .../Resources/ru.lproj/Localizable.strings | 2 +- .../OnboardingPageViewController.swift | 1 - .../OnboardingViewController.swift | 1 - .../ViewControllers/TabBarController.swift | 8 +- .../CreateTrackerViewController.swift | 32 +++----- .../EventSelectionViewController.swift | 2 - .../FiltersViewController.swift | 2 - .../NewCategoriViewController.swift | 6 +- .../ScheduleViewController.swift | 3 +- .../SelectCategoriesViewController.swift | 12 +-- .../StatisticsViewController.swift | 5 +- .../TrackersViewController.swift | 77 +++++++++---------- Tracker/ViewModels/EditTrackerViewModel.swift | 3 - Tracker/ViewModels/NewCategoryViewModel.swift | 1 - Tracker/ViewModels/TrackerViewModel.swift | 21 +---- .../ColorCollectionViewCell.swift | 1 - .../EmojiCollectionViewCell.swift | 1 - .../TrackersCollectionViewCell.swift | 43 ++--------- .../CustomViews/HeaderReusableView.swift | 1 - Tracker/Views/CustomViews/StatisticView.swift | 2 - .../CreateTrackerTableViewCell.swift | 4 +- .../TableViewCell/CustomTableViewCell.swift | 2 - .../TableViewCell/WeekDayTableViewCell.swift | 4 +- 38 files changed, 194 insertions(+), 248 deletions(-) rename Tracker/Assets.xcassets/Colors/{Color.colorset => Placeholder.colorset}/Contents.json (76%) create mode 100644 Tracker/Assets.xcassets/Colors/seachBarColor.colorset/Contents.json create mode 100644 Tracker/Assets.xcassets/Colors/switchBgColor.colorset/Contents.json diff --git a/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index d5b3a8f..9b51128 100644 --- a/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/Tracker.xcodeproj/xcuserdata/mariel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -4493,8 +4493,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "168" - endingLineNumber = "168" + startingLineNumber = "164" + endingLineNumber = "164" landmarkName = "addNewTracker(_:nameCategory:)" landmarkType = "7"> @@ -4853,8 +4853,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "223" - endingLineNumber = "223" + startingLineNumber = "219" + endingLineNumber = "219" landmarkName = "addPinnedCategory(_:pinnedCategory:)" landmarkType = "7"> @@ -5550,8 +5550,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "109" - endingLineNumber = "109" + startingLineNumber = "105" + endingLineNumber = "105" landmarkName = "searchTracker(id:)" landmarkType = "7"> @@ -5598,8 +5598,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "107" - endingLineNumber = "107" + startingLineNumber = "103" + endingLineNumber = "103" landmarkName = "searchTracker(id:)" landmarkType = "7"> @@ -5738,8 +5738,8 @@ filePath = "Tracker/Domain/Store/TrackerStore.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "238" - endingLineNumber = "238" + startingLineNumber = "234" + endingLineNumber = "234" landmarkName = "controllerDidChangeContent(_:)" landmarkType = "7"> diff --git a/Tracker/AnalyticsService/AnalyticsService.swift b/Tracker/AnalyticsService/AnalyticsService.swift index 0ca0037..623d0c1 100644 --- a/Tracker/AnalyticsService/AnalyticsService.swift +++ b/Tracker/AnalyticsService/AnalyticsService.swift @@ -17,7 +17,6 @@ struct AnalyticsService: AnalyticsServiceProtocol { YMMYandexMetrica.reportEvent(field.event.rawValue, parameters: field.params, onFailure: { error in - print("REPORT ERROR: %@", error.localizedDescription) - }) + print("REPORT ERROR: %@", error.localizedDescription)}) } } diff --git a/Tracker/Application/AppDelegate.swift b/Tracker/Application/AppDelegate.swift index 7193512..5db2034 100644 --- a/Tracker/Application/AppDelegate.swift +++ b/Tracker/Application/AppDelegate.swift @@ -28,39 +28,17 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { - // Called when a new scene session is being created. - // Use this method to select a configuration to create the new scene with. return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) } func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { - // Called when the user discards a scene session. - // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. - // Use this method to release any resources that were specific to the discarded scenes, as they will not return. } lazy var persistentContainer: NSPersistentContainer = { - /* - The persistent container for the application. This implementation - creates and returns a container, having loaded the store for the - application to it. This property is optional since there are legitimate - error conditions that could cause the creation of the store to fail. - */ let container = NSPersistentContainer(name: "Model") container.loadPersistentStores(completionHandler: { (storeDescription, error) in if let error = error as NSError? { - // Replace this implementation with code to handle the error appropriately. - // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. - - /* - Typical reasons for an error here include: - * The parent directory does not exist, cannot be created, or disallows writing. - * The persistent store is not accessible, due to permissions or data protection when the device is locked. - * The device is out of space. - * The store could not be migrated to the current model version. - Check the error message to determine what the actual problem was. - */ fatalError("Unresolved error \(error), \(error.userInfo)") } }) diff --git a/Tracker/Application/SceneDelegate.swift b/Tracker/Application/SceneDelegate.swift index c719088..3d0b506 100644 --- a/Tracker/Application/SceneDelegate.swift +++ b/Tracker/Application/SceneDelegate.swift @@ -33,33 +33,5 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { window?.rootViewController = rootViewController isOnboarding = true } - - func sceneDidDisconnect(_ scene: UIScene) { - // Called as the scene is being released by the system. - // This occurs shortly after the scene enters the background, or when its session is discarded. - // Release any resources associated with this scene that can be re-created the next time the scene connects. - // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). - } - - func sceneDidBecomeActive(_ scene: UIScene) { - // Called when the scene has moved from an inactive state to an active state. - // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. - } - - func sceneWillResignActive(_ scene: UIScene) { - // Called when the scene will move from an active state to an inactive state. - // This may occur due to temporary interruptions (ex. an incoming phone call). - } - - func sceneWillEnterForeground(_ scene: UIScene) { - // Called as the scene transitions from the background to the foreground. - // Use this method to undo the changes made on entering the background. - } - - func sceneDidEnterBackground(_ scene: UIScene) { - // Called as the scene transitions from the foreground to the background. - // Use this method to save data, release shared resources, and store enough scene-specific state information - // to restore the scene back to its current state. - } } diff --git a/Tracker/Assets.xcassets/Colors/Color.colorset/Contents.json b/Tracker/Assets.xcassets/Colors/Placeholder.colorset/Contents.json similarity index 76% rename from Tracker/Assets.xcassets/Colors/Color.colorset/Contents.json rename to Tracker/Assets.xcassets/Colors/Placeholder.colorset/Contents.json index 22c4bb0..b032583 100644 --- a/Tracker/Assets.xcassets/Colors/Color.colorset/Contents.json +++ b/Tracker/Assets.xcassets/Colors/Placeholder.colorset/Contents.json @@ -5,9 +5,9 @@ "color-space" : "srgb", "components" : { "alpha" : "1.000", - "blue" : "1.000", - "green" : "1.000", - "red" : "1.000" + "blue" : "0.961", + "green" : "0.922", + "red" : "0.922" } }, "idiom" : "universal" @@ -23,9 +23,9 @@ "color-space" : "srgb", "components" : { "alpha" : "1.000", - "blue" : "1.000", - "green" : "1.000", - "red" : "1.000" + "blue" : "0.961", + "green" : "0.922", + "red" : "0.922" } }, "idiom" : "universal" diff --git a/Tracker/Assets.xcassets/Colors/grayOpacity30.colorset/Contents.json b/Tracker/Assets.xcassets/Colors/grayOpacity30.colorset/Contents.json index 0e15770..8459288 100644 --- a/Tracker/Assets.xcassets/Colors/grayOpacity30.colorset/Contents.json +++ b/Tracker/Assets.xcassets/Colors/grayOpacity30.colorset/Contents.json @@ -4,7 +4,7 @@ "color" : { "color-space" : "srgb", "components" : { - "alpha" : "0.300", + "alpha" : "0.660", "blue" : "0.706", "green" : "0.686", "red" : "0.682" @@ -22,7 +22,7 @@ "color" : { "color-space" : "srgb", "components" : { - "alpha" : "0.300", + "alpha" : "0.660", "blue" : "0.706", "green" : "0.686", "red" : "0.682" diff --git a/Tracker/Assets.xcassets/Colors/seachBarColor.colorset/Contents.json b/Tracker/Assets.xcassets/Colors/seachBarColor.colorset/Contents.json new file mode 100644 index 0000000..4962382 --- /dev/null +++ b/Tracker/Assets.xcassets/Colors/seachBarColor.colorset/Contents.json @@ -0,0 +1,56 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "0.240", + "blue" : "0.502", + "green" : "0.463", + "red" : "0.463" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "light" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "0.120", + "blue" : "0.502", + "green" : "0.463", + "red" : "0.463" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "0.240", + "blue" : "0.502", + "green" : "0.463", + "red" : "0.463" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tracker/Assets.xcassets/Colors/switchBgColor.colorset/Contents.json b/Tracker/Assets.xcassets/Colors/switchBgColor.colorset/Contents.json new file mode 100644 index 0000000..8c1b830 --- /dev/null +++ b/Tracker/Assets.xcassets/Colors/switchBgColor.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.922", + "green" : "0.910", + "red" : "0.902" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.922", + "green" : "0.910", + "red" : "0.902" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tracker/Domain/Store/TrackerStore.swift b/Tracker/Domain/Store/TrackerStore.swift index efec79f..b73eb0e 100644 --- a/Tracker/Domain/Store/TrackerStore.swift +++ b/Tracker/Domain/Store/TrackerStore.swift @@ -61,19 +61,15 @@ private extension TrackerStore { assertionFailure("not found ID") throw StoreErrors.TrackrerStoreError.decodingErrorInvalidId } - guard let name = trackerCoreData.name else { throw StoreErrors.TrackrerStoreError.decodingErrorInvalidName } - guard let emoji = trackerCoreData.emoji else { throw StoreErrors.TrackrerStoreError.decodingErrorInvalidEmoji } - guard let color = trackerCoreData.colorHex else { throw StoreErrors.TrackrerStoreError.decodingErrorInvalidColor } - guard let schedul = trackerCoreData.schedule as? [WeekDay] else { throw StoreErrors.TrackrerStoreError.decodingErrorInvalidSchedul } diff --git a/Tracker/Extension/UIColor.swift b/Tracker/Extension/UIColor.swift index 2a9af9f..2b18c52 100644 --- a/Tracker/Extension/UIColor.swift +++ b/Tracker/Extension/UIColor.swift @@ -18,7 +18,6 @@ extension UIColor { static var lightGray: UIColor {UIColor(named: "lightGray") ?? .gray} static var backgroundNight: UIColor {UIColor(named: "backgroundNight") ?? .gray} static var grayOpacity30: UIColor {UIColor(named: "grayOpacity30") ?? .gray} - static var colorSelection1: UIColor {UIColor(named: "colorSelection1") ?? .red} static var colorSelection2: UIColor {UIColor(named: "colorSelection2") ?? .orange} static var colorSelection3: UIColor {UIColor(named: "colorSelection3") ?? .blue} @@ -37,11 +36,12 @@ extension UIColor { static var colorSelection16: UIColor {UIColor(named: "colorSelection16") ?? .red} static var colorSelection17: UIColor {UIColor(named: "colorSelection17") ?? .red} static var colorSelection18: UIColor {UIColor(named: "colorSelection18") ?? .green} - static var redGradient: UIColor {UIColor(named: "redGradient") ?? .red} static var greenGradient: UIColor {UIColor(named: "greenGradient") ?? .green} static var blueGradient: UIColor {UIColor(named: "blueGradient") ?? .blue} - static var textEventColor: UIColor {UIColor(named: "textEventColor") ?? .gray} static var separatorColor: UIColor {UIColor(named: "separatorColor") ?? .gray} + static var switchBgColor: UIColor {UIColor(named: "switchBgColor") ?? .gray} + static var placeholder: UIColor {UIColor(named: "placeholder") ?? .gray} + static var seachBarColor: UIColor {UIColor(named: "seachBarColor") ?? .gray} } diff --git a/Tracker/Helpers/Colors.swift b/Tracker/Helpers/Colors.swift index d8694e3..afb2bd4 100644 --- a/Tracker/Helpers/Colors.swift +++ b/Tracker/Helpers/Colors.swift @@ -33,4 +33,12 @@ final class Colors { return UIColor.white } } + + lazy var placeholder = UIColor { (traits: UITraitCollection) -> UIColor in + if traits.userInterfaceStyle == .light { + return UIColor.grayDay + } else { + return UIColor.placeholder + } + } } diff --git a/Tracker/Models/Enum/Screen.swift b/Tracker/Models/Enum/Screen.swift index 1c1534a..c4dfcbc 100644 --- a/Tracker/Models/Enum/Screen.swift +++ b/Tracker/Models/Enum/Screen.swift @@ -6,5 +6,5 @@ // enum Screen: String { - case Main + case main } diff --git a/Tracker/Models/Translate.swift b/Tracker/Models/Translate.swift index 268d1a6..86f16ae 100644 --- a/Tracker/Models/Translate.swift +++ b/Tracker/Models/Translate.swift @@ -13,6 +13,7 @@ struct Translate { static let textLableTwoVC = NSLocalizedString("textLableTwoVC", comment: "") static let textButton = NSLocalizedString("textButton", comment: "") static let headerText = NSLocalizedString("headerText", comment: "") + static let trackerLabelStabText = NSLocalizedString("trackerLabelStabText", comment: "") static let labelStabText = NSLocalizedString("labelStabText", comment: "") static let labelNothingFoundText = NSLocalizedString("labelNothingFoundText", comment: "") static let placeholderSearch = NSLocalizedString("placeholderSearch", comment: "") diff --git a/Tracker/Models/UpdateTracker.swift b/Tracker/Models/UpdateTracker.swift index 7834c82..6df003c 100644 --- a/Tracker/Models/UpdateTracker.swift +++ b/Tracker/Models/UpdateTracker.swift @@ -5,7 +5,10 @@ // Created by Григорий Машук on 28.01.24. // +import UIKit + struct UpdateTracker { let count: Int let flag: Bool + let color: UIColor } diff --git a/Tracker/Resources/en.lproj/Localizable.strings b/Tracker/Resources/en.lproj/Localizable.strings index 12eba59..6224312 100644 --- a/Tracker/Resources/en.lproj/Localizable.strings +++ b/Tracker/Resources/en.lproj/Localizable.strings @@ -117,3 +117,4 @@ "perfectDays" = "Perfect days"; "trackersCompleted" = "Trackers completed"; "averageValue" = "Average Value"; + diff --git a/Tracker/Resources/ru.lproj/Localizable.strings b/Tracker/Resources/ru.lproj/Localizable.strings index f81ca61..b2232ed 100644 --- a/Tracker/Resources/ru.lproj/Localizable.strings +++ b/Tracker/Resources/ru.lproj/Localizable.strings @@ -28,7 +28,7 @@ //MARK: - TrackersViewController "headerText" = "Трекеры"; -"labelStabText" = "Что будем отслеживать?"; +"trackerLabelStabText" = "Что будем отслеживать?"; "labelNothingFoundText" = "Ничего не найдено"; "placeholderSearch" = "Поиск"; "textCancel" = "Отменить"; diff --git a/Tracker/ViewControllers/OnboardingViewControllers/OnboardingPageViewController.swift b/Tracker/ViewControllers/OnboardingViewControllers/OnboardingPageViewController.swift index 8fe3d1c..6dd094b 100644 --- a/Tracker/ViewControllers/OnboardingViewControllers/OnboardingPageViewController.swift +++ b/Tracker/ViewControllers/OnboardingViewControllers/OnboardingPageViewController.swift @@ -41,7 +41,6 @@ private extension OnboardingPageViewController { view.addSubview(pageControl) pageControl.translatesAutoresizingMaskIntoConstraints = false pageControl.backgroundColor = .clear - NSLayoutConstraint.activate([ pageControl.centerXAnchor.constraint(equalTo: view.centerXAnchor), pageControl.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, diff --git a/Tracker/ViewControllers/OnboardingViewControllers/OnboardingViewController.swift b/Tracker/ViewControllers/OnboardingViewControllers/OnboardingViewController.swift index 19c6491..fa56e45 100644 --- a/Tracker/ViewControllers/OnboardingViewControllers/OnboardingViewController.swift +++ b/Tracker/ViewControllers/OnboardingViewControllers/OnboardingViewController.swift @@ -85,7 +85,6 @@ extension OnboardingViewController { startButton.backgroundColor = .blackDay startButton.layer.cornerRadius = ConstantsOnboardingViewController.cornerRadiusButton startButton.layer.masksToBounds = true - NSLayoutConstraint.activate([ startButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16), startButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16), diff --git a/Tracker/ViewControllers/TabBarController.swift b/Tracker/ViewControllers/TabBarController.swift index f641c23..8e08cc5 100644 --- a/Tracker/ViewControllers/TabBarController.swift +++ b/Tracker/ViewControllers/TabBarController.swift @@ -24,16 +24,13 @@ final class TabBarController: UITabBarController { tabBar.clipsToBounds = true let trackerViewController = TrackersViewController(viewModel: TrackerViewModel()) let statisticsViewController = StatisticsViewController() - let trackerNavigationController = UINavigationController(rootViewController: trackerViewController) let statisticsNavigationController = UINavigationController(rootViewController: statisticsViewController) - viewControllers = [ generateViewController(vc: trackerNavigationController, imageName: ConstantsTabBar.tabBarImageTrecker, title: Translate.tabBarTitleTrecker, insert: ConstantsTabBar.insertImage), - generateViewController(vc: statisticsNavigationController, imageName: ConstantsTabBar.tabBarImageStatistic, title: Translate.tabBarTitleStatistic, @@ -47,7 +44,7 @@ final class TabBarController: UITabBarController { // Проверяем только изменение цветовой схемы traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) { if traitCollection.userInterfaceStyle == .dark { - tabBar.layer.borderColor = UIColor.black.cgColor + tabBar.layer.borderColor = UIColor.blackDay.cgColor } else { tabBar.layer.borderColor = UIColor.grayDay.cgColor } @@ -56,7 +53,8 @@ final class TabBarController: UITabBarController { deinit { AnalyticsService.reportEvent(field: MetricEvent(event: .close, - params: [EventAnalytics.screen.rawValue: Screen.Main.rawValue])) + params: [EventAnalytics.screen.rawValue: + Screen.main.rawValue])) } } diff --git a/Tracker/ViewControllers/ViewControllers/CreateTrackerViewController.swift b/Tracker/ViewControllers/ViewControllers/CreateTrackerViewController.swift index b9be7e4..835c2e2 100644 --- a/Tracker/ViewControllers/ViewControllers/CreateTrackerViewController.swift +++ b/Tracker/ViewControllers/ViewControllers/CreateTrackerViewController.swift @@ -29,6 +29,7 @@ final class CreateTrackerViewController: UIViewController { static let restrictionsTextField = 38 static let numberOfLinesRestrictionsTextField = 1 static let heightCollectionView = CGFloat(54) + static let heightRowTableView = CGFloat(75) static let textFont = UIFont.systemFont(ofSize: 16, weight: .medium) static let countСompletedTrackersFont = UIFont.boldSystemFont(ofSize: 32) @@ -246,23 +247,19 @@ extension CreateTrackerViewController { self.chengeActivButton() selectionTableView.reloadData() } - viewModel.$color.bind { [weak self] _ in guard let self else { return } self.chengeActivButton() } - viewModel.$emoji.bind { [weak self] _ in guard let self else { return } self.chengeActivButton() } - viewModel.$nameNewCategory.bind { [weak self] _ in guard let self else { return } self.chengeActivButton() selectionTableView.reloadData() } - viewModel.$nameTracker.bind { [weak self] nameTracker in guard let self else { return } self.chengeActivButton() @@ -316,8 +313,10 @@ extension CreateTrackerViewController { createTrackerDelegate.trackerViewControllerDidCancel(self) } AnalyticsService.reportEvent(field: MetricEvent(event: .click, - params: [EventAnalytics.screen.rawValue: Screen.Main.rawValue, - EventAnalytics.item.rawValue: ItemClick.add_track.rawValue])) + params: [EventAnalytics.screen.rawValue: + Screen.main.rawValue, + EventAnalytics.item.rawValue: + ItemClick.add_track.rawValue])) } @objc @@ -374,7 +373,6 @@ extension CreateTrackerViewController { func setupCountLabel() { view.addSubview(countСompletedTrackers) countСompletedTrackers.translatesAutoresizingMaskIntoConstraints = false - NSLayoutConstraint.activate([ countСompletedTrackers.topAnchor.constraint(equalTo: newHabitLabel.bottomAnchor, constant: 38), @@ -385,7 +383,6 @@ extension CreateTrackerViewController { func setupNewHabitLabel() { view.addSubview(newHabitLabel) newHabitLabel.translatesAutoresizingMaskIntoConstraints = false - NSLayoutConstraint.activate([ newHabitLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 24), @@ -399,7 +396,6 @@ extension CreateTrackerViewController { $0.translatesAutoresizingMaskIntoConstraints = false buttonStack.addArrangedSubview($0) } - NSLayoutConstraint.activate([ buttonStack.heightAnchor.constraint(equalToConstant: 60), buttonStack.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, @@ -414,11 +410,9 @@ extension CreateTrackerViewController { func setupScrollView() { view.addSubview(scrollView) scrollView.translatesAutoresizingMaskIntoConstraints = false - let scrollTopAnchor = updateTrackerDelegate == nil ? newHabitLabel.bottomAnchor : countСompletedTrackers.bottomAnchor - NSLayoutConstraint.activate([ scrollView.topAnchor.constraint(equalTo: scrollTopAnchor, constant: 24), @@ -436,7 +430,6 @@ extension CreateTrackerViewController { contentStackView.addArrangedSubview($0) $0.translatesAutoresizingMaskIntoConstraints = false } - NSLayoutConstraint.activate([ contentStackView.topAnchor.constraint(equalTo: scrollView.topAnchor), contentStackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor), @@ -459,8 +452,7 @@ extension CreateTrackerViewController: UITableViewDataSource { } func tableView(_ tableView: UITableView, - cellForRowAt indexPath: IndexPath) -> UITableViewCell - { + cellForRowAt indexPath: IndexPath) -> UITableViewCell { guard let cell = tableView.dequeueReusableCell(withIdentifier: "\(CreateTrackerTableViewCell.self)") as? CreateTrackerTableViewCell, let viewModel = viewModel as? EditTrackerViewModel @@ -476,28 +468,23 @@ extension CreateTrackerViewController: UITableViewDataSource { cell.configCell(choice: listSettings) cell.configSecondaryLableShedule(secondaryText: secondarySchedulText) } - if viewModel.isSchedul { cell.layer.cornerRadius = ConstantsCreateVc.cornerRadius } - if !viewModel.isSchedul && indexPath.row == 0 { cell.setupCornerRadius(cornerRadius: ConstantsCreateVc.cornerRadius, maskedCorners: [.layerMinXMinYCorner, .layerMaxXMinYCorner]) } - if !viewModel.isSchedul && indexPath.row == 1 { cell.setupCornerRadius(cornerRadius: ConstantsCreateVc.cornerRadius, maskedCorners: [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]) } - if indexPath.row == 0 { tableView.separatorInset = ConstantsCreateVc.insertSeparatorTableView tableView.separatorStyle = viewModel.isSchedul ? .none : .singleLine } - if indexPath.row == 1 { tableView.separatorInset = UIEdgeInsets(top: 0, left: view.bounds.width, @@ -511,9 +498,8 @@ extension CreateTrackerViewController: UITableViewDataSource { //MARK: - UITableViewDelegate extension CreateTrackerViewController: UITableViewDelegate { - func tableView(_ tableView: UITableView, - heightForRowAt indexPath: IndexPath) -> CGFloat { - 75 + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + ConstantsCreateVc.heightRowTableView } func tableView(_ tableView: UITableView, @@ -552,6 +538,7 @@ extension CreateTrackerViewController: UICollectionViewDataSource { case .emoji: count = DataSource.emojies.count } + return count } @@ -577,6 +564,7 @@ extension CreateTrackerViewController: UICollectionViewDataSource { cell.configEmoji(emoji: emoji) returnCell = cell } + return returnCell } } diff --git a/Tracker/ViewControllers/ViewControllers/EventSelectionViewController.swift b/Tracker/ViewControllers/ViewControllers/EventSelectionViewController.swift index a9efa44..ff7a0fe 100644 --- a/Tracker/ViewControllers/ViewControllers/EventSelectionViewController.swift +++ b/Tracker/ViewControllers/ViewControllers/EventSelectionViewController.swift @@ -116,7 +116,6 @@ private extension EventSelectionViewController { func setupLableGreate() { view.addSubview(labelCreate) - NSLayoutConstraint.activate([ labelCreate.centerXAnchor.constraint(equalTo: view.centerXAnchor), labelCreate.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, @@ -130,7 +129,6 @@ private extension EventSelectionViewController { [habitButton, irregularEventButton].forEach { stackView.addArrangedSubview($0) } - NSLayoutConstraint.activate([ stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20), diff --git a/Tracker/ViewControllers/ViewControllers/FiltersViewController.swift b/Tracker/ViewControllers/ViewControllers/FiltersViewController.swift index d422fee..8e49093 100644 --- a/Tracker/ViewControllers/ViewControllers/FiltersViewController.swift +++ b/Tracker/ViewControllers/ViewControllers/FiltersViewController.swift @@ -74,7 +74,6 @@ private extension FiltersViewController { private func setupCategoryLabel() { view.addSubview(filtersLable) filtersLable.translatesAutoresizingMaskIntoConstraints = false - NSLayoutConstraint.activate([ filtersLable.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 24), @@ -85,7 +84,6 @@ private extension FiltersViewController { private func setupTableView() { view.addSubview(filterTableView) filterTableView.translatesAutoresizingMaskIntoConstraints = false - NSLayoutConstraint.activate([ filterTableView.topAnchor.constraint(equalTo: filtersLable.bottomAnchor, constant: 24), diff --git a/Tracker/ViewControllers/ViewControllers/NewCategoriViewController.swift b/Tracker/ViewControllers/ViewControllers/NewCategoriViewController.swift index 87cf584..61d2d99 100644 --- a/Tracker/ViewControllers/ViewControllers/NewCategoriViewController.swift +++ b/Tracker/ViewControllers/ViewControllers/NewCategoriViewController.swift @@ -18,7 +18,7 @@ protocol UpdateCategoriViewControllerDelegate: AnyObject { } //MARK: - NewCategoriViewController -class NewCategoriViewController: UIViewController { +final class NewCategoriViewController: UIViewController { private struct ConstantsNewCatVc { static let cornerRadius = CGFloat(16) static let leftIndentTextField = CGFloat(12) @@ -52,7 +52,7 @@ class NewCategoriViewController: UIViewController { readyButton.layer.cornerRadius = ConstantsNewCatVc.cornerRadius readyButton.layer.masksToBounds = true readyButton.translatesAutoresizingMaskIntoConstraints = false - readyButton.addTarget(self, action: #selector(didTapNewСategoriButton), for: .touchUpInside) + readyButton.addTarget(self, action: #selector(didTapNewСategoryButton), for: .touchUpInside) return readyButton }() @@ -108,7 +108,7 @@ private extension NewCategoriViewController { //MARK: - Обработка событий @objc - func didTapNewСategoriButton() { + func didTapNewСategoryButton() { guard let viewModel else { return } if let createCategorydelegate { createCategorydelegate.didNewCategoryName(self, diff --git a/Tracker/ViewControllers/ViewControllers/ScheduleViewController.swift b/Tracker/ViewControllers/ViewControllers/ScheduleViewController.swift index b7dd9da..be3dee8 100644 --- a/Tracker/ViewControllers/ViewControllers/ScheduleViewController.swift +++ b/Tracker/ViewControllers/ViewControllers/ScheduleViewController.swift @@ -43,7 +43,7 @@ final class ScheduleViewController: UIViewController { weekDayTableView.allowsSelection = false weekDayTableView.backgroundColor = .clear weekDayTableView.separatorInset = ConstantsShedulVc.separatorInsetTableView - weekDayTableView.separatorColor = .separatorColor + weekDayTableView.separatorColor = .grayDay weekDayTableView.register(WeekDayTableViewCell.self, forCellReuseIdentifier: "\(WeekDayTableViewCell.self)") @@ -127,7 +127,6 @@ private extension ScheduleViewController { contentStackView.addArrangedSubview($0) } contentStackView.setCustomSpacing(38, after: scheduleLable) - NSLayoutConstraint.activate([ contentStackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 16), diff --git a/Tracker/ViewControllers/ViewControllers/SelectCategoriesViewController.swift b/Tracker/ViewControllers/ViewControllers/SelectCategoriesViewController.swift index 2b9db07..452b3ad 100644 --- a/Tracker/ViewControllers/ViewControllers/SelectCategoriesViewController.swift +++ b/Tracker/ViewControllers/ViewControllers/SelectCategoriesViewController.swift @@ -82,7 +82,7 @@ final class SelectCategoriesViewController: UIViewController { selectionCategoryTableView.delegate = self selectionCategoryTableView.backgroundColor = .clear selectionCategoryTableView.separatorStyle = .singleLine - selectionCategoryTableView.separatorColor = .separatorColor + selectionCategoryTableView.separatorColor = .grayDay selectionCategoryTableView.register(CustomTableViewCell.self, forCellReuseIdentifier: "\(CustomTableViewCell.self)") selectionCategoryTableView.separatorInset = ConstantsCreateCatVc.insertSeparatorTableView @@ -216,7 +216,6 @@ extension SelectCategoriesViewController { private func setupCategoryButton() { view.addSubview(createCategoryButton) - NSLayoutConstraint.activate([ createCategoryButton.heightAnchor.constraint(equalToConstant: 60), createCategoryButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -10), @@ -228,7 +227,6 @@ extension SelectCategoriesViewController { private func setupCategoryLabel() { view.addSubview(categoryLabel) categoryLabel.translatesAutoresizingMaskIntoConstraints = false - NSLayoutConstraint.activate([ categoryLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 24), @@ -239,7 +237,6 @@ extension SelectCategoriesViewController { private func setupTableView() { view.addSubview(selectionCategoryTableView) selectionCategoryTableView.translatesAutoresizingMaskIntoConstraints = false - NSLayoutConstraint.activate([ selectionCategoryTableView.topAnchor.constraint(equalTo: categoryLabel.bottomAnchor, constant: 24), @@ -258,14 +255,11 @@ extension SelectCategoriesViewController { $0.translatesAutoresizingMaskIntoConstraints = false $0.backgroundColor = .clear } - NSLayoutConstraint.activate([ conteinerStabView.centerXAnchor.constraint(equalTo: view.centerXAnchor), conteinerStabView.centerYAnchor.constraint(equalTo: view.centerYAnchor), - imageViewStab.centerYAnchor.constraint(equalTo: conteinerStabView.centerYAnchor), imageViewStab.centerXAnchor.constraint(equalTo: conteinerStabView.centerXAnchor), - lableTextStab.widthAnchor.constraint(equalToConstant: 200), lableTextStab.centerXAnchor.constraint(equalTo: conteinerStabView.centerXAnchor), lableTextStab.topAnchor.constraint(equalTo: imageViewStab.bottomAnchor, @@ -294,11 +288,9 @@ extension SelectCategoriesViewController: UITableViewDataSource { cell.showSelectedImage(flag: isSelected) let model = CustomCellModel(text: text, color: .backgroundNight) cell.config(model: model) - if category.count > ConstantsCreateCatVc.one { cell.separatorInset = ConstantsCreateCatVc.insertSeparatorTableView } - if indexPath.row == .zero && category.count == ConstantsCreateCatVc.one { cell.setupCornerRadius(cornerRadius: ConstantsCreateCatVc.cornerRadius, maskedCorners: nil) @@ -307,13 +299,11 @@ extension SelectCategoriesViewController: UITableViewDataSource { bottom: .zero, right: .zero) } - if indexPath.row == .zero && category.count > ConstantsCreateCatVc.one { cell.setupCornerRadius(cornerRadius: ConstantsCreateCatVc.cornerRadius, maskedCorners: [.layerMaxXMinYCorner, .layerMinXMinYCorner]) } - if indexPath.row == category.count - ConstantsCreateCatVc.one && category.count > ConstantsCreateCatVc.one { cell.setupCornerRadius(cornerRadius: ConstantsCreateCatVc.cornerRadius, diff --git a/Tracker/ViewControllers/ViewControllers/StatisticsViewController.swift b/Tracker/ViewControllers/ViewControllers/StatisticsViewController.swift index 42fd390..d1572d9 100644 --- a/Tracker/ViewControllers/ViewControllers/StatisticsViewController.swift +++ b/Tracker/ViewControllers/ViewControllers/StatisticsViewController.swift @@ -107,13 +107,11 @@ private extension StatisticsViewController { views.forEach { $0.layer.cornerRadius = cornerRadius $0.clipsToBounds = true - let gradient = CAGradientLayer() gradient.frame = $0.bounds gradient.colors = colors gradient.startPoint = startPoint gradient.endPoint = endPoint - let shape = CAShapeLayer() shape.lineWidth = borderWidth let path = UIBezierPath(roundedRect: $0.bounds, @@ -147,7 +145,7 @@ private extension StatisticsViewController { view.addSubview(labelHeader) NSLayoutConstraint.activate([ labelHeader.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16), - labelHeader.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 44) + labelHeader.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor) ]) } @@ -161,7 +159,6 @@ private extension StatisticsViewController { NSLayoutConstraint.activate([ imageViewStab.centerYAnchor.constraint(equalTo: view.centerYAnchor), imageViewStab.centerXAnchor.constraint(equalTo: view.centerXAnchor), - lableTextStab.centerXAnchor.constraint(equalTo: view.centerXAnchor), lableTextStab.topAnchor.constraint(equalTo: imageViewStab.bottomAnchor, constant: 10) ]) diff --git a/Tracker/ViewControllers/ViewControllers/TrackersViewController.swift b/Tracker/ViewControllers/ViewControllers/TrackersViewController.swift index e15cd56..f2ce8eb 100644 --- a/Tracker/ViewControllers/ViewControllers/TrackersViewController.swift +++ b/Tracker/ViewControllers/ViewControllers/TrackersViewController.swift @@ -26,6 +26,8 @@ final class TrackersViewController: UIViewController { static let imageNothingFound = "NothingFound" static let forKeyTextCancel = "cancelButtonText" + static let locale = Locale(identifier: "ru_RU") + static let datePickerCornerRadius = CGFloat(8) static let buttonFilterCornerRadius = CGFloat(16) static let heightCollectionView = CGFloat(148) @@ -71,15 +73,16 @@ final class TrackersViewController: UIViewController { private lazy var datePicker: UIDatePicker = { let datePicker = UIDatePicker() + datePicker.widthAnchor.constraint(equalToConstant: 100).isActive = true datePicker.backgroundColor = .lightGray datePicker.datePickerMode = .date + datePicker.locale = ConstantsTrackerVc.locale datePicker.tintColor = .blueDay datePicker.preferredDatePickerStyle = .compact datePicker.calendar.firstWeekday = ConstantsTrackerVc.firstWeekday datePicker.layer.cornerRadius = ConstantsTrackerVc.datePickerCornerRadius datePicker.layer.masksToBounds = true datePicker.overrideUserInterfaceStyle = .light - datePicker.locale = .autoupdatingCurrent datePicker.addTarget(self, action: #selector(actionForTapDatePicker), for: .valueChanged) return datePicker @@ -118,7 +121,7 @@ final class TrackersViewController: UIViewController { private lazy var lableTextStab: UILabel = { let lableTextStab = UILabel() - lableTextStab.text = Translate.labelStabText + lableTextStab.text = Translate.trackerLabelStabText lableTextStab.font = ConstantsTrackerVc.fontLableTextStab return lableTextStab @@ -172,7 +175,8 @@ final class TrackersViewController: UIViewController { override func viewWillAppear(_ animated: Bool) { AnalyticsService.reportEvent(field: MetricEvent(event: .open, - params: [EventAnalytics.screen.rawValue: Screen.Main.rawValue])) + params: [EventAnalytics.screen.rawValue: + Screen.main.rawValue])) } } @@ -183,17 +187,14 @@ private extension TrackersViewController { guard let self else { return } filterState(state: viewModel.getFilterState()) } - viewModel.$indexPath.bind { [weak self] indexPath in guard let self else { return } self.trackerCollectionView.reloadItems(at: [indexPath]) } - viewModel.$isfilterListTrackersWeekDay.bind { [weak self] flag in guard let self, let flag else { return } self.buttonFilter.isHidden = !flag } - viewModel.$visibleCategory.bind { [weak self] result in guard let self, let cat = handler.resultTypeHandlerGetValue(result, @@ -202,7 +203,6 @@ private extension TrackersViewController { self.showStabView(flag: !cat.isEmpty) self.trackerCollectionView.reloadData() } - viewModel.$filterState.bind { [weak self] state in guard let self else { return } switch state { @@ -227,8 +227,10 @@ private extension TrackersViewController { @objc func actionButtonFilter() { AnalyticsService.reportEvent(field: MetricEvent(event: .click, - params: [EventAnalytics.screen.rawValue: Screen.Main.rawValue, - EventAnalytics.item.rawValue: ItemClick.filter.rawValue])) + params: [EventAnalytics.screen.rawValue: + Screen.main.rawValue, + EventAnalytics.item.rawValue: + ItemClick.filter.rawValue])) let filtersVC = FiltersViewController(viewModel: FiltersViewModel(), delegate: self) filtersVC.modalPresentationStyle = .formSheet @@ -386,7 +388,7 @@ private extension TrackersViewController { isPinned: category.isPinned) } AnalyticsService.reportEvent(field: MetricEvent(event: .click, - params: [EventAnalytics.screen.rawValue: Screen.Main.rawValue, + params: [EventAnalytics.screen.rawValue: Screen.main.rawValue, EventAnalytics.item.rawValue: ItemClick.edit.rawValue])) } } @@ -399,14 +401,14 @@ private extension TrackersViewController { preferredStyle: .actionSheet) let deleteAction = UIAlertAction(title: Translate.textDelete, style: .destructive) { _ in - self.handler.resultTypeHandler(viewModel.deleteTrackersRecord(id: tracker.id), vc: self) {} self.handler.resultTypeHandler(viewModel.deleteTracker(id: tracker.id), vc: self) {} AnalyticsService.reportEvent(field: MetricEvent(event: .click, - params: [EventAnalytics.screen.rawValue: Screen.Main.rawValue, - EventAnalytics.item.rawValue: ItemClick.edit.rawValue])) + params: [EventAnalytics.screen.rawValue: + Screen.main.rawValue, + EventAnalytics.item.rawValue: ItemClick.delete.rawValue])) } let cancelAction = UIAlertAction(title: Translate.textCancel, style: .cancel) { _ in @@ -434,31 +436,29 @@ private extension TrackersViewController { topItem.leftBarButtonItem = UIBarButtonItem(image: UIImage(named: ConstantsTrackerVc.adButtonImageName), style: .plain, target: self, action: #selector(didTapLeftNavBarItem)) - topItem.rightBarButtonItem = UIBarButtonItem(customView: datePicker) topItem.leftBarButtonItem?.imageInsets = UIEdgeInsets(top: .zero, left: -10, bottom: .zero, right: .zero) topItem.leftBarButtonItem?.tintColor = colors.whiteBlackItemColor navBar.backgroundColor = .clear - navigationItem.title = Translate.headerText navigationController?.navigationBar.prefersLargeTitles = true - - let searcheController = UISearchController(searchResultsController: nil) - searcheController.searchResultsUpdater = self - searcheController.obscuresBackgroundDuringPresentation = false - searcheController.hidesNavigationBarDuringPresentation = false - navigationItem.searchController = searcheController - searcheController.searchBar.placeholder = Translate.placeholderSearch - searcheController.searchBar.resignFirstResponder() - searcheController.searchBar.returnKeyType = .search - searcheController.searchBar.setValue(Translate.textCancel, + let searchController = UISearchController(searchResultsController: nil) + searchController.searchResultsUpdater = self + searchController.obscuresBackgroundDuringPresentation = false + searchController.hidesNavigationBarDuringPresentation = false + navigationItem.searchController = searchController + searchController.searchBar.searchTextField.backgroundColor = .seachBarColor + searchController.searchBar.resignFirstResponder() + searchController.searchBar.returnKeyType = .search + searchController.searchBar.setValue(Translate.textCancel, forKey: ConstantsTrackerVc.forKeyTextCancel) + searchController.searchBar.searchTextField.leftView?.tintColor = colors.placeholder + searchController.searchBar.searchTextField.attributedPlaceholder = NSAttributedString.init(string: Translate.placeholderSearch, attributes: [NSAttributedString.Key.foregroundColor: colors.placeholder]) } func setupContentView() { trackerCollectionView.translatesAutoresizingMaskIntoConstraints = false contentView.addSubview(trackerCollectionView) - NSLayoutConstraint.activate([ trackerCollectionView.topAnchor.constraint(equalTo: contentView.topAnchor), trackerCollectionView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), @@ -472,7 +472,6 @@ private extension TrackersViewController { contentView.translatesAutoresizingMaskIntoConstraints = false contentView.backgroundColor = .clear verticalStack.addArrangedSubview(contentView) - NSLayoutConstraint.activate([ verticalStack.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), verticalStack.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor), @@ -489,7 +488,6 @@ private extension TrackersViewController { $0.translatesAutoresizingMaskIntoConstraints = false $0.backgroundColor = .clear } - NSLayoutConstraint.activate([ imageViewStab.centerYAnchor.constraint(equalTo: view.centerYAnchor), imageViewStab.centerXAnchor.constraint(equalTo: view.centerXAnchor), @@ -502,7 +500,6 @@ private extension TrackersViewController { func setupButtonFilter() { view.addSubview(buttonFilter) - NSLayoutConstraint.activate([ buttonFilter.centerXAnchor.constraint(equalTo: view.centerXAnchor), buttonFilter.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, @@ -521,6 +518,7 @@ extension TrackersViewController: UICollectionViewDataSource { let itemsCount = handler.resultTypeHandlerGetValue(viewModel.visibleCategory, vc: self)?[section].arrayTrackers.count else { return .zero } + return itemsCount } @@ -529,6 +527,7 @@ extension TrackersViewController: UICollectionViewDataSource { let sectionCount = handler.resultTypeHandlerGetValue(viewModel.visibleCategory, vc: self)?.count else { return .zero } + return sectionCount } @@ -548,24 +547,20 @@ extension TrackersViewController: UICollectionViewDataSource { switch dateComparison { case .orderedAscending: cell.isEnableAddButton(flag: false) - cell.updateBackgraundAddButton(isHidden: false) - case .orderedSame: - cell.isEnableAddButton(flag: true) - cell.updateBackgraundAddButton(isHidden: true) - case .orderedDescending: + default: cell.isEnableAddButton(flag: true) - cell.updateBackgraundAddButton(isHidden: true) } cell.setPinned(flag: visibleCategories[indexPath.section].isPinned) handler.resultTypeHandler(viewModel.loadTrackerRecord(id: tracker.id), vc: self) { [weak self] count in guard let self, - let isComplited = self.handler.resultTypeHandlerGetValue(viewModel.getIsComplited(tracker: tracker, - date: self.datePicker.date), + let isComplited = self.handler.resultTypeHandlerGetValue( + viewModel.getIsComplited(tracker: tracker, date: self.datePicker.date), vc: self) else { return } let updateModel = UpdateTracker(count: count, - flag: isComplited) + flag: isComplited, + color: tracker.color) cell.config(tracker: tracker) cell.updateLableCountAndImageAddButton(updateModel) } @@ -703,16 +698,14 @@ extension TrackersViewController: UISearchResultsUpdating { viewModel.getShowListTrackerSearchForName(seachCategory) return } - if word.isEmpty { let _ = viewModel.getShowListTrackersForDay(date: datePicker.date) chengeStab(text: Translate.labelNothingFoundText, nameImage: ConstantsTrackerVc.imageNothingFound) } - if !searchController.isActive { let _ = viewModel.getShowListTrackersForDay(date: datePicker.date) - chengeStab(text: Translate.labelStabText, + chengeStab(text: Translate.trackerLabelStabText, nameImage: ConstantsTrackerVc.imageStar) } } @@ -733,7 +726,7 @@ extension TrackersViewController: TrackersCollectionViewCellDelegate { date: datePicker.date), vc: self) {} AnalyticsService.reportEvent(field: MetricEvent(event: .click, - params: [EventAnalytics.screen.rawValue: Screen.Main.rawValue, + params: [EventAnalytics.screen.rawValue: Screen.main.rawValue, EventAnalytics.item.rawValue: ItemClick.track.rawValue])) } } diff --git a/Tracker/ViewModels/EditTrackerViewModel.swift b/Tracker/ViewModels/EditTrackerViewModel.swift index c856fbe..fb80000 100644 --- a/Tracker/ViewModels/EditTrackerViewModel.swift +++ b/Tracker/ViewModels/EditTrackerViewModel.swift @@ -17,7 +17,6 @@ protocol EditTrackerModelProtocol { func setListWeekDay(listWeekDay: [WeekDay]) func getColorRow(color: UIColor) -> Int func getEmojiRow(emoji: String) -> Int - func setSchedule(_ vc: CreateTrackerViewController, schedule: [WeekDay]) func setColor(_ vc: CreateTrackerViewController, color: UIColor) func setNameNewCategory(_ vc: CreateTrackerViewController, nameCategory: String) @@ -66,7 +65,6 @@ extension EditTrackerViewModel: EditTrackerModelProtocol { color != nil && !emoji.isEmpty && !nameNewCategory.isEmpty ? true : false - return flag } flag = !schedule.isEmpty && @@ -90,7 +88,6 @@ extension EditTrackerViewModel: EditTrackerModelProtocol { } let listDay = schedule.map { $0.briefWordDay } stringListDay = listDay.joined(separator: ",") - return stringListDay } diff --git a/Tracker/ViewModels/NewCategoryViewModel.swift b/Tracker/ViewModels/NewCategoryViewModel.swift index 165d2ab..d70907f 100644 --- a/Tracker/ViewModels/NewCategoryViewModel.swift +++ b/Tracker/ViewModels/NewCategoryViewModel.swift @@ -10,7 +10,6 @@ import Foundation protocol NewCategoryViewModelProtocol { func setNewNameCategory(text: String) func getNewNameCategory() -> String - func setOldNameCategory(text: String) func getOldNameCategory() -> String } diff --git a/Tracker/ViewModels/TrackerViewModel.swift b/Tracker/ViewModels/TrackerViewModel.swift index 807f331..3be2312 100644 --- a/Tracker/ViewModels/TrackerViewModel.swift +++ b/Tracker/ViewModels/TrackerViewModel.swift @@ -13,30 +13,23 @@ protocol TrackerViewModelProtocol { var visibleCategory: Result<[TrackerCategory], Error> { get } func getCategory() -> Result<[TrackerCategory], Error> func addCategory(nameCategory: String) -> Result - func getCountTrackerCompleted(id: UUID) -> Result func deleteTrackersRecord(id: UUID) -> Result - func addNewTracker(_ tracker: Tracker, nameCategory: String) -> Result func updateTracker(tracker: Tracker, nameCategory: String?) -> Result func deleteTracker(id: UUID) -> Result - func getShowListTrackersForDay(date: Date) func getShowListTrackerSearchForName(_ searchCategory: [TrackerCategory]) func filterListTrackersName(word: String) -> Result<[TrackerCategory], Error> - func addPinnedCategory(id: UUID, nameCategory: String) -> Result func editPinnedCategory(_ id: UUID, newPinnedCat: String) -> Result func getPinnedCategory(_ id: UUID) -> Result func deleteAndGetPinnedCategory(id: UUID) -> Result - func allTrackersByDate(date: Date) func getNotCompleted(date: Date, flag: Bool) func getCompleted(date: Date, flag: Bool) - func setFilterState(state: FiltersState) func getFilterState() -> FiltersState - func getIsCategoryForDay() -> Bool? } @@ -44,25 +37,18 @@ protocol TrackerViewModelProtocol { final class TrackerViewModel { @Observable> private(set) var category: Result<[TrackerCategory], Error> - @Observable> private(set) var visibleCategory: Result<[TrackerCategory], Error> - @Observable private(set) var isfilterListTrackersWeekDay: Bool? @Observable private(set) var indexPath: IndexPath @Observableprivate(set) var filterState: FiltersState - @UserDefaultsBacked(key: UserDefaultKeys.selectNameCategory.rawValue) private var selectNameCategory: String? - @UserDefaultsBacked(key: UserDefaultKeys.selectFilter.rawValue) private var selectFilter: String? - @UserDefaultsBacked(key: UserDefaultKeys.isTracker.rawValue) private(set) var isTracker: Bool? - private(set) var completedTrackers: Result, Error> - private let trackerCategoryStore: TrackerCategoryStoreProtocol private let trackerRecordStore: TrackerRecordStoreProtocol private let trackerStore: TrackerStoreProtocol @@ -162,24 +148,21 @@ private extension TrackerViewModel { date: Date) -> [TrackerCategory] { let calendar = Calendar.current let filter = calendar.component(.weekday, from: date) - let listCategories: [TrackerCategory] = trackerCategory.compactMap { cat in let trackers = cat.arrayTrackers.filter { tracker in tracker.schedule.contains { weekDay in weekDay.rawValue + 1 == filter } } - if trackers.isEmpty { return nil } - return TrackerCategory(nameCategory: cat.nameCategory, arrayTrackers: trackers, isPinned: cat.isPinned) } - isfilterListTrackersWeekDay = !listCategories.isEmpty + return listCategories } @@ -288,7 +271,7 @@ extension TrackerViewModel: TrackerViewModelProtocol } func updateCompletedTrackers(tracker: Tracker, date: Date) -> Result { - return deleteOrCreateTrackerRecord(id: tracker.id, date: date) + deleteOrCreateTrackerRecord(id: tracker.id, date: date) } func getShowListTrackerSearchForName(_ searchCategory: [TrackerCategory]) { diff --git a/Tracker/Views/CollectionViewCell/ColorCollectionViewCell.swift b/Tracker/Views/CollectionViewCell/ColorCollectionViewCell.swift index 6aa8bf5..6695214 100644 --- a/Tracker/Views/CollectionViewCell/ColorCollectionViewCell.swift +++ b/Tracker/Views/CollectionViewCell/ColorCollectionViewCell.swift @@ -60,7 +60,6 @@ extension ColorCollectionViewCell { private func setupColorView() { addSubview(contentColorView) contentColorView.addSubview(colorView) - NSLayoutConstraint.activate([ contentColorView.centerXAnchor.constraint(equalTo: centerXAnchor), contentColorView.centerYAnchor.constraint(equalTo: centerYAnchor), diff --git a/Tracker/Views/CollectionViewCell/EmojiCollectionViewCell.swift b/Tracker/Views/CollectionViewCell/EmojiCollectionViewCell.swift index 930454b..b122391 100644 --- a/Tracker/Views/CollectionViewCell/EmojiCollectionViewCell.swift +++ b/Tracker/Views/CollectionViewCell/EmojiCollectionViewCell.swift @@ -51,7 +51,6 @@ extension EmojiCollectionViewCell { //MARK: - SetupUI private func setupColorView() { addSubview(emojiView) - NSLayoutConstraint.activate([ emojiView.centerXAnchor.constraint(equalTo: centerXAnchor), emojiView.centerYAnchor.constraint(equalTo: centerYAnchor), diff --git a/Tracker/Views/CollectionViewCell/TrackersCollectionViewCell.swift b/Tracker/Views/CollectionViewCell/TrackersCollectionViewCell.swift index 0fa5137..4d79b61 100644 --- a/Tracker/Views/CollectionViewCell/TrackersCollectionViewCell.swift +++ b/Tracker/Views/CollectionViewCell/TrackersCollectionViewCell.swift @@ -23,6 +23,7 @@ final class TrackersCollectionViewCell: UICollectionViewCell { static let cornerRadiusColorView = CGFloat(16) static let borderWidthColorView = CGFloat(1) + static let alphaComponentDoneButton = CGFloat(0.3) static let fontLabelEmoji = UIFont.systemFont(ofSize: 16, weight: .medium) static let fontTextLable = UIFont.systemFont(ofSize: 12, weight: .medium) @@ -34,7 +35,6 @@ final class TrackersCollectionViewCell: UICollectionViewCell { } weak var delegate: TrackersCollectionViewCellDelegate? - private var isSelectedAddButton: Bool = false private let colors = Colors() private lazy var colorView: UIView = { @@ -95,17 +95,6 @@ final class TrackersCollectionViewCell: UICollectionViewCell { return addButton }() - private lazy var backgroundAddButtonView: UIView = { - let backgroundAddButtonView = UIView() - backgroundAddButtonView.isHidden = true - backgroundAddButtonView.translatesAutoresizingMaskIntoConstraints = false - backgroundAddButtonView.layer.cornerRadius = ConstantsTrackerCell.sizeAddButton.width / 2 - backgroundAddButtonView.layer.masksToBounds = true - backgroundAddButtonView.backgroundColor = .grayOpacity30 - - return backgroundAddButtonView - }() - private lazy var horisontalStack: UIStackView = { let horisontalStack = UIStackView() horisontalStack.axis = .horizontal @@ -153,16 +142,16 @@ extension TrackersCollectionViewCell { case true: let image = UIImage(named: ConstantsTrackerCell.adButttonImageDone)?.withRenderingMode(.alwaysTemplate) addButton.setImage(image, for: .normal) + addButton.backgroundColor = updateModel.color.withAlphaComponent( + ConstantsTrackerCell.alphaComponentDoneButton) + addButton.tintColor = .textEventColor case false: let image = UIImage(named: ConstantsTrackerCell.addButtonImageAdd)?.withRenderingMode(.alwaysTemplate) addButton.setImage(image, for: .normal) + addButton.tintColor = .textEventColor } } - func updateBackgraundAddButton(isHidden: Bool) { - backgroundAddButtonView.isHidden = isHidden - } - func setPinned(flag: Bool) { isPinnedImageView.isHidden = flag } @@ -171,10 +160,6 @@ extension TrackersCollectionViewCell { addButton.isEnabled = flag } - private func setIsSelectedAddButton(flag: Bool) { - isSelectedAddButton = flag - } - func config(tracker: Tracker) { colorView.backgroundColor = tracker.color emojiLable.text = tracker.emoji @@ -191,7 +176,6 @@ extension TrackersCollectionViewCell { setupColorView() setupVerticallStack() setupHorisontalStack() - setupBackgroundAddButtonView() } private func setupVerticallStack() { @@ -200,7 +184,6 @@ extension TrackersCollectionViewCell { $0.translatesAutoresizingMaskIntoConstraints = false verticalStack.addArrangedSubview($0) } - NSLayoutConstraint.activate([ verticalStack.topAnchor.constraint(equalTo: contentView.topAnchor), verticalStack.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), @@ -214,7 +197,6 @@ extension TrackersCollectionViewCell { $0.translatesAutoresizingMaskIntoConstraints = false horisontalStack.addArrangedSubview($0) } - NSLayoutConstraint.activate([ dayCounterLable.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 12), @@ -230,25 +212,12 @@ extension TrackersCollectionViewCell { ]) } - private func setupBackgroundAddButtonView() { - addButton.addSubview(backgroundAddButtonView) - - NSLayoutConstraint.activate([ - backgroundAddButtonView.centerXAnchor.constraint(equalTo: addButton.centerXAnchor), - backgroundAddButtonView.centerYAnchor.constraint(equalTo: addButton.centerYAnchor), - backgroundAddButtonView.heightAnchor.constraint(equalToConstant: ConstantsTrackerCell.sizeAddButton.height), - backgroundAddButtonView.widthAnchor.constraint(equalToConstant: ConstantsTrackerCell.sizeAddButton.width) - ]) - } - private func setupColorView() { [emojiLable, nameTrackerLabel, isPinnedImageView].forEach { $0.translatesAutoresizingMaskIntoConstraints = false colorView.addSubview($0) } - let indentation = CGFloat(12) - NSLayoutConstraint.activate([ emojiLable.topAnchor.constraint(equalTo: colorView.topAnchor, constant: indentation), @@ -256,14 +225,12 @@ extension TrackersCollectionViewCell { constant: indentation), emojiLable.heightAnchor.constraint(equalToConstant: ConstantsTrackerCell.sizeLableView.height), emojiLable.widthAnchor.constraint(equalToConstant: ConstantsTrackerCell.sizeLableView.width), - nameTrackerLabel.leadingAnchor.constraint(equalTo: colorView.leadingAnchor, constant: indentation), nameTrackerLabel.trailingAnchor.constraint(equalTo: colorView.trailingAnchor, constant: -indentation), nameTrackerLabel.bottomAnchor.constraint(equalTo: colorView.bottomAnchor, constant: -indentation), - isPinnedImageView.topAnchor.constraint(equalTo: colorView.topAnchor, constant: indentation), isPinnedImageView.trailingAnchor.constraint(equalTo: colorView.trailingAnchor, diff --git a/Tracker/Views/CustomViews/HeaderReusableView.swift b/Tracker/Views/CustomViews/HeaderReusableView.swift index c99a793..f3c5ce1 100644 --- a/Tracker/Views/CustomViews/HeaderReusableView.swift +++ b/Tracker/Views/CustomViews/HeaderReusableView.swift @@ -40,7 +40,6 @@ extension HeaderReusableView { private func setupLable() { addSubview(label) label.translatesAutoresizingMaskIntoConstraints = false - NSLayoutConstraint.activate([ label.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 12), diff --git a/Tracker/Views/CustomViews/StatisticView.swift b/Tracker/Views/CustomViews/StatisticView.swift index bdfb9b2..9070e2c 100644 --- a/Tracker/Views/CustomViews/StatisticView.swift +++ b/Tracker/Views/CustomViews/StatisticView.swift @@ -49,9 +49,7 @@ extension StatisticsView { $0.translatesAutoresizingMaskIntoConstraints = false addSubview($0) } - let indentation = CGFloat(12) - NSLayoutConstraint.activate([ lableHeader.topAnchor.constraint(equalTo: topAnchor, constant: indentation), diff --git a/Tracker/Views/TableViewCell/CreateTrackerTableViewCell.swift b/Tracker/Views/TableViewCell/CreateTrackerTableViewCell.swift index 150658a..4a84d70 100644 --- a/Tracker/Views/TableViewCell/CreateTrackerTableViewCell.swift +++ b/Tracker/Views/TableViewCell/CreateTrackerTableViewCell.swift @@ -8,7 +8,7 @@ import UIKit //MARK: - CreateTrackerTableViewCell -class CreateTrackerTableViewCell: UITableViewCell { +final class CreateTrackerTableViewCell: UITableViewCell { private struct ConstantsCreateCell { static let iconButton = "IconButtonCell" static let lableFont = UIFont.systemFont(ofSize: 17, weight: .regular) @@ -76,7 +76,6 @@ extension CreateTrackerTableViewCell { clickImage.translatesAutoresizingMaskIntoConstraints = false contentView.addSubview(clickImage) clickImage.backgroundColor = .clear - NSLayoutConstraint.activate([ clickImage.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -12), @@ -93,7 +92,6 @@ extension CreateTrackerTableViewCell { $0.translatesAutoresizingMaskIntoConstraints = false lableStackView.addArrangedSubview($0) } - NSLayoutConstraint.activate([ lableStackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 12), diff --git a/Tracker/Views/TableViewCell/CustomTableViewCell.swift b/Tracker/Views/TableViewCell/CustomTableViewCell.swift index 0058ba1..e5cb9bb 100644 --- a/Tracker/Views/TableViewCell/CustomTableViewCell.swift +++ b/Tracker/Views/TableViewCell/CustomTableViewCell.swift @@ -79,7 +79,6 @@ extension CustomTableViewCell { nameCategoriLableView.translatesAutoresizingMaskIntoConstraints = false nameCategoriLableView.backgroundColor = .clear contentView.addSubview(nameCategoriLableView) - NSLayoutConstraint.activate([ nameCategoriLableView.centerYAnchor.constraint(equalTo: centerYAnchor), nameCategoriLableView.leadingAnchor.constraint(equalTo: leadingAnchor, @@ -90,7 +89,6 @@ extension CustomTableViewCell { private func setupSelectedImage() { contentView.addSubview(selectedImage) selectedImage.translatesAutoresizingMaskIntoConstraints = false - NSLayoutConstraint.activate([ selectedImage.centerYAnchor.constraint(equalTo: centerYAnchor), selectedImage.trailingAnchor.constraint(equalTo: trailingAnchor, diff --git a/Tracker/Views/TableViewCell/WeekDayTableViewCell.swift b/Tracker/Views/TableViewCell/WeekDayTableViewCell.swift index b3ab8ac..c3cd479 100644 --- a/Tracker/Views/TableViewCell/WeekDayTableViewCell.swift +++ b/Tracker/Views/TableViewCell/WeekDayTableViewCell.swift @@ -32,6 +32,7 @@ final class WeekDayTableViewCell: UITableViewCell { choiceDaySwitch.addTarget(self, action: #selector(switching), for: .touchUpInside) + choiceDaySwitch.subviews[0].subviews[0].backgroundColor = .switchBgColor return choiceDaySwitch }() @@ -76,13 +77,10 @@ extension WeekDayTableViewCell { contentView.addSubview($0) $0.backgroundColor = .clear } - NSLayoutConstraint.activate([ dayLable.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 12), dayLable.centerYAnchor.constraint(equalTo: contentView.centerYAnchor), - - choiceDaySwitch.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -12), choiceDaySwitch.centerYAnchor.constraint(equalTo: contentView.centerYAnchor) From a6d1907639b1cd8b7578f05620ed0854fc1339b8 Mon Sep 17 00:00:00 2001 From: Grifon Date: Thu, 1 Feb 2024 15:38:54 +0300 Subject: [PATCH 22/24] refactoring color dark theme --- .../Contents.json | 0 Tracker/Extension/UIColor.swift | 2 +- Tracker/Helpers/Colors.swift | 1 - Tracker/Resources/en.lproj/Localizable.strings | 2 +- Tracker/ViewControllers/TabBarController.swift | 1 + .../ViewControllers/CreateTrackerViewController.swift | 4 ++-- .../ViewControllers/EventSelectionViewController.swift | 4 ++-- .../ViewControllers/FiltersViewController.swift | 2 +- .../ViewControllers/NewCategoriViewController.swift | 8 ++++---- .../ViewControllers/ScheduleViewController.swift | 4 ++-- .../ViewControllers/SelectCategoriesViewController.swift | 4 ++-- .../ViewControllers/StatisticsViewController.swift | 2 +- .../ViewControllers/TrackersViewController.swift | 6 +++++- Tracker/ViewModels/TrackerViewModel.swift | 5 +++++ .../CollectionViewCell/TrackersCollectionViewCell.swift | 4 ++-- Tracker/Views/CustomViews/HeaderReusableView.swift | 2 +- Tracker/Views/CustomViews/StatisticView.swift | 2 +- .../Views/TableViewCell/CreateTrackerTableViewCell.swift | 2 +- Tracker/Views/TableViewCell/CustomTableViewCell.swift | 2 +- 19 files changed, 33 insertions(+), 24 deletions(-) rename Tracker/Assets.xcassets/Colors/{textEventColor.colorset => liteDarkThemeColor.colorset}/Contents.json (100%) diff --git a/Tracker/Assets.xcassets/Colors/textEventColor.colorset/Contents.json b/Tracker/Assets.xcassets/Colors/liteDarkThemeColor.colorset/Contents.json similarity index 100% rename from Tracker/Assets.xcassets/Colors/textEventColor.colorset/Contents.json rename to Tracker/Assets.xcassets/Colors/liteDarkThemeColor.colorset/Contents.json diff --git a/Tracker/Extension/UIColor.swift b/Tracker/Extension/UIColor.swift index 2b18c52..0575c96 100644 --- a/Tracker/Extension/UIColor.swift +++ b/Tracker/Extension/UIColor.swift @@ -39,7 +39,7 @@ extension UIColor { static var redGradient: UIColor {UIColor(named: "redGradient") ?? .red} static var greenGradient: UIColor {UIColor(named: "greenGradient") ?? .green} static var blueGradient: UIColor {UIColor(named: "blueGradient") ?? .blue} - static var textEventColor: UIColor {UIColor(named: "textEventColor") ?? .gray} + static var liteDarkThemeColor: UIColor {UIColor(named: "liteDarkThemeColor") ?? .gray} static var separatorColor: UIColor {UIColor(named: "separatorColor") ?? .gray} static var switchBgColor: UIColor {UIColor(named: "switchBgColor") ?? .gray} static var placeholder: UIColor {UIColor(named: "placeholder") ?? .gray} diff --git a/Tracker/Helpers/Colors.swift b/Tracker/Helpers/Colors.swift index afb2bd4..96ef7b2 100644 --- a/Tracker/Helpers/Colors.swift +++ b/Tracker/Helpers/Colors.swift @@ -8,7 +8,6 @@ import UIKit final class Colors { - lazy var viewBackground = UIColor.systemBackground lazy var buttonDisabledColor = UIColor { (traits: UITraitCollection) -> UIColor in if traits.userInterfaceStyle == .light { diff --git a/Tracker/Resources/en.lproj/Localizable.strings b/Tracker/Resources/en.lproj/Localizable.strings index 6224312..be63b6d 100644 --- a/Tracker/Resources/en.lproj/Localizable.strings +++ b/Tracker/Resources/en.lproj/Localizable.strings @@ -29,7 +29,7 @@ //MARK: - TrackersViewController "headerText" = "Trackers"; -"labelStabText" = "What will we track?"; +"trackerLabelStabText" = "What are we going to track?"; "labelNothingFoundText" = "Nothing found"; "placeholderSearch" = "Search"; "textCancel" = "Cancel"; diff --git a/Tracker/ViewControllers/TabBarController.swift b/Tracker/ViewControllers/TabBarController.swift index 8e08cc5..d293f92 100644 --- a/Tracker/ViewControllers/TabBarController.swift +++ b/Tracker/ViewControllers/TabBarController.swift @@ -20,6 +20,7 @@ final class TabBarController: UITabBarController { override func viewDidLoad() { super.viewDidLoad() + view.backgroundColor = .liteDarkThemeColor tabBar.layer.borderWidth = 1 tabBar.clipsToBounds = true let trackerViewController = TrackersViewController(viewModel: TrackerViewModel()) diff --git a/Tracker/ViewControllers/ViewControllers/CreateTrackerViewController.swift b/Tracker/ViewControllers/ViewControllers/CreateTrackerViewController.swift index 835c2e2..16e84f9 100644 --- a/Tracker/ViewControllers/ViewControllers/CreateTrackerViewController.swift +++ b/Tracker/ViewControllers/ViewControllers/CreateTrackerViewController.swift @@ -232,7 +232,7 @@ final class CreateTrackerViewController: UIViewController { super.viewDidLoad() bind() setupUIElement() - view.backgroundColor = colors.viewBackground + view.backgroundColor = .liteDarkThemeColor let tapGesture = UITapGestureRecognizer(target: self, action: #selector(hideKeyboard)) tapGesture.cancelsTouchesInView = false view.addGestureRecognizer(tapGesture) @@ -342,7 +342,7 @@ extension CreateTrackerViewController { private func isActivCreateButton(flag: Bool) { createButton.isEnabled = flag createButton.backgroundColor = flag ? colors.buttonDisabledColor : .grayDay - createButton.setTitleColor( flag ? .textEventColor : .whiteDay, for: .normal) + createButton.setTitleColor( flag ? .liteDarkThemeColor : .whiteDay, for: .normal) } func reverseIsSchedul() { diff --git a/Tracker/ViewControllers/ViewControllers/EventSelectionViewController.swift b/Tracker/ViewControllers/ViewControllers/EventSelectionViewController.swift index ff7a0fe..cef6f72 100644 --- a/Tracker/ViewControllers/ViewControllers/EventSelectionViewController.swift +++ b/Tracker/ViewControllers/ViewControllers/EventSelectionViewController.swift @@ -68,7 +68,7 @@ final class EventSelectionViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - view.backgroundColor = colors.viewBackground + view.backgroundColor = .liteDarkThemeColor setupLableGreate() setupStackView() } @@ -105,7 +105,7 @@ private extension EventSelectionViewController { let button = UIButton() button.setTitle(text.name, for: .normal) button.backgroundColor = colors.buttonEventColor - button.setTitleColor(.textEventColor, for: .normal) + button.setTitleColor(.liteDarkThemeColor, for: .normal) button.titleLabel?.font = font button.layer.cornerRadius = cornerRadius ?? CGFloat(0) button.layer.masksToBounds = cornerRadius == nil ? false : true diff --git a/Tracker/ViewControllers/ViewControllers/FiltersViewController.swift b/Tracker/ViewControllers/ViewControllers/FiltersViewController.swift index 8e49093..4f8f8c7 100644 --- a/Tracker/ViewControllers/ViewControllers/FiltersViewController.swift +++ b/Tracker/ViewControllers/ViewControllers/FiltersViewController.swift @@ -53,7 +53,7 @@ final class FiltersViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - view.backgroundColor = colors.viewBackground + view.backgroundColor = .liteDarkThemeColor setupCategoryLabel() setupTableView() } diff --git a/Tracker/ViewControllers/ViewControllers/NewCategoriViewController.swift b/Tracker/ViewControllers/ViewControllers/NewCategoriViewController.swift index 61d2d99..1bc286b 100644 --- a/Tracker/ViewControllers/ViewControllers/NewCategoriViewController.swift +++ b/Tracker/ViewControllers/ViewControllers/NewCategoriViewController.swift @@ -45,7 +45,7 @@ final class NewCategoriViewController: UIViewController { private lazy var readyButton: UIButton = { let readyButton = UIButton() readyButton.setTitle(Translate.buttonName, for: .normal) - readyButton.setTitleColor(.textEventColor, for: .normal) + readyButton.setTitleColor(.liteDarkThemeColor, for: .normal) readyButton.isEnabled = false readyButton.backgroundColor = .grayDay readyButton.titleLabel?.font = ConstantsNewCatVc.font @@ -90,7 +90,7 @@ final class NewCategoriViewController: UIViewController { action: #selector(hideKeyboard)) tapGesture.cancelsTouchesInView = false view.addGestureRecognizer(tapGesture) - view.backgroundColor = colors.viewBackground + view.backgroundColor = .liteDarkThemeColor setupUIElement() } } @@ -102,7 +102,7 @@ private extension NewCategoriViewController { guard let self else { return } chengeHiddenButton(flag: viewModel.getNewNameCategory().isEmpty) readyButton.backgroundColor = !text.isEmpty ? colors.buttonDisabledColor : .grayDay - readyButton.setTitleColor( !text.isEmpty ? .textEventColor : .whiteDay, for: .normal) + readyButton.setTitleColor( !text.isEmpty ? .liteDarkThemeColor : .whiteDay, for: .normal) } } @@ -131,7 +131,7 @@ private extension NewCategoriViewController { readyButton.isEnabled = !flag readyButton.backgroundColor = !flag ? .blackDay : .grayDay readyButton.backgroundColor = flag ? colors.buttonDisabledColor : .grayDay - readyButton.setTitleColor( flag ? .textEventColor : .whiteDay, for: .normal) + readyButton.setTitleColor( flag ? .liteDarkThemeColor : .whiteDay, for: .normal) } //MARK: - SetupUI diff --git a/Tracker/ViewControllers/ViewControllers/ScheduleViewController.swift b/Tracker/ViewControllers/ViewControllers/ScheduleViewController.swift index be3dee8..eeb1c15 100644 --- a/Tracker/ViewControllers/ViewControllers/ScheduleViewController.swift +++ b/Tracker/ViewControllers/ViewControllers/ScheduleViewController.swift @@ -59,7 +59,7 @@ final class ScheduleViewController: UIViewController { doneButton.layer.masksToBounds = true doneButton.backgroundColor = colors.buttonDisabledColor doneButton.setTitle(Translate.doneButtonText, for: .normal) - doneButton.setTitleColor(.textEventColor, for: .normal) + doneButton.setTitleColor(.liteDarkThemeColor, for: .normal) return doneButton }() @@ -84,7 +84,7 @@ final class ScheduleViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - view.backgroundColor = colors.viewBackground + view.backgroundColor = .liteDarkThemeColor setupContentSteck() } } diff --git a/Tracker/ViewControllers/ViewControllers/SelectCategoriesViewController.swift b/Tracker/ViewControllers/ViewControllers/SelectCategoriesViewController.swift index 452b3ad..d8957e8 100644 --- a/Tracker/ViewControllers/ViewControllers/SelectCategoriesViewController.swift +++ b/Tracker/ViewControllers/ViewControllers/SelectCategoriesViewController.swift @@ -93,7 +93,7 @@ final class SelectCategoriesViewController: UIViewController { private lazy var createCategoryButton: UIButton = { let createCategoryButton = UIButton() createCategoryButton.setTitle(Translate.categoryAddButtonText, for: .normal) - createCategoryButton.setTitleColor(.textEventColor, for: .normal) + createCategoryButton.setTitleColor(.liteDarkThemeColor, for: .normal) createCategoryButton.backgroundColor = colors.buttonDisabledColor createCategoryButton.titleLabel?.font = ConstantsCreateCatVc.lableFont createCategoryButton.layer.cornerRadius = ConstantsCreateCatVc.cornerRadius @@ -121,7 +121,7 @@ final class SelectCategoriesViewController: UIViewController { guard let viewModel = viewModel as? CategoryViewModel else { return } setupUIElement() bind() - view.backgroundColor = colors.viewBackground + view.backgroundColor = .liteDarkThemeColor handler.resultTypeHandler(viewModel.сategoryExcludingFixed(), vc: self) { [weak self] cat in guard let self else { return } self.showStabView(flag: !cat.isEmpty) diff --git a/Tracker/ViewControllers/ViewControllers/StatisticsViewController.swift b/Tracker/ViewControllers/ViewControllers/StatisticsViewController.swift index d1572d9..ac84ace 100644 --- a/Tracker/ViewControllers/ViewControllers/StatisticsViewController.swift +++ b/Tracker/ViewControllers/ViewControllers/StatisticsViewController.swift @@ -63,7 +63,7 @@ final class StatisticsViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - view.backgroundColor = colors.viewBackground + view.backgroundColor = .liteDarkThemeColor viewModel = StatisticsViewModel() showStabView(flag: viewModel?.getIsTracker()) bind() diff --git a/Tracker/ViewControllers/ViewControllers/TrackersViewController.swift b/Tracker/ViewControllers/ViewControllers/TrackersViewController.swift index f2ce8eb..1e97f4d 100644 --- a/Tracker/ViewControllers/ViewControllers/TrackersViewController.swift +++ b/Tracker/ViewControllers/ViewControllers/TrackersViewController.swift @@ -158,7 +158,7 @@ final class TrackersViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - view.backgroundColor = colors.viewBackground + view.backgroundColor = .liteDarkThemeColor setupUIElement() viewModel = TrackerViewModel() bind() @@ -241,6 +241,10 @@ private extension TrackersViewController { func actionForTapDatePicker() { guard let viewModel else { return } filterState(state: viewModel.getFilterState()) + if viewModel.getFilterState() == FiltersState.toDayTrackers { + viewModel.setSelectFilter(selectFilter: FiltersState.allTrackers.rawValue) + setColorButtonFilter(state: .allTrackers, button: buttonFilter) + } } @objc diff --git a/Tracker/ViewModels/TrackerViewModel.swift b/Tracker/ViewModels/TrackerViewModel.swift index 3be2312..f2431bd 100644 --- a/Tracker/ViewModels/TrackerViewModel.swift +++ b/Tracker/ViewModels/TrackerViewModel.swift @@ -28,6 +28,7 @@ protocol TrackerViewModelProtocol { func allTrackersByDate(date: Date) func getNotCompleted(date: Date, flag: Bool) func getCompleted(date: Date, flag: Bool) + func setSelectFilter(selectFilter: String) func setFilterState(state: FiltersState) func getFilterState() -> FiltersState func getIsCategoryForDay() -> Bool? @@ -333,6 +334,10 @@ extension TrackerViewModel: TrackerViewModelProtocol filterState = state } + func setSelectFilter(selectFilter: String) { + self.selectFilter = selectFilter + } + func getFilterState() -> FiltersState { filterState } diff --git a/Tracker/Views/CollectionViewCell/TrackersCollectionViewCell.swift b/Tracker/Views/CollectionViewCell/TrackersCollectionViewCell.swift index 4d79b61..15b7c2f 100644 --- a/Tracker/Views/CollectionViewCell/TrackersCollectionViewCell.swift +++ b/Tracker/Views/CollectionViewCell/TrackersCollectionViewCell.swift @@ -144,11 +144,11 @@ extension TrackersCollectionViewCell { addButton.setImage(image, for: .normal) addButton.backgroundColor = updateModel.color.withAlphaComponent( ConstantsTrackerCell.alphaComponentDoneButton) - addButton.tintColor = .textEventColor + addButton.tintColor = .liteDarkThemeColor case false: let image = UIImage(named: ConstantsTrackerCell.addButtonImageAdd)?.withRenderingMode(.alwaysTemplate) addButton.setImage(image, for: .normal) - addButton.tintColor = .textEventColor + addButton.tintColor = .liteDarkThemeColor } } diff --git a/Tracker/Views/CustomViews/HeaderReusableView.swift b/Tracker/Views/CustomViews/HeaderReusableView.swift index f3c5ce1..f7528a9 100644 --- a/Tracker/Views/CustomViews/HeaderReusableView.swift +++ b/Tracker/Views/CustomViews/HeaderReusableView.swift @@ -26,7 +26,7 @@ final class HeaderReusableView: UICollectionReusableView { override init(frame: CGRect) { super.init(frame: frame) - backgroundColor = colors.viewBackground + backgroundColor = .liteDarkThemeColor setupLable() } diff --git a/Tracker/Views/CustomViews/StatisticView.swift b/Tracker/Views/CustomViews/StatisticView.swift index 9070e2c..b18c44f 100644 --- a/Tracker/Views/CustomViews/StatisticView.swift +++ b/Tracker/Views/CustomViews/StatisticView.swift @@ -34,7 +34,7 @@ final class StatisticsView: UIView { override init(frame: CGRect) { super.init(frame: frame) setupView() - backgroundColor = colors.viewBackground + backgroundColor = .liteDarkThemeColor } required init?(coder: NSCoder) { diff --git a/Tracker/Views/TableViewCell/CreateTrackerTableViewCell.swift b/Tracker/Views/TableViewCell/CreateTrackerTableViewCell.swift index 4a84d70..537f037 100644 --- a/Tracker/Views/TableViewCell/CreateTrackerTableViewCell.swift +++ b/Tracker/Views/TableViewCell/CreateTrackerTableViewCell.swift @@ -52,7 +52,7 @@ final class CreateTrackerTableViewCell: UITableViewCell { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) selectionStyle = .none - backgroundColor = colors.viewBackground + backgroundColor = .liteDarkThemeColor setupUIElement() } diff --git a/Tracker/Views/TableViewCell/CustomTableViewCell.swift b/Tracker/Views/TableViewCell/CustomTableViewCell.swift index e5cb9bb..161a9c0 100644 --- a/Tracker/Views/TableViewCell/CustomTableViewCell.swift +++ b/Tracker/Views/TableViewCell/CustomTableViewCell.swift @@ -38,7 +38,7 @@ final class CustomTableViewCell: UITableViewCell { super.init(style: style, reuseIdentifier: reuseIdentifier) selectionStyle = .none layer.masksToBounds = true - backgroundColor = colors.viewBackground + backgroundColor = .liteDarkThemeColor setupLable() setupSelectedImage() } From 7698a339b6b087972266fbc3f829f4f99a79ae3f Mon Sep 17 00:00:00 2001 From: Grifon Date: Thu, 1 Feb 2024 20:24:51 +0300 Subject: [PATCH 23/24] refactoring filter --- .../TrackersViewController.swift | 73 +++++++++++++------ Tracker/ViewModels/TrackerViewModel.swift | 51 ++++++++----- 2 files changed, 85 insertions(+), 39 deletions(-) diff --git a/Tracker/ViewControllers/ViewControllers/TrackersViewController.swift b/Tracker/ViewControllers/ViewControllers/TrackersViewController.swift index 1e97f4d..ac038d6 100644 --- a/Tracker/ViewControllers/ViewControllers/TrackersViewController.swift +++ b/Tracker/ViewControllers/ViewControllers/TrackersViewController.swift @@ -169,7 +169,8 @@ final class TrackersViewController: UIViewController { filterState(state: state) } } else { - viewModel.allTrackersByDate(date: datePicker.date) + let cat = viewModel.allTrackersByDate(date: datePicker.date) + viewModel.showVisibleCategory(categoty: cat) } } @@ -207,17 +208,21 @@ private extension TrackersViewController { guard let self else { return } switch state { case .allTrackers: - viewModel.allTrackersByDate(date: datePicker.date) + let cat = viewModel.allTrackersByDate(date: datePicker.date) + viewModel.showVisibleCategory(categoty: cat) setColorButtonFilter(state: state, button: buttonFilter) case .toDayTrackers: datePicker.date = Date() - viewModel.allTrackersByDate(date: datePicker.date) + let cat = viewModel.allTrackersByDate(date: datePicker.date) + viewModel.showVisibleCategory(categoty: cat) setColorButtonFilter(state: state, button: buttonFilter) case .completed: - viewModel.getCompleted(date: datePicker.date, flag: true) + let cat = viewModel.getCompleted(date: datePicker.date, flag: true) + viewModel.showVisibleCategory(categoty: cat) setColorButtonFilter(state: state, button: buttonFilter) case .notCompleted: - viewModel.getNotCompleted(date: datePicker.date, flag: false) + let cat = viewModel.getNotCompleted(date: datePicker.date, flag: false) + viewModel.showVisibleCategory(categoty: cat) setColorButtonFilter(state: state, button: buttonFilter) } } @@ -268,19 +273,23 @@ private extension TrackersViewController { guard let viewModel else { return } switch state { case .allTrackers: - viewModel.allTrackersByDate(date: datePicker.date) + let cat = viewModel.allTrackersByDate(date: datePicker.date) + viewModel.showVisibleCategory(categoty: cat) setColorButtonFilter(state: viewModel.getFilterState(), button: buttonFilter) case .toDayTrackers: - viewModel.allTrackersByDate(date: datePicker.date) + let cat = viewModel.allTrackersByDate(date: datePicker.date) + viewModel.showVisibleCategory(categoty: cat) setColorButtonFilter(state: viewModel.getFilterState(), button: buttonFilter) case .completed: - viewModel.getCompleted(date: datePicker.date, flag: true) + let cat = viewModel.getCompleted(date: datePicker.date, flag: true) + viewModel.showVisibleCategory(categoty: cat) setColorButtonFilter(state: viewModel.getFilterState(), button: buttonFilter) case .notCompleted: - viewModel.getNotCompleted(date: datePicker.date, flag: false) + let cat = viewModel.getNotCompleted(date: datePicker.date, flag: false) + viewModel.showVisibleCategory(categoty: cat) setColorButtonFilter(state: viewModel.getFilterState(), button: buttonFilter) } @@ -455,7 +464,7 @@ private extension TrackersViewController { searchController.searchBar.resignFirstResponder() searchController.searchBar.returnKeyType = .search searchController.searchBar.setValue(Translate.textCancel, - forKey: ConstantsTrackerVc.forKeyTextCancel) + forKey: ConstantsTrackerVc.forKeyTextCancel) searchController.searchBar.searchTextField.leftView?.tintColor = colors.placeholder searchController.searchBar.searchTextField.attributedPlaceholder = NSAttributedString.init(string: Translate.placeholderSearch, attributes: [NSAttributedString.Key.foregroundColor: colors.placeholder]) } @@ -560,7 +569,7 @@ extension TrackersViewController: UICollectionViewDataSource { guard let self, let isComplited = self.handler.resultTypeHandlerGetValue( viewModel.getIsComplited(tracker: tracker, date: self.datePicker.date), - vc: self) + vc: self) else { return } let updateModel = UpdateTracker(count: count, flag: isComplited, @@ -696,21 +705,43 @@ extension TrackersViewController: UISearchResultsUpdating { let viewModel else { return } if !word.isEmpty { - guard let seachCategory = handler.resultTypeHandlerGetValue(viewModel.filterListTrackersName(word: word), - vc: self) - else { return } - viewModel.getShowListTrackerSearchForName(seachCategory) + switch viewModel.getFilterState() { + case .allTrackers: + let cat = viewModel.allTrackersByDate(date: datePicker.date) + if let seachCategory = handler.resultTypeHandlerGetValue( + viewModel.filterListTrackersName(word: word, + category: cat), vc: self) { + viewModel.showVisibleCategory(categoty: .success(seachCategory)) + } + case .toDayTrackers: + let cat = viewModel.allTrackersByDate(date: datePicker.date) + if let seachCategory = handler.resultTypeHandlerGetValue( + viewModel.filterListTrackersName(word: word, + category: cat), vc: self) { + viewModel.showVisibleCategory(categoty: .success(seachCategory)) + } + case .completed: + let cat = viewModel.getCompleted(date: datePicker.date, flag: true) + if let seachCategory = handler.resultTypeHandlerGetValue( + viewModel.filterListTrackersName(word: word, + category: cat), vc: self) { + viewModel.showVisibleCategory(categoty: .success(seachCategory)) + } + case .notCompleted: + let cat = viewModel.getNotCompleted(date: datePicker.date, flag: false) + if let seachCategory = handler.resultTypeHandlerGetValue( + viewModel.filterListTrackersName(word: word, + category: cat), vc: self) { + viewModel.showVisibleCategory(categoty: .success(seachCategory)) + } + } return } if word.isEmpty { - let _ = viewModel.getShowListTrackersForDay(date: datePicker.date) - chengeStab(text: Translate.labelNothingFoundText, - nameImage: ConstantsTrackerVc.imageNothingFound) + filterState(state: viewModel.getFilterState()) } if !searchController.isActive { - let _ = viewModel.getShowListTrackersForDay(date: datePicker.date) - chengeStab(text: Translate.trackerLabelStabText, - nameImage: ConstantsTrackerVc.imageStar) + filterState(state: viewModel.getFilterState()) } } } diff --git a/Tracker/ViewModels/TrackerViewModel.swift b/Tracker/ViewModels/TrackerViewModel.swift index f2431bd..215d0fd 100644 --- a/Tracker/ViewModels/TrackerViewModel.swift +++ b/Tracker/ViewModels/TrackerViewModel.swift @@ -18,20 +18,22 @@ protocol TrackerViewModelProtocol { func addNewTracker(_ tracker: Tracker, nameCategory: String) -> Result func updateTracker(tracker: Tracker, nameCategory: String?) -> Result func deleteTracker(id: UUID) -> Result - func getShowListTrackersForDay(date: Date) + func getShowListTrackersForDay(date: Date) -> Result<[TrackerCategory], Error> func getShowListTrackerSearchForName(_ searchCategory: [TrackerCategory]) - func filterListTrackersName(word: String) -> Result<[TrackerCategory], Error> + func filterListTrackersName(word: String, + category: Result<[TrackerCategory], Error>) -> Result<[TrackerCategory], Error> func addPinnedCategory(id: UUID, nameCategory: String) -> Result func editPinnedCategory(_ id: UUID, newPinnedCat: String) -> Result func getPinnedCategory(_ id: UUID) -> Result func deleteAndGetPinnedCategory(id: UUID) -> Result - func allTrackersByDate(date: Date) - func getNotCompleted(date: Date, flag: Bool) - func getCompleted(date: Date, flag: Bool) + func allTrackersByDate(date: Date) -> Result<[TrackerCategory], Error> + func getNotCompleted(date: Date, flag: Bool) -> Result<[TrackerCategory], Error> + func getCompleted(date: Date, flag: Bool) -> Result<[TrackerCategory], Error> func setSelectFilter(selectFilter: String) func setFilterState(state: FiltersState) func getFilterState() -> FiltersState func getIsCategoryForDay() -> Bool? + func showVisibleCategory(categoty: Result<[TrackerCategory], Error>) } //MARK: - TrackerViewModel @@ -120,7 +122,7 @@ private extension TrackerViewModel { } } - func setVisibleCategory(date: Date, flag: Bool) { + func setVisibleCategory(date: Date, flag: Bool) -> Result<[TrackerCategory], Error> { switch category { case .success(let category): var listCategories: [TrackerCategory] = [] @@ -136,12 +138,14 @@ private extension TrackerViewModel { listCategories.append(cat) } } catch { - visibleCategory = .failure(error) +// visibleCategory = .failure(error) } } - visibleCategory = .success(listCategories) +// visibleCategory = .success(listCategories) + return .success(listCategories) case .failure(let error): - visibleCategory = .failure(error) +// visibleCategory = .failure(error) + return .failure(error) } } @@ -167,11 +171,11 @@ private extension TrackerViewModel { return listCategories } - func filterListTrackersCompleted(date: Date, flag: Bool) { + func filterListTrackersCompleted(date: Date, flag: Bool) -> Result<[TrackerCategory], Error> { setVisibleCategory(date: date, flag: flag) } - func filterListTrackersNotCompleted(date: Date, flag: Bool) { + func filterListTrackersNotCompleted(date: Date, flag: Bool) -> Result<[TrackerCategory], Error> { setVisibleCategory(date: date, flag: flag) } @@ -242,18 +246,20 @@ extension TrackerViewModel: TrackerViewModelProtocol trackerStore.updateTracker(tracker: tracker, nameCategory: nameCategory) } - func getShowListTrackersForDay(date: Date) { + func getShowListTrackersForDay(date: Date) -> Result<[TrackerCategory], Error> { switch category { case .success(let trackerCategory): let filterList = filterListTrackersWeekDay(trackerCategory: trackerCategory, date: date) - visibleCategory = .success(filterList) +// visibleCategory = .success(filterList) + return .success(filterList) case .failure(let error): - visibleCategory = .failure(error) +// visibleCategory = .failure(error) + return .failure(error) } } - func filterListTrackersName(word: String) -> Result<[TrackerCategory], Error> { + func filterListTrackersName(word: String, category: Result<[TrackerCategory], Error>) -> Result<[TrackerCategory], Error> { var listCategories: [TrackerCategory] = [] switch category { case .success(let cat): @@ -318,15 +324,15 @@ extension TrackerViewModel: TrackerViewModelProtocol pinnedCategoryStore.deleteAndGetPinnedCategory(id) } - func allTrackersByDate(date: Date) { + func allTrackersByDate(date: Date) -> Result<[TrackerCategory], Error> { getShowListTrackersForDay(date: date) } - func getNotCompleted(date: Date, flag: Bool) { + func getNotCompleted(date: Date, flag: Bool) -> Result<[TrackerCategory], Error> { filterListTrackersNotCompleted(date: date, flag: flag) } - func getCompleted(date: Date, flag: Bool) { + func getCompleted(date: Date, flag: Bool) -> Result<[TrackerCategory], Error> { filterListTrackersCompleted(date: date, flag: flag) } @@ -353,6 +359,15 @@ extension TrackerViewModel: TrackerViewModelProtocol func getIsCategoryForDay() -> Bool? { isfilterListTrackersWeekDay } + + func showVisibleCategory(categoty: Result<[TrackerCategory], Error>) { + switch categoty { + case .success(let cat): + visibleCategory = .success(cat) + case .failure(let error): + visibleCategory = .failure(error) + } + } } //MARK: - TrackerCategoryStoreDelegate From effb4462a8aa0b45ca2005a834e969f797c83976 Mon Sep 17 00:00:00 2001 From: Grifon Date: Thu, 1 Feb 2024 20:36:39 +0300 Subject: [PATCH 24/24] refactoring --- Tracker/ViewModels/TrackerViewModel.swift | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/Tracker/ViewModels/TrackerViewModel.swift b/Tracker/ViewModels/TrackerViewModel.swift index 215d0fd..60240af 100644 --- a/Tracker/ViewModels/TrackerViewModel.swift +++ b/Tracker/ViewModels/TrackerViewModel.swift @@ -137,14 +137,10 @@ private extension TrackerViewModel { if !cat.arrayTrackers.isEmpty { listCategories.append(cat) } - } catch { -// visibleCategory = .failure(error) - } + } catch { } } -// visibleCategory = .success(listCategories) return .success(listCategories) case .failure(let error): -// visibleCategory = .failure(error) return .failure(error) } } @@ -251,10 +247,8 @@ extension TrackerViewModel: TrackerViewModelProtocol case .success(let trackerCategory): let filterList = filterListTrackersWeekDay(trackerCategory: trackerCategory, date: date) -// visibleCategory = .success(filterList) return .success(filterList) case .failure(let error): -// visibleCategory = .failure(error) return .failure(error) } }