diff --git a/ClassDataModel.xcdatamodeld/ClassDataModel.xcdatamodel/contents b/ClassDataModel.xcdatamodeld/ClassDataModel.xcdatamodel/contents
index 65ba196..efc5761 100644
--- a/ClassDataModel.xcdatamodeld/ClassDataModel.xcdatamodel/contents
+++ b/ClassDataModel.xcdatamodeld/ClassDataModel.xcdatamodel/contents
@@ -1,6 +1,6 @@
-
+
@@ -12,4 +12,11 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Schedule ICTIS.xcodeproj/xcuserdata/g412.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/Schedule ICTIS.xcodeproj/xcuserdata/g412.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
index 1c2910a..e96d03a 100644
--- a/Schedule ICTIS.xcodeproj/xcuserdata/g412.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
+++ b/Schedule ICTIS.xcodeproj/xcuserdata/g412.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
@@ -8,7 +8,7 @@
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
some View {
+ ScrollView(.vertical, showsIndicators: false) {
+ VStack(spacing: 30) {
+ subjectsSection(isOnline: isOnline)
+ myPairsSection
+ }
+ .frame(width: UIScreen.main.bounds.width)
+ .padding(.bottom, 100)
+ .padding(.top, 10)
+ .background(GeometryReader { geometry in
+ Color.clear.preference(key: ViewOffsetKey.self, value: geometry.frame(in: .global).minY)
+ })
+ }
+ .onPreferenceChange(ViewOffsetKey.self) { offset in
+ if offset != lastOffset {
+ isScrolling = true
+ scrollTimer?.invalidate()
+ scrollTimer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: false) { _ in
+ isScrolling = false
+ }
+ }
+ lastOffset = offset
+ }
+ .onDisappear {
+ scrollTimer?.invalidate()
+ }
+ }
+
+ // Секция с парами
+ private func subjectsSection(isOnline: Bool) -> some View {
+ VStack(alignment: .leading, spacing: 10) {
+ if isOnline {
+ ForEach(0.. = JsonClassModel.fetchRequest()
+ let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
+
+ try context.execute(deleteRequest)
+ try context.save()
+ print("✅ Все объекты JsonClassModel успешно удалены")
+ }
+
+ func checkSavingOncePerDay() {
+ let today = Date()
+ let calendar = Calendar.current
+ let todayStart = calendar.startOfDay(for: today) // Начало текущего дня
+
+ // Получаем дату последнего выполнения из UserDefaults
+ let lastCheckDate = UserDefaults.standard.object(forKey: "LastSaving") as? Date ?? .distantPast
+ let lastCheckStart = calendar.startOfDay(for: lastCheckDate)
+
+ print("Дата последнего сохранения расписания в CoreData: \(lastCheckDate)")
+
+ // Проверяем, был ли уже выполнен код сегодня
+ if lastCheckStart < todayStart && networkMonitor.isConnected {
+ print("✅ Интернет есть, сохранение пар в CoreData")
+ vm.fillDictForVm()
+ vm.fetchWeekSchedule()
+ do {
+ try deleteAllJsonClassModelsSync()
+ } catch {
+ print("Ошибка при удалении: \(error)")
+ return
+ }
+ saveGroupsToMemory()
+
+ // Сохраняем текущую дату как дату последнего выполнения
+ UserDefaults.standard.set(today, forKey: "LastSaving")
+ }
}
}
@@ -136,7 +259,3 @@ struct ViewOffsetKey: PreferenceKey {
value += nextValue()
}
}
-
-#Preview {
- ContentView()
-}
diff --git a/Schedule ICTIS/Main/Views/SearchBarView.swift b/Schedule ICTIS/Main/Views/SearchBarView.swift
index 199ced0..c061f1b 100644
--- a/Schedule ICTIS/Main/Views/SearchBarView.swift
+++ b/Schedule ICTIS/Main/Views/SearchBarView.swift
@@ -31,9 +31,11 @@ struct SearchBarView: View {
if (!text.isEmpty) {
vm.nameToHtml[vm.searchingGroup] = nil
vm.removeFromSchedule(group: vm.searchingGroup)
+ text = transformStringToFormat(text)
vm.searchingGroup = text
vm.nameToHtml[text] = ""
vm.fetchWeekSchedule()
+ vm.updateFilteringGroups()
}
self.text = ""
}
@@ -88,6 +90,3 @@ struct SearchBarView: View {
}
}
-#Preview {
- ContentView()
-}
diff --git a/Schedule ICTIS/Main/Views/SubjectView.swift b/Schedule ICTIS/Main/Views/SubjectView.swift
new file mode 100644
index 0000000..4030bc0
--- /dev/null
+++ b/Schedule ICTIS/Main/Views/SubjectView.swift
@@ -0,0 +1,61 @@
+//
+// SubjectView.swift
+// Schedule ICTIS
+//
+// Created by Egor Mironov on 02.04.2025.
+//
+
+import SwiftUI
+
+struct SubjectView: View {
+ let info: ClassInfo
+ @ObservedObject var vm: ScheduleViewModel
+ @State private var onlyOneGroup: Bool = false
+
+ var body: some View {
+ VStack(alignment: .trailing) {
+ if !onlyOneGroup {
+ Text(info.group)
+ .font(.custom("Montserrat-Regular", fixedSize: 11))
+ .foregroundColor(Color("grayForNameGroup"))
+ }
+ HStack(spacing: 15) {
+ VStack {
+ Text(convertTimeString(info.time)[0])
+ .font(.custom("Montserrat-Regular", fixedSize: 15))
+ .padding(.bottom, 1)
+ Text(convertTimeString(info.time)[1])
+ .font(.custom("Montserrat-Regular", fixedSize: 15))
+ .padding(.top, 1)
+ }
+ .frame(width: 48)
+ .padding(.top, 7)
+ .padding(.bottom, 7)
+ .padding(.leading, 10)
+ Rectangle()
+ .frame(width: 2)
+ .frame(maxHeight: UIScreen.main.bounds.height - 18)
+ .padding(.top, 7)
+ .padding(.bottom, 7)
+ .foregroundColor(getColorForClass(info.subject))
+ Text(info.subject)
+ .font(.custom("Montserrat-Medium", fixedSize: 16))
+ .lineSpacing(3)
+ .padding(.top, 9)
+ .padding(.bottom, 9)
+ Spacer()
+ }
+ .frame(maxWidth: UIScreen.main.bounds.width - 40, maxHeight: 230)
+ .background(Color.white)
+ .cornerRadius(20)
+ .shadow(color: .black.opacity(0.25), radius: 4, x: 2, y: 2)
+ }
+ .onAppear {
+ onlyOneGroup = (vm.showOnlyChoosenGroup != vm.filteringGroups[0])
+ }
+ .padding(.bottom, onlyOneGroup ? 17 : 0)
+ .onChange(of: vm.showOnlyChoosenGroup) { oldValue, newValue in
+ onlyOneGroup = (newValue != vm.filteringGroups[0])
+ }
+ }
+}
diff --git a/Schedule ICTIS/Main/Views/TabViews/MonthTabView.swift b/Schedule ICTIS/Main/Views/TabViews/MonthTabView.swift
index 7c8514c..1f92190 100644
--- a/Schedule ICTIS/Main/Views/TabViews/MonthTabView.swift
+++ b/Schedule ICTIS/Main/Views/TabViews/MonthTabView.swift
@@ -15,31 +15,31 @@ struct MonthTabView: View {
@ObservedObject var vm: ScheduleViewModel
var body: some View {
VStack {
- HStack (spacing: 34) {
+ HStack (spacing: 33) {
ForEach(MockData.daysOfWeek.indices, id: \.self) { index in
Text(MockData.daysOfWeek[index])
.font(.custom("Montserrat-SemiBold", fixedSize: 15))
.foregroundColor(MockData.daysOfWeek[index] == "Вс" ? Color(.red) : Color("customGray2"))
- .padding(.top, 13)
- .foregroundColor(.gray)
}
}
- .padding(.top, 14)
TabView(selection: $currentMonthIndex) {
ForEach(monthSlider.indices, id: \.self) { index in
let month = monthSlider[index]
MonthView(month)
.tag(index)
+ .transition(.slide)
}
}
- .padding(.top, -25)
- .padding(.bottom, -10)
.padding(.horizontal, -15)
.tabViewStyle(.page(indexDisplayMode: .never))
+ //.animation(.easeIn(duration: 0.3), value: currentMonthIndex)
}
- .onAppear(perform: {
+ .frame(height: 220)
+ .padding(.top, 26)
+ .padding(.bottom, 20)
+ .onAppear {
updateMonthScreenViewForNewGroup()
- })
+ }
.onChange(of: currentMonthIndex, initial: false) { oldValue, newValue in
if newValue == 0 || newValue == (monthSlider.count - 1) {
createMonth = true
@@ -82,6 +82,3 @@ struct MonthTabView: View {
}
-#Preview {
- ContentView()
-}
diff --git a/Schedule ICTIS/Main/Views/TabViews/WeekTabView.swift b/Schedule ICTIS/Main/Views/TabViews/WeekTabView.swift
index da58428..30a896a 100644
--- a/Schedule ICTIS/Main/Views/TabViews/WeekTabView.swift
+++ b/Schedule ICTIS/Main/Views/TabViews/WeekTabView.swift
@@ -44,7 +44,3 @@ struct WeekTabView: View {
}
}
}
-
-#Preview {
- ContentView()
-}
diff --git a/Schedule ICTIS/Model/ClassModel.swift b/Schedule ICTIS/Model/CoreDataClassModel.swift
similarity index 79%
rename from Schedule ICTIS/Model/ClassModel.swift
rename to Schedule ICTIS/Model/CoreDataClassModel.swift
index b18c8d5..71f904f 100644
--- a/Schedule ICTIS/Model/ClassModel.swift
+++ b/Schedule ICTIS/Model/CoreDataClassModel.swift
@@ -8,7 +8,7 @@
import Foundation
import CoreData
-final class ClassModel: NSManagedObject, Identifiable {
+final class CoreDataClassModel: NSManagedObject, Identifiable {
@NSManaged var auditory: String
@NSManaged var professor: String
@NSManaged var subject: String
@@ -47,29 +47,29 @@ final class ClassModel: NSManagedObject, Identifiable {
}
// Расширение для загрузки данных из памяти
-extension ClassModel {
+extension CoreDataClassModel {
// Получаем все данные из памяти
- private static var classesFetchRequest: NSFetchRequest {
- NSFetchRequest(entityName: "ClassModel")
+ private static var classesFetchRequest: NSFetchRequest {
+ NSFetchRequest(entityName: "CoreDataClassModel")
}
// Получаем все данные и сортируем их по дню
// Этот метод будет использоваться на View(ScheduleView), где отображаются пары
- static func all() -> NSFetchRequest {
- let request: NSFetchRequest = classesFetchRequest
+ static func all() -> NSFetchRequest {
+ let request: NSFetchRequest = classesFetchRequest
request.sortDescriptors = [
- NSSortDescriptor(keyPath: \ClassModel.day, ascending: true)
+ NSSortDescriptor(keyPath: \CoreDataClassModel.day, ascending: true)
]
return request
}
}
-extension ClassModel {
+extension CoreDataClassModel {
@discardableResult
- static func makePreview(count: Int, in context: NSManagedObjectContext) -> [ClassModel] {
- var classes = [ClassModel]()
+ static func makePreview(count: Int, in context: NSManagedObjectContext) -> [CoreDataClassModel] {
+ var classes = [CoreDataClassModel]()
for i in 0.. ClassModel {
+ static func preview(context: NSManagedObjectContext = ClassProvider.shared.viewContext) -> CoreDataClassModel {
return makePreview(count: 1, in: context)[0]
}
- static func empty(context: NSManagedObjectContext = ClassProvider.shared.viewContext) -> ClassModel {
- return ClassModel(context: context)
+ static func empty(context: NSManagedObjectContext = ClassProvider.shared.viewContext) -> CoreDataClassModel {
+ return CoreDataClassModel(context: context)
}
}
diff --git a/Schedule ICTIS/Model/GroupsModel.swift b/Schedule ICTIS/Model/GroupsModel.swift
index afc0a94..09b32ef 100644
--- a/Schedule ICTIS/Model/GroupsModel.swift
+++ b/Schedule ICTIS/Model/GroupsModel.swift
@@ -9,11 +9,11 @@ import Foundation
// MARK: - Welcome
struct Welcome: Decodable {
- let choices: [Choice]
+ let choices: [Subject]
}
// MARK: - Choice
-struct Choice: Decodable, Identifiable {
+struct Subject: Decodable, Identifiable {
let name: String
let id: String
let group: String
diff --git a/Schedule ICTIS/Model/JsonClassModel.swift b/Schedule ICTIS/Model/JsonClassModel.swift
new file mode 100644
index 0000000..a29168f
--- /dev/null
+++ b/Schedule ICTIS/Model/JsonClassModel.swift
@@ -0,0 +1,46 @@
+//
+// JsonClassModel.swift
+// Schedule ICTIS
+//
+// Created by Mironov Egor on 27.03.2025.
+//
+
+import Foundation
+import CoreData
+
+final class JsonDataClassModel: NSManagedObject, Identifiable {
+ @NSManaged var name: String
+ @NSManaged var group: String
+ @NSManaged var time: String
+ @NSManaged var day: Int16
+
+ // Здесь мы выполняем дополнительную инициализацию, назначая значения по умолчанию
+ // Этот метод вызывается всякий раз, когда объект Core Data вставляется в контекст
+ override func awakeFromInsert() {
+ super.awakeFromInsert()
+
+ setPrimitiveValue("", forKey: "name")
+ setPrimitiveValue("", forKey: "group")
+ setPrimitiveValue("", forKey: "time")
+ setPrimitiveValue(0, forKey: "day")
+ setPrimitiveValue(0, forKey: "week")
+ }
+}
+
+// Расширение для загрузки данных из памяти
+extension JsonClassModel {
+ // Получаем все данные из памяти
+ private static var subjectsFetchRequest: NSFetchRequest {
+ NSFetchRequest(entityName: "JsonClassModel")
+ }
+
+ // Получаем все данные и сортируем их по дню
+ // Этот метод будет использоваться на View(ScheduleView), где отображаются пары
+ static func all() -> NSFetchRequest {
+ let request: NSFetchRequest = subjectsFetchRequest
+ request.sortDescriptors = [
+ NSSortDescriptor(keyPath: \JsonClassModel.time, ascending: true)
+ ]
+ return request
+ }
+}
diff --git a/Schedule ICTIS/NetworkErrorView.swift b/Schedule ICTIS/NetworkErrorView.swift
new file mode 100644
index 0000000..a45f8ea
--- /dev/null
+++ b/Schedule ICTIS/NetworkErrorView.swift
@@ -0,0 +1,31 @@
+//
+// NetworkErrorView.swift
+// Schedule ICTIS
+//
+// Created by Mironov Egor on 26.03.2025.
+//
+
+import SwiftUI
+
+struct NetworkErrorView: View {
+ var body: some View {
+ VStack {
+ Spacer()
+ VStack {
+ Image(systemName: "wifi.slash")
+ .font(.system(size: 60, weight: .light))
+ .frame(width: 70, height: 70)
+ Text("Восстановите подключение к интернету чтобы мы могли загрузить расписание")
+ .font(.custom("Montserrat-Medium", fixedSize: 15))
+ .padding(.top, 5)
+ }
+ .padding(.horizontal, 30)
+ .padding(.top, UIScreen.main.bounds.height/8)
+ Spacer()
+ }
+ }
+}
+
+#Preview {
+ NetworkErrorView()
+}
diff --git a/Schedule ICTIS/Provider/ClassProvider.swift b/Schedule ICTIS/Provider/ClassProvider.swift
index 5e7f4a2..839a08f 100644
--- a/Schedule ICTIS/Provider/ClassProvider.swift
+++ b/Schedule ICTIS/Provider/ClassProvider.swift
@@ -49,11 +49,11 @@ final class ClassProvider {
}
}
- func exists(_ lesson: ClassModel, in context: NSManagedObjectContext) -> ClassModel? {
- try? context.existingObject(with: lesson.objectID) as? ClassModel
+ func exists(_ lesson: CoreDataClassModel, in context: NSManagedObjectContext) -> CoreDataClassModel? {
+ try? context.existingObject(with: lesson.objectID) as? CoreDataClassModel
}
- func delete(_ lesson: ClassModel, in context: NSManagedObjectContext) throws {
+ func delete(_ lesson: CoreDataClassModel, in context: NSManagedObjectContext) throws {
if let existingClass = exists(lesson, in: context) {
context.delete(existingClass)
Task(priority: .background) {
@@ -76,3 +76,20 @@ extension EnvironmentValues {
return ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1"
}
}
+
+extension ClassProvider {
+ func exists(_ jsonClass: JsonClassModel, in context: NSManagedObjectContext) -> JsonClassModel? {
+ try? context.existingObject(with: jsonClass.objectID) as? JsonClassModel
+ }
+
+ func delete(_ jsonClass: JsonClassModel, in context: NSManagedObjectContext) throws {
+ if let existingJsonClass = exists(jsonClass, in: context) {
+ context.delete(existingJsonClass)
+ Task(priority: .background) {
+ try await context.perform {
+ try context.save()
+ }
+ }
+ }
+ }
+}
diff --git a/Schedule ICTIS/Schedule_ICTISApp.swift b/Schedule ICTIS/Schedule_ICTISApp.swift
index 3c3000d..9630afc 100644
--- a/Schedule ICTIS/Schedule_ICTISApp.swift
+++ b/Schedule ICTIS/Schedule_ICTISApp.swift
@@ -9,10 +9,16 @@ import SwiftUI
@main
struct Schedule_ICTISApp: App {
+ @StateObject private var networkMonitor = NetworkMonitor()
+ @StateObject var vm = ScheduleViewModel()
+ var provider = ClassProvider.shared
var body: some Scene {
WindowGroup {
- ContentView()
+ ContentView(vm: vm, networkMonitor: networkMonitor)
.environment(\.managedObjectContext, ClassProvider.shared.viewContext)
+ .onAppear {
+ vm.fillDictForVm()
+ }
}
}
}
diff --git a/Schedule ICTIS/Settings/ListOfGroupsView.swift b/Schedule ICTIS/Settings/ListOfGroupsView.swift
index d6ae7aa..debdc28 100644
--- a/Schedule ICTIS/Settings/ListOfGroupsView.swift
+++ b/Schedule ICTIS/Settings/ListOfGroupsView.swift
@@ -44,6 +44,7 @@ struct ListOfGroupsView: View {
}
vm.nameToHtml[item.name] = ""
vm.fetchWeekSchedule()
+ vm.updateFilteringGroups()
dismiss()
}
}
diff --git a/Schedule ICTIS/Settings/SelectingGroupView.swift b/Schedule ICTIS/Settings/SelectingGroupView.swift
index 9064525..55b0308 100644
--- a/Schedule ICTIS/Settings/SelectingGroupView.swift
+++ b/Schedule ICTIS/Settings/SelectingGroupView.swift
@@ -49,17 +49,17 @@ struct SelectingGroupView: View {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
if vm.errorInNetwork == .noError {
vm.errorInNetwork = nil
+ text = transformStringToFormat(text)
if firstFavGroup == "" {
UserDefaults.standard.set(text, forKey: "group")
- vm.nameToHtml[text] = ""
} else if secondFavGroup == "" {
UserDefaults.standard.set(text, forKey: "group2")
- vm.nameToHtml[text] = ""
} else {
UserDefaults.standard.set(text, forKey: "group3")
- vm.nameToHtml[text] = ""
}
+ vm.nameToHtml[text] = ""
vm.fetchWeekSchedule()
+ vm.updateFilteringGroups()
self.isLoading = false
self.text = ""
dismiss()
@@ -126,6 +126,7 @@ struct SelectingGroupView: View {
UserDefaults.standard.set(item.name, forKey: "group3")
vm.nameToHtml[item.name] = ""
}
+ vm.updateFilteringGroups()
vm.fetchWeekSchedule()
dismiss()
}
diff --git a/Schedule ICTIS/Settings/SelectingVPKView.swift b/Schedule ICTIS/Settings/SelectingVPKView.swift
index 18fbbc2..7834406 100644
--- a/Schedule ICTIS/Settings/SelectingVPKView.swift
+++ b/Schedule ICTIS/Settings/SelectingVPKView.swift
@@ -56,7 +56,9 @@ struct SelectingVPKView: View {
} else {
UserDefaults.standard.set(text, forKey: "vpk3")
}
+ text = transformStringToFormat(text)
vm.nameToHtml[text] = ""
+ vm.updateFilteringGroups()
vm.fetchWeekSchedule()
self.isLoading = false
self.text = ""
diff --git a/Schedule ICTIS/TabBar/TabBarView.swift b/Schedule ICTIS/TabBar/TabBarView.swift
index f583201..c47a758 100644
--- a/Schedule ICTIS/TabBar/TabBarView.swift
+++ b/Schedule ICTIS/TabBar/TabBarView.swift
@@ -58,7 +58,3 @@ struct TabBarView: View {
}
}
}
-
-#Preview {
- ContentView()
-}
diff --git a/Schedule ICTIS/Utilities/Extensions/Date+Extensions.swift b/Schedule ICTIS/Utilities/Extensions/Date+Extensions.swift
index 135fd62..824689a 100644
--- a/Schedule ICTIS/Utilities/Extensions/Date+Extensions.swift
+++ b/Schedule ICTIS/Utilities/Extensions/Date+Extensions.swift
@@ -87,9 +87,11 @@ extension Date {
func createPreviousMonth() -> [MonthWeek] {
let calendar = Calendar.current
let startOfFirstDate = calendar.startOfDay(for: self)
- guard let previousDate = calendar.date(byAdding: .month, value: -1, to: startOfFirstDate) else {
+ guard let previousDate = calendar.date(byAdding: .weekOfMonth, value: -5, to: startOfFirstDate) else {
return []
}
+ print("Start of first date \(startOfFirstDate)")
+ print("Previous date \(previousDate)")
return fetchMonth(previousDate)
}
diff --git a/Schedule ICTIS/Utilities/Extensions/View+Extensions.swift b/Schedule ICTIS/Utilities/Extensions/View+Extensions.swift
index 13d64de..441bc86 100644
--- a/Schedule ICTIS/Utilities/Extensions/View+Extensions.swift
+++ b/Schedule ICTIS/Utilities/Extensions/View+Extensions.swift
@@ -8,6 +8,36 @@
import SwiftUI
extension View {
+
+ func transformStringToFormat(_ input: String) -> String {
+ var result = input
+
+ // Условие 1: начинается с "кт"
+ if result.lowercased().hasPrefix("кт") {
+ result = result.lowercased()
+ let firstTwo = String(result.prefix(2)).uppercased()
+ let rest = String(result.dropFirst(2))
+ result = firstTwo + rest
+ return result
+ }
+
+ // Условие 2: содержит "впк"
+ if result.lowercased().contains("впк") {
+ result = result.lowercased()
+ result = result.replacingOccurrences(of: "впк", with: "ВПК")
+ return result
+ }
+
+ // Условие 3: содержит "мвпк"
+ if result.lowercased().contains("мвпк") {
+ result = result.lowercased()
+ result = result.replacingOccurrences(of: "впк", with: "ВПК")
+ return result
+ }
+
+ return result
+ }
+
func isSameDate(_ date1: Date, _ date2: Date) -> Bool {
return Calendar.current.isDate(date1, inSameDayAs: date2)
}
@@ -270,13 +300,17 @@ extension MonthTabView {
let currentMonth = Date().fetchMonth(vm.selectedDay)
if let firstDate = currentMonth.first?.week[0].date {
- monthSlider.append(firstDate.createPreviousMonth())
+ let temp = firstDate.createPreviousMonth()
+ print("First date - \(firstDate)")
+ print(temp)
+ monthSlider.append(temp)
}
monthSlider.append(currentMonth)
if let lastDate = currentMonth.last?.week[6].date {
- monthSlider.append(lastDate.createNextMonth())
+ let temp = lastDate.createNextMonth()
+ monthSlider.append(temp)
}
}
}
diff --git a/Schedule ICTIS/Utilities/Network/NetworkError.swift b/Schedule ICTIS/Utilities/Network/NetworkError.swift
index 804dad0..bfaba72 100644
--- a/Schedule ICTIS/Utilities/Network/NetworkError.swift
+++ b/Schedule ICTIS/Utilities/Network/NetworkError.swift
@@ -11,8 +11,8 @@ enum NetworkError: String, Error, LocalizedError {
case invalidUrl
case invalidResponse
case invalidData
- case noNetwork
case noError
+ case timeout
var errorDescription: String? {
switch self {
@@ -22,8 +22,8 @@ enum NetworkError: String, Error, LocalizedError {
"InvalidResponse"
case .invalidData:
"Проверьте номер группы"
- case .noNetwork:
- "No network connection"
+ case .timeout:
+ "Ошибка сети"
case .noError:
"Нет ошибки"
}
@@ -37,8 +37,8 @@ enum NetworkError: String, Error, LocalizedError {
"Для этой недели расписания еще нет"
case .invalidData:
"Похоже такой группы не существует"
- case .noNetwork:
- "Проверьте подключение к интернету и попробуйте заново"
+ case .timeout:
+ "Проверьте соединение с интернетом"
case .noError:
"Ошибки нет"
}
diff --git a/Schedule ICTIS/Utilities/Network/NetworkManager.swift b/Schedule ICTIS/Utilities/Network/NetworkManager.swift
index cdb69c1..ff4ad9c 100644
--- a/Schedule ICTIS/Utilities/Network/NetworkManager.swift
+++ b/Schedule ICTIS/Utilities/Network/NetworkManager.swift
@@ -14,9 +14,14 @@ final class NetworkManager {
private let decoder = JSONDecoder()
private let urlForGroup = "https://webictis.sfedu.ru/schedule-api/?query="
private let urlForWeek = "https://webictis.sfedu.ru/schedule-api/?group="
+ private let customSession: URLSession // Кастомная сессия для ограничения времени ответа от сервера
//MARK: Initializer
private init() {
+ let configuration = URLSessionConfiguration.default
+ configuration.timeoutIntervalForRequest = 3 // Таймаут запроса 10 секунд
+ configuration.timeoutIntervalForResource = 3 // Таймаут ресурса 15 секунд
+ self.customSession = URLSession(configuration: configuration)
decoder.dateDecodingStrategy = .iso8601
}
@@ -32,7 +37,7 @@ final class NetworkManager {
func getSchedule(_ group: String) async throws -> Schedule {
let newUrlForGroup = makeUrlForGroup(group)
guard let url = URL(string: newUrlForGroup) else { throw NetworkError.invalidUrl }
- let (data, response) = try await URLSession.shared.data(from: url)
+ let (data, response) = try await customSession.data(from: url)
guard let response = response as? HTTPURLResponse, response.statusCode == 200 else { throw NetworkError.invalidResponse }
do {
@@ -47,7 +52,7 @@ final class NetworkManager {
let newUrlForWeek = makeUrlForWeek(numOfWeek, htmlNameOfGroup)
print(newUrlForWeek)
guard let url = URL(string: newUrlForWeek) else { throw NetworkError.invalidUrl }
- let (data, response) = try await URLSession.shared.data(from: url)
+ let (data, response) = try await customSession.data(from: url)
guard let response = response as? HTTPURLResponse, response.statusCode == 200 else { throw NetworkError.invalidResponse }
do {
@@ -61,7 +66,7 @@ final class NetworkManager {
func getGroups(group: String) async throws -> Welcome {
let newUrlForGroups = makeUrlForGroup(group)
guard let url = URL(string: newUrlForGroups) else { throw NetworkError.invalidUrl }
- let (data, response) = try await URLSession.shared.data(from: url)
+ let (data, response) = try await customSession.data(from: url)
guard let response = response as? HTTPURLResponse, response.statusCode == 200 else { throw NetworkError.invalidResponse }
do {
diff --git a/Schedule ICTIS/Utilities/Network/NetworkMonitor.swift b/Schedule ICTIS/Utilities/Network/NetworkMonitor.swift
new file mode 100644
index 0000000..2ca2ba3
--- /dev/null
+++ b/Schedule ICTIS/Utilities/Network/NetworkMonitor.swift
@@ -0,0 +1,37 @@
+//
+// NetworkMonitor.swift
+// Schedule ICTIS
+//
+// Created by Mironov Egor on 27.03.2025.
+//
+
+import Network
+import SwiftUI
+
+class NetworkMonitor: ObservableObject {
+ @Published var isConnected: Bool = false
+ private let monitor = NWPathMonitor()
+ private let queue = DispatchQueue(label: "NetworkMonitorQueue")
+
+ init() {
+ startMonitoring()
+ }
+
+ func startMonitoring() {
+ monitor.pathUpdateHandler = { [weak self] path in
+ DispatchQueue.main.async {
+ self?.isConnected = path.status == .satisfied
+ print(self?.isConnected == true ? "✅ Интернет подключен!" : "❌ Нет подключения к интернету.")
+ }
+ }
+ monitor.start(queue: queue)
+ }
+
+ func stopMonitoring() {
+ monitor.cancel()
+ }
+
+ deinit {
+ stopMonitoring()
+ }
+}
diff --git a/Schedule ICTIS/ViewModel/EditClassViewModel.swift b/Schedule ICTIS/ViewModel/EditClassViewModel.swift
index 1706c1f..0b28d46 100644
--- a/Schedule ICTIS/ViewModel/EditClassViewModel.swift
+++ b/Schedule ICTIS/ViewModel/EditClassViewModel.swift
@@ -2,14 +2,14 @@
// EditClassViewModel.swift
// Schedule ICTIS
//
-// Created by G412 on 18.12.2024.
+// Created by Egor Mironov on 18.12.2024.
//
import Foundation
import CoreData
final class EditClassViewModel: ObservableObject {
- @Published var _class: ClassModel
+ @Published var _class: CoreDataClassModel
let isNew: Bool
@@ -17,7 +17,7 @@ final class EditClassViewModel: ObservableObject {
private let context: NSManagedObjectContext
- init(provider: ClassProvider, _class: ClassModel? = nil) {
+ init(provider: ClassProvider, _class: CoreDataClassModel? = nil) {
self.provider = provider
self.context = provider.newContext
@@ -27,7 +27,7 @@ final class EditClassViewModel: ObservableObject {
self.isNew = false
}
else {
- self._class = ClassModel(context: self.context)
+ self._class = CoreDataClassModel(context: self.context)
self.isNew = true
}
}
diff --git a/Schedule ICTIS/ViewModel/SaveScheduleViewModel.swift b/Schedule ICTIS/ViewModel/SaveScheduleViewModel.swift
new file mode 100644
index 0000000..50d1dd6
--- /dev/null
+++ b/Schedule ICTIS/ViewModel/SaveScheduleViewModel.swift
@@ -0,0 +1,35 @@
+//
+// SaveScheduleViewModel.swift
+// Schedule ICTIS
+//
+// Created by Egor Mironov on 02.04.2025.
+//
+
+import Foundation
+import CoreData
+
+final class SaveScheduleViewModel: ObservableObject {
+ @Published var subject: JsonClassModel
+
+
+ private let provider: ClassProvider
+
+ private let context: NSManagedObjectContext
+
+ init(provider: ClassProvider, subject: JsonClassModel? = nil) {
+ self.provider = provider
+ self.context = provider.newContext
+
+ if let subject,
+ let existingClassCopy = provider.exists(subject, in: context) {
+ self.subject = existingClassCopy
+ }
+ else {
+ self.subject = JsonClassModel(context: self.context)
+ }
+ }
+
+ func save() throws {
+ try provider.persist(in: context)
+ }
+}
diff --git a/Schedule ICTIS/ViewModel/ScheduleViewModel.swift b/Schedule ICTIS/ViewModel/ScheduleViewModel.swift
index 277ae01..58f5190 100644
--- a/Schedule ICTIS/ViewModel/ScheduleViewModel.swift
+++ b/Schedule ICTIS/ViewModel/ScheduleViewModel.swift
@@ -14,6 +14,8 @@ final class ScheduleViewModel: ObservableObject {
@Published var nameToHtml: [String : String] = [:]
@Published var classesGroups: [[ClassInfo]] = []
@Published var searchingGroup = ""
+ @Published var filteringGroups: [String] = ["Все"]
+ @Published var showOnlyChoosenGroup: String = "Все"
//Schedule
@Published var weekScheduleGroup: Table = Table(
@@ -25,7 +27,7 @@ final class ScheduleViewModel: ObservableObject {
link: ""
)
@Published var selectedDay: Date = .init()
- @Published var selectedIndex: Int = 1
+ @Published var selectedIndex: Int = 0
@Published var week: Int = 0
@Published var isFirstStartOffApp = true
@@ -92,7 +94,10 @@ final class ScheduleViewModel: ObservableObject {
// Сортируем по времени
self.sortClassesByTime()
} catch {
- if let error = error as? NetworkError {
+ if let urlError = error as? URLError, urlError.code == .timedOut {
+ errorInNetwork = .timeout
+ print("Ошибка: превышено время ожидания ответа от сервера")
+ } else if let error = error as? NetworkError {
switch error {
case .invalidResponse:
errorInNetwork = .invalidResponse
@@ -102,9 +107,9 @@ final class ScheduleViewModel: ObservableObject {
default:
print("Неизвестная ошибка: \(error)")
}
- isLoading = false
print("Есть ошибка: \(error)")
}
+ isLoading = false
}
}
}
@@ -172,4 +177,37 @@ final class ScheduleViewModel: ObservableObject {
}
}
}
+
+ func updateFilteringGroups() {
+ self.filteringGroups = ["Все"]
+ let keys = self.nameToHtml.keys
+ self.filteringGroups.append(contentsOf: keys)
+ }
+
+ func fillDictForVm() {
+ let group1 = UserDefaults.standard.string(forKey: "group")
+ let group2 = UserDefaults.standard.string(forKey: "group2")
+ let group3 = UserDefaults.standard.string(forKey: "group3")
+ let vpk1 = UserDefaults.standard.string(forKey: "vpk1")
+ let vpk2 = UserDefaults.standard.string(forKey: "vpk2")
+ let vpk3 = UserDefaults.standard.string(forKey: "vpk3")
+ if let nameGroup1 = group1, nameGroup1 != "" {
+ nameToHtml[nameGroup1] = ""
+ }
+ if let nameGroup2 = group2, nameGroup2 != "" {
+ nameToHtml[nameGroup2] = ""
+ }
+ if let nameGroup3 = group3, nameGroup3 != "" {
+ nameToHtml[nameGroup3] = ""
+ }
+ if let nameVpk1 = vpk1, nameVpk1 != "" {
+ nameToHtml[nameVpk1] = ""
+ }
+ if let nameVpk2 = vpk2, nameVpk2 != "" {
+ nameToHtml[nameVpk2] = ""
+ }
+ if let nameVpk3 = vpk3, nameVpk3 != "" {
+ nameToHtml[nameVpk3] = ""
+ }
+ }
}
diff --git a/Schedule ICTIS/ViewModel/SearchGroupsViewModel.swift b/Schedule ICTIS/ViewModel/SearchGroupsViewModel.swift
index 16b3e53..b5669e0 100644
--- a/Schedule ICTIS/ViewModel/SearchGroupsViewModel.swift
+++ b/Schedule ICTIS/ViewModel/SearchGroupsViewModel.swift
@@ -9,7 +9,7 @@ import Foundation
@MainActor
final class SearchGroupsViewModel: ObservableObject {
- @Published var groups: [Choice] = []
+ @Published var groups: [Subject] = []
func fetchGroups(group: String) {
Task {