diff --git a/SiriusGamesLiveActivity/Assets.xcassets/AccentColor.colorset/Contents.json b/SiriusGamesLiveActivity/Assets.xcassets/AccentColor.colorset/Contents.json
new file mode 100644
index 0000000..eb87897
--- /dev/null
+++ b/SiriusGamesLiveActivity/Assets.xcassets/AccentColor.colorset/Contents.json
@@ -0,0 +1,11 @@
+{
+ "colors" : [
+ {
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/SiriusGamesLiveActivity/Assets.xcassets/AppIcon.appiconset/Contents.json b/SiriusGamesLiveActivity/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..2305880
--- /dev/null
+++ b/SiriusGamesLiveActivity/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,35 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "platform" : "ios",
+ "size" : "1024x1024"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "idiom" : "universal",
+ "platform" : "ios",
+ "size" : "1024x1024"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "tinted"
+ }
+ ],
+ "idiom" : "universal",
+ "platform" : "ios",
+ "size" : "1024x1024"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/SiriusGamesLiveActivity/Assets.xcassets/Contents.json b/SiriusGamesLiveActivity/Assets.xcassets/Contents.json
new file mode 100644
index 0000000..73c0059
--- /dev/null
+++ b/SiriusGamesLiveActivity/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/SiriusGamesLiveActivity/Assets.xcassets/WidgetBackground.colorset/Contents.json b/SiriusGamesLiveActivity/Assets.xcassets/WidgetBackground.colorset/Contents.json
new file mode 100644
index 0000000..eb87897
--- /dev/null
+++ b/SiriusGamesLiveActivity/Assets.xcassets/WidgetBackground.colorset/Contents.json
@@ -0,0 +1,11 @@
+{
+ "colors" : [
+ {
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/SiriusGamesLiveActivity/Info.plist b/SiriusGamesLiveActivity/Info.plist
new file mode 100644
index 0000000..0f118fb
--- /dev/null
+++ b/SiriusGamesLiveActivity/Info.plist
@@ -0,0 +1,11 @@
+
+
+
+
+ NSExtension
+
+ NSExtensionPointIdentifier
+ com.apple.widgetkit-extension
+
+
+
diff --git a/SiriusGamesLiveActivity/SiriusGamesLiveActivity.swift b/SiriusGamesLiveActivity/SiriusGamesLiveActivity.swift
new file mode 100644
index 0000000..ea45940
--- /dev/null
+++ b/SiriusGamesLiveActivity/SiriusGamesLiveActivity.swift
@@ -0,0 +1,63 @@
+//
+// SiriusGamesLiveActivity.swift
+// SiriusGamesLiveActivity
+//
+// Created by Илья Лебедев on 04.04.2025.
+//
+import ActivityKit
+import SwiftUI
+import WidgetKit
+
+@main
+struct SportsActivityWidget: Widget {
+ var body: some WidgetConfiguration {
+ ActivityConfiguration(for: EventAttributes.self) { context in
+ if let eventState = EventState(rawValue: context.state.status), let nextEventSutus = EventState(rawValue: context.state.nextEventSatus) {
+ LockScreenActivityView(
+ eventName: context.state.eventName,
+ currentEventState: eventState,
+ status: context.state.status,
+ nextEventName: context.state.nextEventName,
+ nextEventStatus: nextEventSutus,
+ score: context.state.score
+ )
+ }
+ } dynamicIsland: { context in
+ DynamicIsland {
+ DynamicIslandExpandedRegion(.leading) {
+ if let eventState = EventState(rawValue: context.state.status) {
+ HStack {
+ Circle()
+ .foregroundStyle(eventState.getColor())
+ .frame(width: 10)
+ Text("\(NSLocalizedString(context.state.status, comment: "status"))")
+ }
+ }
+ Text("\(context.state.eventName)")
+ }
+ DynamicIslandExpandedRegion(.center) {
+ Text(NSLocalizedString("score", comment: "score") + ": \(context.state.score)")
+ .bold()
+ }
+ DynamicIslandExpandedRegion(.trailing) {
+ if let eventState = EventState(rawValue: context.state.nextEventSatus) {
+ HStack {
+ Circle()
+ .foregroundStyle(eventState.getColor())
+ .frame(width: 10)
+ Text("\(context.state.nextEventSatus)")
+ }
+ }
+ Text("\(context.state.nextEventName)")
+ }
+ } compactLeading: {
+ Text("\(context.state.eventName)")
+ } compactTrailing: {
+ Text("\(context.state.score)")
+ .bold()
+ } minimal: {
+ Text("")
+ }
+ }
+ }
+}
diff --git a/SiriusProject/SiriusProject/ContentView.swift b/SiriusProject/SiriusProject/ContentView.swift
index 8770c1b..ce79417 100644
--- a/SiriusProject/SiriusProject/ContentView.swift
+++ b/SiriusProject/SiriusProject/ContentView.swift
@@ -63,7 +63,7 @@ struct ContentView: View {
ContentView(
appViewModel: AppViewModel(
logging: printLogging,
- eventsListViewModel: EventsListViewModel(networkManager: FakeNetworkManager(logging: printLogging), logging: printLogging),
+ eventsListViewModel: EventsListViewModel(networkManager: FakeNetworkManager(logging: printLogging), liveActivtityManager: LiveActivityManager(), logging: printLogging),
settingsViewModel: SettingsViewModel(networkManager: FakeNetworkManager(logging: printLogging), logging: printLogging),
loginViewModel: LoginViewModel(networkManager: FakeNetworkManager(logging: printLogging), logging: { _ in }),
pointsViewModel: PointsViewModel(networkManager: FakeNetworkManager(logging: printLogging)),
diff --git a/SiriusProject/SiriusProject/Info.plist b/SiriusProject/SiriusProject/Info.plist
index 960075b..b20a1a9 100644
--- a/SiriusProject/SiriusProject/Info.plist
+++ b/SiriusProject/SiriusProject/Info.plist
@@ -34,5 +34,11 @@
NSAllowsArbitraryLoads
+ NSSupportsLiveActivities
+
+ NSSupportsLiveActivitiesFrequentUpdates
+
+ NSLiveActivityUsageDescription
+ Используется для отображения актуальной информации на экране блокировки
diff --git a/SiriusProject/SiriusProject/Managers/LiveActivityManager.swift b/SiriusProject/SiriusProject/Managers/LiveActivityManager.swift
new file mode 100644
index 0000000..e9f1d1c
--- /dev/null
+++ b/SiriusProject/SiriusProject/Managers/LiveActivityManager.swift
@@ -0,0 +1,43 @@
+//
+// LiveActivityManager.swift
+// SiriusProject
+//
+// Created by Илья Лебедев on 04.04.2025.
+//
+
+import ActivityKit
+import Foundation
+
+class LiveActivityManager {
+ private var activity: Activity?
+
+ func startActivity(currentEvent: Event?, nextEvent: Event?, score: Int) {
+ let attributes = EventAttributes()
+ let initialState = EventAttributes.ContentState(currentEvent: currentEvent, nextEvent: nextEvent, score: score)
+
+ do {
+ activity = try Activity.request(
+ attributes: attributes,
+ contentState: initialState,
+ pushType: nil
+ )
+ print("Live Activity запущена!")
+ } catch {
+ print("Ошибка запуска Live Activity: \(error.localizedDescription)")
+ }
+ }
+
+ func updateActivity(currentEvent: Event?, nextEvent: Event?, score: Int) {
+ Task {
+ let newState = EventAttributes.ContentState(currentEvent: currentEvent, nextEvent: nextEvent, score: score)
+ await activity?.update(using: newState)
+ }
+ }
+
+ func endActivity() {
+ Task {
+ await activity?.end(dismissalPolicy: .immediate)
+ activity = nil
+ }
+ }
+}
diff --git a/SiriusProject/SiriusProject/Model/EventAttributes.swift b/SiriusProject/SiriusProject/Model/EventAttributes.swift
new file mode 100644
index 0000000..feb6d47
--- /dev/null
+++ b/SiriusProject/SiriusProject/Model/EventAttributes.swift
@@ -0,0 +1,41 @@
+//
+// EventAttributes.swift
+// SiriusProject
+//
+// Created by Илья Лебедев on 04.04.2025.
+//
+
+import ActivityKit
+import Foundation
+
+struct EventAttributes: ActivityAttributes {
+ public typealias TimerStatus = ContentState
+
+ public struct ContentState: Codable, Hashable {
+ var eventName: String
+ var status: String
+ var nextEventName: String
+ var nextEventSatus: String
+ var score: Int
+
+ init(currentEvent: Event?, nextEvent: Event?, score: Int) {
+ if let currentEvent = currentEvent {
+ eventName = currentEvent.title
+ status = currentEvent.state.rawValue
+ } else {
+ eventName = "finish"
+ status = "finish"
+ }
+
+ if let nextEvent = nextEvent {
+ nextEventName = nextEvent.title
+ nextEventSatus = nextEvent.state.rawValue
+ } else {
+ nextEventName = "finish"
+ nextEventSatus = "finish"
+ }
+
+ self.score = score
+ }
+ }
+}
diff --git a/SiriusProject/SiriusProject/SiriusProjectApp.swift b/SiriusProject/SiriusProject/SiriusProjectApp.swift
index df71a00..b743b12 100644
--- a/SiriusProject/SiriusProject/SiriusProjectApp.swift
+++ b/SiriusProject/SiriusProject/SiriusProjectApp.swift
@@ -12,6 +12,7 @@ import UserNotifications
struct SiriusProjectApp: App {
let networkManager: NetworkManagerProtocol
var notificationsManager: NotificationsManager
+ let liveAcivityManager: LiveActivityManager
var appViewModel: AppViewModel
let logging: Logging
@@ -32,13 +33,15 @@ struct SiriusProjectApp: App {
errorLogging = errorPublisherLogging(errorPublisher)
- networkManager = /* FakeNetworkManager(logging: printLogging) */
+ networkManager =
NetworkManager(service: APIService(urlSession: URLSession.shared), logging: errorLogging)
notificationsManager = NotificationsManager()
+ liveAcivityManager = LiveActivityManager()
+
appViewModel = AppViewModel(
logging: logging,
- eventsListViewModel: EventsListViewModel(networkManager: networkManager, logging: logging),
+ eventsListViewModel: EventsListViewModel(networkManager: networkManager, liveActivtityManager: LiveActivityManager(), logging: logging),
settingsViewModel: SettingsViewModel(networkManager: networkManager, logging: logging),
loginViewModel: LoginViewModel(networkManager: networkManager, logging: logging),
pointsViewModel: PointsViewModel(networkManager: networkManager),
diff --git a/SiriusProject/SiriusProject/Utilities/SportIconProvider.swift b/SiriusProject/SiriusProject/Utilities/SportIconProvider.swift
index af54ef9..d3c66ae 100644
--- a/SiriusProject/SiriusProject/Utilities/SportIconProvider.swift
+++ b/SiriusProject/SiriusProject/Utilities/SportIconProvider.swift
@@ -42,6 +42,7 @@ enum SportIconProvider {
case "шахматы": return "rectangle.pattern.checkered"
case "регби": return "figure.rugby"
case "скалолазание": return "figure.climbing"
+ case "": return "flag.pattern.checkered"
default: return "trophy.fill"
}
}
diff --git a/SiriusProject/SiriusProject/View/EventsListView.swift b/SiriusProject/SiriusProject/View/EventsListView.swift
index 679c7cf..8765c7f 100644
--- a/SiriusProject/SiriusProject/View/EventsListView.swift
+++ b/SiriusProject/SiriusProject/View/EventsListView.swift
@@ -49,11 +49,13 @@ struct EventsListView: View {
.onAppear {
print("Try get events on appear")
viewModel.fetchEvents()
+ viewModel.updateLiveActivity()
}
.onChange(of: isNeedUpdate) {
print("New fetch events")
DispatchQueue.global(qos: .userInitiated).asyncAfter(deadline: .now() + 0.5) {
viewModel.fetchEvents()
+ viewModel.updateLiveActivity()
}
}
.overlay(alignment: .bottomTrailing) {
@@ -70,7 +72,7 @@ struct EventsListView: View {
EventsListView(
appViewModel: AppViewModel(
logging: printLogging,
- eventsListViewModel: EventsListViewModel(networkManager: FakeNetworkManager(logging: printLogging), logging: printLogging),
+ eventsListViewModel: EventsListViewModel(networkManager: FakeNetworkManager(logging: printLogging), liveActivtityManager: LiveActivityManager(), logging: printLogging),
settingsViewModel: SettingsViewModel(networkManager: FakeNetworkManager(logging: printLogging), logging: printLogging),
loginViewModel: LoginViewModel(networkManager: FakeNetworkManager(logging: printLogging), logging: printLogging),
pointsViewModel: PointsViewModel(networkManager: FakeNetworkManager(logging: printLogging)),
@@ -79,7 +81,7 @@ struct EventsListView: View {
notificationsViewModel: NotificationsViewModel(networkManager: FakeNetworkManager(logging: printLogging), logging: printLogging), getRateReviewModel: GetRateReviewModel(networkManager: FakeNetworkManager(logging: printLogging), logging: printLogging),
createEventViewModel: CreateEventViewModel(networkManager: FakeNetworkManager(logging: printLogging), logging: printLogging), sendPushViewModel: SendPushViewModel(networkManager: FakeNetworkManager(logging: printLogging), logging: printLogging)
), eventsListViewModel: EventsListViewModel(
- networkManager: FakeNetworkManager(logging: printLogging),
+ networkManager: FakeNetworkManager(logging: printLogging), liveActivtityManager: LiveActivityManager(),
logging: printLogging
),
isNotificationViewShowing: .constant(false)
diff --git a/SiriusProject/SiriusProject/View/LockScreenActivityView.swift b/SiriusProject/SiriusProject/View/LockScreenActivityView.swift
new file mode 100644
index 0000000..08a9185
--- /dev/null
+++ b/SiriusProject/SiriusProject/View/LockScreenActivityView.swift
@@ -0,0 +1,66 @@
+//
+// LockScreenActivityView.swift
+// SiriusProject
+//
+// Created by Илья Лебедев on 04.04.2025.
+//
+
+import SwiftUI
+
+struct LockScreenActivityView: View {
+ @State var eventName: String = ""
+ @State var currentEventState: EventState = .now
+ @State var status: String = ""
+ @State var nextEventName: String = "Гольф"
+ @State var nextEventStatus: EventState = .next
+ @State var score: Int = 0
+ var body: some View {
+ VStack {
+ HStack {
+ Image(systemName: SportIconProvider.getSportIcon(for: eventName))
+ .font(.title)
+ if !eventName.isEmpty {
+ VStack(alignment: .leading) {
+ HStack {
+ Text(eventName)
+ .font(.headline)
+ .bold()
+ Circle()
+ .foregroundStyle(currentEventState.getColor())
+ .frame(width: 10)
+ Text("\(status)")
+ }
+
+ HStack {
+ Text(nextEventName)
+ .font(.footnote)
+ Circle()
+ .font(.footnote)
+ .foregroundStyle(nextEventStatus.getColor())
+ .frame(width: 7)
+ Text("\(nextEventStatus)")
+ .font(.footnote)
+ }
+ }
+ .padding(.horizontal)
+
+ } else {
+ Text("finishevents")
+ .font(.headline)
+ .padding(.horizontal)
+ }
+
+ Spacer()
+
+ Text("Score: \(score)")
+ .font(.headline)
+ .bold()
+ }
+ .padding(.horizontal)
+ }
+ }
+}
+
+#Preview {
+ LockScreenActivityView()
+}
diff --git a/SiriusProject/SiriusProject/ViewModel/EventsListViewModel.swift b/SiriusProject/SiriusProject/ViewModel/EventsListViewModel.swift
index 0ef7207..bb043df 100644
--- a/SiriusProject/SiriusProject/ViewModel/EventsListViewModel.swift
+++ b/SiriusProject/SiriusProject/ViewModel/EventsListViewModel.swift
@@ -10,14 +10,32 @@ import SwiftUI
final class EventsListViewModel: ObservableObject {
let networkManager: NetworkManagerProtocol
let logging: Logging
+ let liveActivtityManager: LiveActivityManager
+ var score: Int = 0
@AppStorage("teamID") var teamID: Int = 0
@Published var events: [Event] = []
- init(networkManager: NetworkManagerProtocol, logging: @escaping Logging) {
+ init(networkManager: NetworkManagerProtocol, liveActivtityManager: LiveActivityManager, logging: @escaping Logging) {
self.networkManager = networkManager
self.logging = logging
+ self.liveActivtityManager = liveActivtityManager
fetchEvents()
+ startLiveActivity()
+ }
+
+ private func startLiveActivity() {
+ let currentEvent = events.first(where: { $0.state == EventState.now })
+ let nextEvent = events.first(where: { $0.state == EventState.next })
+ fetchTeamScore()
+ liveActivtityManager.startActivity(currentEvent: currentEvent, nextEvent: nextEvent, score: score)
+ }
+
+ func updateLiveActivity() {
+ let currentEvent = events.first(where: { $0.state == EventState.now })
+ let nextEvent = events.first(where: { $0.state == EventState.next })
+ fetchTeamScore()
+ liveActivtityManager.updateActivity(currentEvent: currentEvent, nextEvent: nextEvent, score: score)
}
func fetchEvents() {
@@ -31,4 +49,11 @@ final class EventsListViewModel: ObservableObject {
})
}
}
+
+ func fetchTeamScore() {
+ networkManager.getTeam(teamId: teamID, completion: { [weak self] team in
+ self?.score = team?.score ?? 0
+ })
+ print("score: \(score)")
+ }
}
diff --git a/SiriusProject/en.lproj/Localizable.strings b/SiriusProject/en.lproj/Localizable.strings
index 7971607..d41fdb6 100644
--- a/SiriusProject/en.lproj/Localizable.strings
+++ b/SiriusProject/en.lproj/Localizable.strings
@@ -62,4 +62,5 @@
"createevent" = "Create event";
"sendpush" = "Send push to all";
"sendpushbutton" = "Send push";
+"finishevents" = "Finish";
diff --git a/SiriusProject/ru.lproj/Localizable.strings b/SiriusProject/ru.lproj/Localizable.strings
index 1d68db6..a9f395c 100644
--- a/SiriusProject/ru.lproj/Localizable.strings
+++ b/SiriusProject/ru.lproj/Localizable.strings
@@ -62,3 +62,4 @@
"createevent" = "Добавить событие";
"sendpush" = "Уведомления для всех";
"sendpushbutton" = "Отправить";
+"finishevents" = "Игры завершены";