diff --git a/hearo/HearoadWatch Watch App/ContentView.swift b/hearo/HearoadWatch Watch App/ContentView.swift
index 04293a7..9306915 100644
--- a/hearo/HearoadWatch Watch App/ContentView.swift
+++ b/hearo/HearoadWatch Watch App/ContentView.swift
@@ -9,18 +9,54 @@ import SwiftUI
import WatchKit
struct ContentView: View {
- @ObservedObject private var sessionManager = WatchSessionManager.shared
-
+ @ObservedObject var sessionManager = WatchSessionManager.shared
+
var body: some View {
- VStack {
- Text("소리 감지: \(sessionManager.alertMessage)")
- .font(.headline)
- .foregroundColor(.red)
- .padding()
+ ZStack {
+ // 배경색을 alert 상태에 따라 변경
+ sessionManager.isAlerting ? Color.red.edgesIgnoringSafeArea(.all) : Color.black.edgesIgnoringSafeArea(.all)
- Button("진동 테스트") {
- WKInterfaceDevice.current().play(.notification) // 수동으로 진동 테스트
+ VStack {
+ // 상태에 따른 텍스트 표시
+ Text(sessionManager.isAlerting ? sessionManager.alertMessage : "인식중")
+ .foregroundColor(.white)
+ .font(.title)
+ .padding()
+
+
+ // 알림 아이콘을 표시하고, 알림이 아닐 때는 숨김 처리
+ if sessionManager.isAlerting {
+ Image(systemName: "exclamationmark.triangle.fill")
+ .resizable()
+ .frame(width: 50, height: 50)
+ .foregroundColor(.yellow)
+ .padding()
+ }
}
}
+ .onAppear {
+ // 초기화
+ sessionManager.resetAlert()
+ }
}
}
+//struct ContentView: View {
+// @ObservedObject var viewManager = WatchViewManager()
+//
+// var body: some View {
+// VStack {
+// if viewManager.currentView == "working" {
+// WatchWorkingView()
+// } else if viewManager.currentView == "warning" {
+// WatchWarningView()
+// } else if viewManager.currentView == "finish" {
+// WatchFinishView()
+// } else {
+// WatchHomeView()
+// }
+// }
+// .onAppear {
+// print("현재 Watch 뷰: \(viewManager.currentView)")
+// }
+// }
+//}
diff --git a/hearo/HearoadWatch Watch App/HearoadWatch.entitlements b/hearo/HearoadWatch Watch App/HearoadWatch.entitlements
new file mode 100644
index 0000000..3cd3197
--- /dev/null
+++ b/hearo/HearoadWatch Watch App/HearoadWatch.entitlements
@@ -0,0 +1,8 @@
+
+
+
+
+ com.apple.developer.watchkit.extended-runtime-session
+
+
+
diff --git a/hearo/HearoadWatch Watch App/WatchSessionManager.swift b/hearo/HearoadWatch Watch App/WatchSessionManager.swift
index 6a4ae66..6934e4b 100644
--- a/hearo/HearoadWatch Watch App/WatchSessionManager.swift
+++ b/hearo/HearoadWatch Watch App/WatchSessionManager.swift
@@ -8,27 +8,12 @@ import Foundation
import WatchKit
import WatchConnectivity
-class WatchSessionManager: NSObject, ObservableObject, WCSessionDelegate, WKExtendedRuntimeSessionDelegate {
- func extendedRuntimeSession(_ extendedRuntimeSession: WKExtendedRuntimeSession, didInvalidateWith reason: WKExtendedRuntimeSessionInvalidationReason, error: Error?) {
- switch reason {
- case .expired:
- print("세션이 만료되었습니다.")
- default:
- print("알 수 없는 이유로 세션이 만료되었습니다.")
- }
-
- if let error = error {
- print("세션 만료 오류: \(error.localizedDescription)")
- }
-
- // 세션 만료 후 백그라운드 세션을 다시 시작
- startBackgroundSession()
- }
+class WatchSessionManager: NSObject, ObservableObject, WCSessionDelegate {
static let shared = WatchSessionManager() // 싱글톤 인스턴스 생성
- @Published var alertMessage: String = "대기 중"
- private var backgroundSession: WKExtendedRuntimeSession?
-
+ @Published var alertMessage: String = "인식중" // 기본 메시지
+ @Published var isAlerting: Bool = false // 알림 상태 확인
+
private override init() {
super.init()
@@ -36,55 +21,51 @@ class WatchSessionManager: NSObject, ObservableObject, WCSessionDelegate, WKExte
WCSession.default.delegate = self
WCSession.default.activate()
}
-
- startBackgroundSession() // 백그라운드 세션 시작
}
-
- // 백그라운드 실행을 위한 세션 시작
- private func startBackgroundSession() {
- backgroundSession = WKExtendedRuntimeSession()
- backgroundSession?.delegate = self
- backgroundSession?.start()
- }
-
+
// iOS에서 경고 메시지를 수신하는 메서드
func session(_ session: WCSession, didReceiveMessage message: [String : Any]) {
if let alert = message["alert"] as? String {
DispatchQueue.main.async {
- print("watch alarm 감지:", alert)
- self.alertMessage = alert
- WKInterfaceDevice.current().play(.notification) // 진동 알림 발생
- self.showAlert(alertMessage: alert)
+ self.showAlert(with: alert)
}
}
}
-
- // 알림 팝업을 표시하는 메서드
- private func showAlert(alertMessage: String) {
- let action = WKAlertAction(title: "확인", style: .default) { }
+
+ // 3초 동안 알림 표시 후 기본 상태로 복구
+ func showAlert(with message: String) {
+ alertMessage = message
+ isAlerting = true
- // 현재 활성화된 인터페이스 컨트롤러에서 경고를 표시
- if let controller = WKExtension.shared().visibleInterfaceController {
- controller.presentAlert(withTitle: "경고", message: alertMessage, preferredStyle: .alert, actions: [action])
+ // 강한 진동 알림 발생
+ playUrgentHapticPattern()
+
+ // 3초 후에 기본 상태로 복구
+ DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
+ self.resetAlert()
}
}
- // WKExtendedRuntimeSessionDelegate - 세션 시작
- func extendedRuntimeSessionDidStart(_ extendedRuntimeSession: WKExtendedRuntimeSession) {
- print("배경 세션 시작됨")
+ // 긴급 상황을 위한 강한 진동 패턴
+ func playUrgentHapticPattern() {
+ // 반복 횟수 및 간격 설정
+ let repeatCount = 5 // 진동 반복 횟수
+ let interval: TimeInterval = 0.1 // 반복 간격 (0.1초)
+
+ // 반복적으로 강한 진동을 재생하는 패턴
+ for i in 0..
-
- UIBackgroundModes
-
- audio
- remote-notification
-
- WKBackgroundModes
-
- App downloads content from the network
-
-
+
diff --git a/hearo/hearo.xcodeproj/project.pbxproj b/hearo/hearo.xcodeproj/project.pbxproj
index 6b39af7..206cd6c 100644
--- a/hearo/hearo.xcodeproj/project.pbxproj
+++ b/hearo/hearo.xcodeproj/project.pbxproj
@@ -26,18 +26,22 @@
001C9F752CCF7984004EC4D2 /* HearoadWatch_Watch_AppUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 001C9F742CCF7984004EC4D2 /* HearoadWatch_Watch_AppUITestsLaunchTests.swift */; };
001C9F782CCF7984004EC4D2 /* HearoadWatch Watch App.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = 001C9F552CCF7983004EC4D2 /* HearoadWatch Watch App.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
001C9F842CCF81A1004EC4D2 /* WatchSessionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 001C9F832CCF81A1004EC4D2 /* WatchSessionManager.swift */; };
+ 001D669B2CDB3E11005746AC /* Haptic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 001D669A2CDB3E11005746AC /* Haptic.swift */; };
0045C6B82CC0295F009B4261 /* WarningView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0045C6B72CC0295F009B4261 /* WarningView.swift */; };
0045C6BA2CC02969009B4261 /* WarningViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0045C6B92CC02969009B4261 /* WarningViewModel.swift */; };
007BEFCE2CB159AD00C0E9FD /* WorkingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 007BEFCD2CB159AD00C0E9FD /* WorkingView.swift */; };
007BEFD02CB159BF00C0E9FD /* WorkingViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 007BEFCF2CB159BF00C0E9FD /* WorkingViewModel.swift */; };
00A04EA72CB7CBB20031DECC /* SpoqaHanSansNeo_OTF_original in Resources */ = {isa = PBXBuildFile; fileRef = 00A04EA62CB7CBB20031DECC /* SpoqaHanSansNeo_OTF_original */; };
+
112177742CC283BC000A146F /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 112177732CC283BC000A146F /* WidgetKit.framework */; };
112177762CC283BC000A146F /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 112177752CC283BC000A146F /* SwiftUI.framework */; };
112177792CC283BC000A146F /* LiveActivityBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 112177782CC283BC000A146F /* LiveActivityBundle.swift */; };
1121777B2CC283BC000A146F /* LiveActivityLiveActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1121777A2CC283BC000A146F /* LiveActivityLiveActivity.swift */; };
112177812CC283BD000A146F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 112177802CC283BD000A146F /* Assets.xcassets */; };
112177852CC283BD000A146F /* LiveActivityExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 112177712CC283BC000A146F /* LiveActivityExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
+
1159334C2CDB2A7C005902F2 /* HornSoundClassifier_V11.mlmodel in Sources */ = {isa = PBXBuildFile; fileRef = 1159334B2CDB2A7C005902F2 /* HornSoundClassifier_V11.mlmodel */; };
+
64F73A252CBAC9F100D2A140 /* HornSoundDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64F73A242CBAC9F100D2A140 /* HornSoundDetector.swift */; };
64F73A272CBAD1C000D2A140 /* SoundDetectorViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64F73A262CBAD1C000D2A140 /* SoundDetectorViewModel.swift */; };
/* End PBXBuildFile section */
@@ -138,12 +142,14 @@
001C9F742CCF7984004EC4D2 /* HearoadWatch_Watch_AppUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HearoadWatch_Watch_AppUITestsLaunchTests.swift; sourceTree = ""; };
001C9F832CCF81A1004EC4D2 /* WatchSessionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchSessionManager.swift; sourceTree = ""; };
001C9F852CD0CA11004EC4D2 /* HearoadWatch-Watch-App-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "HearoadWatch-Watch-App-Info.plist"; sourceTree = SOURCE_ROOT; };
+ 001D669A2CDB3E11005746AC /* Haptic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Haptic.swift; sourceTree = ""; };
0045C6B72CC0295F009B4261 /* WarningView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WarningView.swift; sourceTree = ""; };
0045C6B92CC02969009B4261 /* WarningViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WarningViewModel.swift; sourceTree = ""; };
007BEFCD2CB159AD00C0E9FD /* WorkingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkingView.swift; sourceTree = ""; };
007BEFCF2CB159BF00C0E9FD /* WorkingViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkingViewModel.swift; sourceTree = ""; };
00A04EA62CB7CBB20031DECC /* SpoqaHanSansNeo_OTF_original */ = {isa = PBXFileReference; lastKnownFileType = folder; path = SpoqaHanSansNeo_OTF_original; sourceTree = ""; };
00A04EA82CB7CC320031DECC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; };
+
112177712CC283BC000A146F /* LiveActivityExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = LiveActivityExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
112177732CC283BC000A146F /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; };
112177752CC283BC000A146F /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; };
@@ -151,7 +157,9 @@
1121777A2CC283BC000A146F /* LiveActivityLiveActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveActivityLiveActivity.swift; sourceTree = ""; };
112177802CC283BD000A146F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
112177822CC283BD000A146F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+
1159334B2CDB2A7C005902F2 /* HornSoundClassifier_V11.mlmodel */ = {isa = PBXFileReference; lastKnownFileType = file.mlmodel; path = HornSoundClassifier_V11.mlmodel; sourceTree = ""; };
+
64F73A242CBAC9F100D2A140 /* HornSoundDetector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = HornSoundDetector.swift; path = hearo/Sources/Presentations/Working/ViewModel/HornSoundDetector.swift; sourceTree = SOURCE_ROOT; };
64F73A262CBAD1C000D2A140 /* SoundDetectorViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SoundDetectorViewModel.swift; path = hearo/Sources/Presentations/Working/ViewModel/SoundDetectorViewModel.swift; sourceTree = SOURCE_ROOT; };
/* End PBXFileReference section */
@@ -215,6 +223,11 @@
isa = PBXGroup;
children = (
00078BE62CAFC79E00FB3F70 /* OnboardingView.swift */,
+ 645C1B152CCBB3BF00251157 /* OnboardingWelcomeView.swift */,
+ 645C1B172CCBB53C00251157 /* OnboardingNotiPermissionView.swift */,
+ 645C1B192CCBBB4800251157 /* OnboardingPrivacyView.swift */,
+ 645C1B1B2CCBBE7000251157 /* OnboardingWarningView.swift */,
+ 645C1B1D2CCBBFAB00251157 /* OnboardingStandRecommendView.swift */,
);
path = View;
sourceTree = "";
@@ -335,22 +348,11 @@
isa = PBXGroup;
children = (
00A04EA32CB7CA510031DECC /* Helper */,
- 00A04EA22CB7CA320031DECC /* Enums */,
- 001C6FAC2CAFC44900B03767 /* Delegate */,
- 001C6FAF2CAFC46200B03767 /* Extensions */,
- 001C6FAE2CAFC45900B03767 /* Data */,
001C6FAD2CAFC45100B03767 /* Presentations */,
);
path = Sources;
sourceTree = "";
};
- 001C6FAC2CAFC44900B03767 /* Delegate */ = {
- isa = PBXGroup;
- children = (
- );
- path = Delegate;
- sourceTree = "";
- };
001C6FAD2CAFC45100B03767 /* Presentations */ = {
isa = PBXGroup;
children = (
@@ -364,20 +366,6 @@
path = Presentations;
sourceTree = "";
};
- 001C6FAE2CAFC45900B03767 /* Data */ = {
- isa = PBXGroup;
- children = (
- );
- path = Data;
- sourceTree = "";
- };
- 001C6FAF2CAFC46200B03767 /* Extensions */ = {
- isa = PBXGroup;
- children = (
- );
- path = Extensions;
- sourceTree = "";
- };
001C6FB32CAFC4DF00B03767 /* Home */ = {
isa = PBXGroup;
children = (
@@ -416,12 +404,13 @@
001C9F562CCF7983004EC4D2 /* HearoadWatch Watch App */ = {
isa = PBXGroup;
children = (
+ 001C9F5D2CCF7984004EC4D2 /* Preview Content */,
+ 00AD926C2CD227A200DDA11B /* HearoadWatch.entitlements */,
001C9F852CD0CA11004EC4D2 /* HearoadWatch-Watch-App-Info.plist */,
001C9F572CCF7983004EC4D2 /* HearoadWatchApp.swift */,
001C9F832CCF81A1004EC4D2 /* WatchSessionManager.swift */,
001C9F592CCF7983004EC4D2 /* ContentView.swift */,
001C9F5B2CCF7984004EC4D2 /* Assets.xcassets */,
- 001C9F5D2CCF7984004EC4D2 /* Preview Content */,
);
path = "HearoadWatch Watch App";
sourceTree = "";
@@ -501,19 +490,15 @@
path = View;
sourceTree = "";
};
- 00A04EA22CB7CA320031DECC /* Enums */ = {
- isa = PBXGroup;
- children = (
- );
- path = Enums;
- sourceTree = "";
- };
00A04EA32CB7CA510031DECC /* Helper */ = {
isa = PBXGroup;
children = (
+
1159334B2CDB2A7C005902F2 /* HornSoundClassifier_V11.mlmodel */,
+
64F73A242CBAC9F100D2A140 /* HornSoundDetector.swift */,
64F73A262CBAD1C000D2A140 /* SoundDetectorViewModel.swift */,
+ 001D669A2CDB3E11005746AC /* Haptic.swift */,
);
path = Helper;
sourceTree = "";
@@ -792,17 +777,24 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ 645C1B1C2CCBBE7000251157 /* OnboardingWarningView.swift in Sources */,
00078BED2CAFC7BA00FB3F70 /* HomeViewModel.swift in Sources */,
+ 645C1B162CCBB3BF00251157 /* OnboardingWelcomeView.swift in Sources */,
+ 645C1B1E2CCBBFAB00251157 /* OnboardingStandRecommendView.swift in Sources */,
00078BE12CAFC77000FB3F70 /* FinishViewModel.swift in Sources */,
+ 00FC15162CDA10B700FBCE0C /* HornSoundClassifier_V9.mlmodel in Sources */,
+ 001D669B2CDB3E11005746AC /* Haptic.swift in Sources */,
00078BEB2CAFC7B000FB3F70 /* HomeView.swift in Sources */,
0045C6BA2CC02969009B4261 /* WarningViewModel.swift in Sources */,
001C6F7E2CAFBC6900B03767 /* hearoApp.swift in Sources */,
+
00078BE92CAFC7A800FB3F70 /* OnboardingViewModel.swift in Sources */,
00078BDF2CAFC75900FB3F70 /* FinishView.swift in Sources */,
64F73A272CBAD1C000D2A140 /* SoundDetectorViewModel.swift in Sources */,
007BEFCE2CB159AD00C0E9FD /* WorkingView.swift in Sources */,
007BEFD02CB159BF00C0E9FD /* WorkingViewModel.swift in Sources */,
64F73A252CBAC9F100D2A140 /* HornSoundDetector.swift in Sources */,
+ 645C1B1A2CCBBB4800251157 /* OnboardingPrivacyView.swift in Sources */,
0045C6B82CC0295F009B4261 /* WarningView.swift in Sources */,
1159334C2CDB2A7C005902F2 /* HornSoundClassifier_V11.mlmodel in Sources */,
00078BE72CAFC79E00FB3F70 /* OnboardingView.swift in Sources */,
@@ -1024,7 +1016,9 @@
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"hearo/Preview Content\"";
+
DEVELOPMENT_TEAM = GT56H2MYWV;
+
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = hearo/Info.plist;
@@ -1059,7 +1053,9 @@
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"hearo/Preview Content\"";
+
DEVELOPMENT_TEAM = GT56H2MYWV;
+
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = hearo/Info.plist;
@@ -1169,7 +1165,9 @@
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"HearoadWatch Watch App/Preview Content\"";
+
DEVELOPMENT_TEAM = GT56H2MYWV;
+
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "HearoadWatch-Watch-App-Info.plist";
@@ -1200,7 +1198,9 @@
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"HearoadWatch Watch App/Preview Content\"";
+
DEVELOPMENT_TEAM = GT56H2MYWV;
+
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "HearoadWatch-Watch-App-Info.plist";
@@ -1312,7 +1312,9 @@
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
+
DEVELOPMENT_TEAM = GT56H2MYWV;
+
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = LiveActivity/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = LiveActivity;
@@ -1340,7 +1342,9 @@
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
+
DEVELOPMENT_TEAM = GT56H2MYWV;
+
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = LiveActivity/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = LiveActivity;
diff --git a/hearo/hearo.xcodeproj/xcshareddata/xcschemes/HearoadWatch Watch App.xcscheme b/hearo/hearo.xcodeproj/xcshareddata/xcschemes/HearoadWatch Watch App.xcscheme
index 6aad58a..bc759b8 100644
--- a/hearo/hearo.xcodeproj/xcshareddata/xcschemes/HearoadWatch Watch App.xcscheme
+++ b/hearo/hearo.xcodeproj/xcshareddata/xcschemes/HearoadWatch Watch App.xcscheme
@@ -88,6 +88,13 @@
ReferencedContainer = "container:hearo.xcodeproj">
+
+
+
+
+
+
+
+
.request(
- attributes: attributes,
- contentState: initialContentState,
- pushType: nil
- )
- print("라이브 액티비티가 시작되었습니다: \(activity.id)")
- } catch {
- print("라이브 액티비티 시작 실패: \(error)")
+ // 라이브 액티비티 시작 메서드
+ func startLiveActivity(isWarning: Bool) {
+ guard ActivityAuthorizationInfo().areActivitiesEnabled else {
+ print("라이브 액티비티가 지원되지 않거나 비활성화되었습니다.")
+ return
+ }
+
+ let attributes = LiveActivityAttributes(name: "주행")
+ let initialContentState = LiveActivityAttributes.ContentState(isWarning: isWarning)
+
+ do {
+ let activity = try Activity.request(
+ attributes: attributes,
+ contentState: initialContentState,
+ pushType: nil
+ )
+ print("라이브 액티비티가 시작되었습니다: \(activity.id)")
+ } catch {
+ print("라이브 액티비티 시작 실패: \(error)")
+ }
}
- }
-
- // 라이브 액티비티 중지 메서드
- func stopLiveActivity() {
- Task {
- for activity in Activity.activities {
- await activity.end(dismissalPolicy: .immediate)
- print("라이브 액티비티가 중지되었습니다: \(activity.id)")
- }
+
+ // 라이브 액티비티 중지 메서드
+ func stopLiveActivity() {
+ Task {
+ for activity in Activity.activities {
+ await activity.end(dismissalPolicy: .immediate)
+ print("라이브 액티비티가 중지되었습니다: \(activity.id)")
+ }
+ }
}
- }
-
- // 라이브 액티비티 업데이트 메서드
- func updateLiveActivity(isWarning: Bool) {
- guard let activity = Activity.activities.first else { return }
- Task {
- // 현재 상태와 새로운 경고 상태가 다를 때만 업데이트
- if activity.contentState.isWarning != isWarning {
- let updatedContentState = LiveActivityAttributes.ContentState(isWarning: isWarning)
- await activity.update(using: updatedContentState)
- print("라이브 액티비티 상태 업데이트: \(isWarning ? "경고" : "주행 중")")
- }
+ // 라이브 액티비티 업데이트 메서드
+ func updateLiveActivity(isWarning: Bool) {
+ guard let activity = Activity.activities.first else { return }
+
+ Task {
+ if activity.contentState.isWarning != isWarning {
+ let updatedContentState = LiveActivityAttributes.ContentState(isWarning: isWarning)
+ await activity.update(using: updatedContentState)
+ print("라이브 액티비티 상태 업데이트: \(isWarning ? "경고" : "주행 중")")
+ }
+ }
}
- }
}
+// ContentView 정의 (AppRootManager 클래스 외부에 위치)
struct ContentView: View {
@ObservedObject var appRootManager: AppRootManager
diff --git a/hearo/hearo/Resources/Assets.xcassets/HPrimaryColor.colorset/Contents.json b/hearo/hearo/Resources/Assets.xcassets/HPrimaryColor.colorset/Contents.json
index fb54bee..9a696be 100644
--- a/hearo/hearo/Resources/Assets.xcassets/HPrimaryColor.colorset/Contents.json
+++ b/hearo/hearo/Resources/Assets.xcassets/HPrimaryColor.colorset/Contents.json
@@ -5,9 +5,9 @@
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
- "blue" : "243",
- "green" : "122",
- "red" : "10"
+ "blue" : "89",
+ "green" : "199",
+ "red" : "52"
}
},
"idiom" : "universal"
@@ -23,9 +23,9 @@
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
- "blue" : "243",
- "green" : "122",
- "red" : "10"
+ "blue" : "89",
+ "green" : "199",
+ "red" : "52"
}
},
"idiom" : "universal"
diff --git a/hearo/hearo/Sources/Helper/Haptic.swift b/hearo/hearo/Sources/Helper/Haptic.swift
new file mode 100644
index 0000000..d546345
--- /dev/null
+++ b/hearo/hearo/Sources/Helper/Haptic.swift
@@ -0,0 +1,24 @@
+//
+// Haptic.swift
+// hearo
+//
+// Created by Pil_Gaaang on 11/6/24.
+//
+
+import Foundation
+import UIKit
+
+func triggerSuccessHaptic() {
+ let generator = UINotificationFeedbackGenerator()
+ generator.notificationOccurred(.success)
+}
+
+func triggerWarningHaptic() {
+ let generator = UINotificationFeedbackGenerator()
+ generator.notificationOccurred(.warning)
+}
+
+func triggerErrorHaptic() {
+ let generator = UINotificationFeedbackGenerator()
+ generator.notificationOccurred(.error)
+}
diff --git a/hearo/hearo/Sources/Presentations/Finish/View/FinishView.swift b/hearo/hearo/Sources/Presentations/Finish/View/FinishView.swift
index 0a0e029..6bd9ba9 100644
--- a/hearo/hearo/Sources/Presentations/Finish/View/FinishView.swift
+++ b/hearo/hearo/Sources/Presentations/Finish/View/FinishView.swift
@@ -12,64 +12,47 @@ struct FinishView: View {
var body: some View {
VStack {
- Spacer().frame(height: 84)
+ Spacer().frame(height: 195)
- HStack {
- Spacer().frame(width: 16)
-
+
Text("안전 주행 완료!")
.font(
- Font.custom("Spoqa Han Sans Neo", size: 25)
+ Font.custom("Spoqa Han Sans Neo", size: 24)
.weight(.bold)
)
- .foregroundColor(Color("HWhite"))
+ .foregroundColor(Color.black)
- Spacer()
- }
-
- Spacer().frame(height: 19)
- HStack {
- Spacer().frame(width: 16)
-
- Text("오늘도 히어로드와 함께 무사히 도착하셨습니다.\n다음에도 안전하게 뵙겠습니다!")
- .font(Font.custom("Spoqa Han Sans Neo", size: 16))
- .foregroundColor(Color("HGray2"))
- .frame(width: 295, alignment: .topLeading)
+ Spacer().frame(height: 10)
+
+ Text("오늘도 함께 무사히 도착했습니다.\n다음에도 안전하게 뵙겠습니다.")
+ .font(Font.custom("Spoqa Han Sans Neo", size: 15))
+ .foregroundColor(Color("HGray2"))
+ .font(Font.custom("Pretendard", size: 15))
+ .foregroundColor(Color(red: 0.24, green: 0.26, blue: 0.31))
+ .frame(width: 345, alignment: .center) // 가로 중앙 정렬
+ .multilineTextAlignment(.center) // 텍스트 줄바꿈 시 가운데 정렬
- Spacer()
- }
+
+
- Spacer().frame(height: 104)
+ Spacer().frame(height: 72)
Image(systemName: "checkmark.circle.fill") // SF Symbols에서 원형 아이콘 사용
.resizable()
- .frame(width: 160, height: 160)
+ .frame(width: 121, height: 121)
.foregroundColor(Color("HPrimaryColor")) // 아이콘 색상 설정
Spacer().frame(height: 231)
- Button(action: {
- viewModel.goToHome() // 홈으로 돌아가는 동작
- }) {
- ZStack {
- Rectangle()
- .foregroundColor(.clear)
- .frame(width: 361, height: 58)
- .background(Color.white)
- .cornerRadius(10)
- .opacity(0.28)
-
- Text("시작하기")
- .font(Font.custom("Spoqa Han Sans Neo", size: 18).weight(.medium))
- .multilineTextAlignment(.center)
- .foregroundColor(.white)
- }
- }
+
Spacer()
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
- .background(Color(red: 28/255, green: 34/255, blue: 46/255, opacity: 1))
+ .background(Color(red: 1, green: 1, blue: 1, opacity: 1))
.edgesIgnoringSafeArea(.all)
+ .onDisappear {
+ triggerSuccessHaptic()
+ }
}
}
diff --git a/hearo/hearo/Sources/Presentations/Finish/ViewModel/FinishViewModel.swift b/hearo/hearo/Sources/Presentations/Finish/ViewModel/FinishViewModel.swift
index c1a6f67..206c22c 100644
--- a/hearo/hearo/Sources/Presentations/Finish/ViewModel/FinishViewModel.swift
+++ b/hearo/hearo/Sources/Presentations/Finish/ViewModel/FinishViewModel.swift
@@ -6,15 +6,20 @@
//
import Foundation
+import UIKit
class FinishViewModel: ObservableObject {
@Published var appRootManager: AppRootManager
init(appRootManager: AppRootManager) {
self.appRootManager = appRootManager
+ goToHomeWithDelay()
}
- func goToHome() {
- appRootManager.currentRoot = .home
+ private func goToHomeWithDelay() {
+
+ DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in
+ self?.appRootManager.currentRoot = .home
+ }
}
}
diff --git a/hearo/hearo/Sources/Presentations/Home/View/HomeView.swift b/hearo/hearo/Sources/Presentations/Home/View/HomeView.swift
index 8f4799b..41bd7e1 100644
--- a/hearo/hearo/Sources/Presentations/Home/View/HomeView.swift
+++ b/hearo/hearo/Sources/Presentations/Home/View/HomeView.swift
@@ -77,3 +77,7 @@ struct HomeView: View {
}
}
}
+
+#Preview {
+ HomeView(viewModel: HomeViewModel(appRootManager: AppRootManager()))
+}
diff --git a/hearo/hearo/Sources/Presentations/OnBoarding/View/OnboardingNotiPermissionView.swift b/hearo/hearo/Sources/Presentations/OnBoarding/View/OnboardingNotiPermissionView.swift
new file mode 100644
index 0000000..3ad5e4b
--- /dev/null
+++ b/hearo/hearo/Sources/Presentations/OnBoarding/View/OnboardingNotiPermissionView.swift
@@ -0,0 +1,74 @@
+//
+// OnboardingNotiPermissionView.swift
+// hearo
+//
+// Created by 규북 on 10/25/24.
+//
+
+import SwiftUI
+
+struct OnboardingNotiPermissionView: View {
+ @StateObject var viewModel: OnboardingViewModel
+
+ var body: some View {
+ VStack {
+ Spacer().frame(height: 59)
+
+ HStack {
+ Spacer().frame(width: 16)
+
+ Text("경적 소리 감지시\n안전을 위한 알림을 허용하세요")
+ .font(
+ Font.custom("Spoqa Han Sans Neo", size: 25)
+ .weight(.bold)
+ )
+ // .foregroundColor(Color("HWhite"))
+
+ Spacer()
+ }
+
+ Spacer().frame(height: 19)
+
+ HStack {
+ Spacer().frame(width: 16)
+
+ Text("안전한 주행을 위해, 주행 중 경적, 사이렌 소리 등\n중요한 경고 신호를 놓치지 않도록 알림을 허용해 주세요.")
+ .font(Font.custom("Spoqa Han Sans Neo", size: 13))
+ .foregroundColor(Color("HGray2"))
+ .frame(width: 295, alignment: .topLeading)
+
+ Spacer()
+ }
+
+ Spacer().frame(height: 139)
+
+ Image(systemName: "bell.fill")
+ .resizable()
+ .frame(width: 138.34, height: 161.72)
+ .foregroundColor(Color("HPrimaryColor"))
+
+ Spacer().frame(height: 121.28)
+
+ Button(action: {
+ viewModel.moveToNextPage() // 두 번째 페이지로 전환
+ }) {
+ ZStack {
+ Rectangle()
+ // .foregroundColor(.clear)
+ .frame(width: 361, height: 58)
+ .background(Color.white)
+ .cornerRadius(10)
+ .opacity(0.28)
+
+ Text("시작하기")
+ .font(Font.custom("Spoqa Han Sans Neo", size: 18).weight(.medium))
+ .multilineTextAlignment(.center)
+ }
+ }
+ }
+ }
+}
+
+#Preview {
+ OnboardingNotiPermissionView(viewModel: OnboardingViewModel(appRootManager: AppRootManager()))
+}
diff --git a/hearo/hearo/Sources/Presentations/OnBoarding/View/OnboardingPrivacyView.swift b/hearo/hearo/Sources/Presentations/OnBoarding/View/OnboardingPrivacyView.swift
new file mode 100644
index 0000000..c7b731d
--- /dev/null
+++ b/hearo/hearo/Sources/Presentations/OnBoarding/View/OnboardingPrivacyView.swift
@@ -0,0 +1,66 @@
+//
+// OnboardingPrivacyView.swift
+// hearo
+//
+// Created by 규북 on 10/25/24.
+//
+
+import SwiftUI
+
+struct OnboardingPrivacyView: View {
+ @StateObject var viewModel: OnboardingViewModel
+ var body: some View {
+ VStack {
+ Spacer().frame(height: 59)
+
+ HStack {
+ Spacer().frame(width: 16)
+
+ Text("프라이버시 보호관련 안내")
+ .font(
+ Font.custom("Spoqa Han Sans Neo", size: 25)
+ .weight(.bold)
+ )
+
+ Spacer()
+ }
+
+ Spacer().frame(height: 19)
+
+ Text("· 녹음된 오디오는 오직 경적 소리 인식 목적으로만 사용됩니다.\n\n"+"· 녹음 데이터는 저장되지 않으며, 실시간으로 분석 후 즉시 삭제됩니다.\n\n"+"· 사용자의 다른 행동이나 대화는 절대 녹음되지 않습니다.")
+ .foregroundStyle(Color("HGray2"))
+ .padding(.leading,44)
+
+
+
+ Spacer().frame(height: 139)
+
+ Image(systemName: "lock.shield.fill")
+ .resizable()
+ .frame(width: 138.34, height: 161.72)
+ .foregroundColor(Color("HPrimaryColor"))
+
+ Spacer().frame(height: 121.28)
+
+ Button(action: {
+ viewModel.moveToNextPage() // 다음 페이지로 전환
+ }) {
+ ZStack {
+ Rectangle()
+ .frame(width: 361, height: 58)
+ .background(Color.white)
+ .cornerRadius(10)
+ .opacity(0.28)
+
+ Text("다음")
+ .font(Font.custom("Spoqa Han Sans Neo", size: 18).weight(.medium))
+ .multilineTextAlignment(.center)
+ }
+ }
+ }
+ }
+}
+
+#Preview {
+ OnboardingPrivacyView(viewModel: OnboardingViewModel(appRootManager: AppRootManager()))
+}
diff --git a/hearo/hearo/Sources/Presentations/OnBoarding/View/OnboardingStandRecommendView.swift b/hearo/hearo/Sources/Presentations/OnBoarding/View/OnboardingStandRecommendView.swift
new file mode 100644
index 0000000..78f63d4
--- /dev/null
+++ b/hearo/hearo/Sources/Presentations/OnBoarding/View/OnboardingStandRecommendView.swift
@@ -0,0 +1,62 @@
+//
+// OnboardingStandRecommendView.swift
+// hearo
+//
+// Created by 규북 on 10/25/24.
+//
+
+import SwiftUI
+
+struct OnboardingStandRecommendView: View {
+ @StateObject var viewModel: OnboardingViewModel
+ var body: some View {
+ VStack {
+ Text("정확한 위치 확인을 위해서\n거치대를 확인해주세요")
+ .font(
+ Font.custom("Spoqa Han Sans Neo", size: 25)
+ .weight(.bold)
+ )
+ .frame(width: 393, alignment: .leading)
+ .padding()
+
+
+
+ Text("더 안전하고 정확한 경고 알림을 받기 위해\n스마트폰을 거치대에 고정해 주행해 주세요.")
+ .font(Font.custom("Spoqa Han Sans Neo", size: 14))
+ .foregroundStyle(Color("HGray2"))
+ .frame(width: 345, alignment: .topLeading)
+
+
+ Spacer().frame(height: 139)
+
+ Image(systemName: "iphone.gen1.radiowaves.left.and.right")
+ .resizable()
+ .frame(width: 135.84, height: 180)
+ .foregroundColor(Color("HPrimaryColor"))
+
+ Spacer().frame(height: 162)
+
+ Button(action: {
+ viewModel.moveToHome() // 홈 화면으로 전환
+ }) {
+ ZStack {
+ Rectangle()
+ // .foregroundColor(.clear)
+ .frame(width: 361, height: 58)
+ .background(Color.white)
+ .cornerRadius(10)
+ .opacity(0.28)
+
+ Text("확인")
+ .font(Font.custom("Spoqa Han Sans Neo", size: 18).weight(.medium))
+ .multilineTextAlignment(.center)
+ .foregroundColor(.white)
+ }
+ }
+ }
+ }
+}
+
+#Preview {
+ OnboardingStandRecommendView(viewModel: OnboardingViewModel(appRootManager: AppRootManager()))
+}
diff --git a/hearo/hearo/Sources/Presentations/OnBoarding/View/OnboardingView.swift b/hearo/hearo/Sources/Presentations/OnBoarding/View/OnboardingView.swift
index 26cc6b7..6828c75 100644
--- a/hearo/hearo/Sources/Presentations/OnBoarding/View/OnboardingView.swift
+++ b/hearo/hearo/Sources/Presentations/OnBoarding/View/OnboardingView.swift
@@ -13,193 +13,27 @@ struct OnboardingView: View {
var body: some View {
TabView(selection: $viewModel.currentPage) {
// 첫 번째 온보딩 페이지
- VStack {
- Spacer().frame(height: 59)
-
- HStack {
- Spacer().frame(width: 16)
-
- Text("당신을 위한 소리 감지 앱\n히어로드에 오신 걸 환영합니다!")
- .font(
- Font.custom("Spoqa Han Sans Neo", size: 25)
- .weight(.bold)
- )
- .foregroundColor(Color("HWhite"))
-
- Spacer()
- }
-
- Spacer().frame(height: 19)
-
- HStack {
- Spacer().frame(width: 16)
-
- Text("히여로는 주행 중 위험 신호를 실시간으로 감지해\n시각과 진동으로 알려드립니다.")
- .font(Font.custom("Spoqa Han Sans Neo", size: 13))
- .foregroundColor(Color("HGray2"))
- .frame(width: 295, alignment: .topLeading)
-
- Spacer()
- }
-
- Spacer().frame(height: 102)
-
- Image(systemName: "circle.fill")
- .resizable()
- .frame(width: 189, height: 189)
- .foregroundColor(Color("HPrimaryColor"))
-
- Spacer().frame(height: 120)
-
- Button(action: {
- viewModel.moveToNextPage() // 두 번째 페이지로 전환
- }) {
- ZStack {
- Rectangle()
- .foregroundColor(.clear)
- .frame(width: 361, height: 58)
- .background(Color.white)
- .cornerRadius(10)
- .opacity(0.28)
-
- Text("시작하기")
- .font(Font.custom("Spoqa Han Sans Neo", size: 18).weight(.medium))
- .multilineTextAlignment(.center)
- .foregroundColor(.white)
- }
- }
-
- Spacer()
- }
+ OnboardingNotiPermissionView(viewModel: viewModel)
.tag(0)
// 두 번째 온보딩 페이지
- VStack {
- Spacer().frame(height: 59)
-
- HStack {
- Spacer().frame(width: 16)
-
- Text("경적 소리 감지시\n안전을 위한 알림을 허용하세요")
- .font(
- Font.custom("Spoqa Han Sans Neo", size: 25)
- .weight(.bold)
- )
- .foregroundColor(Color("HWhite"))
-
- Spacer()
- }
-
- Spacer().frame(height: 19)
-
- HStack {
- Spacer().frame(width: 16)
-
- Text("안전한 주행을 위해, 주행 중 경적, 사이렌 소리 등\n중요한 경고 신호를 놓치지 않도록 알림을 허용해 주세요.")
- .font(Font.custom("Spoqa Han Sans Neo", size: 13))
- .foregroundColor(Color("HGray2"))
- .frame(width: 295, alignment: .topLeading)
-
- Spacer()
- }
-
- Spacer().frame(height: 139)
-
- Image(systemName: "bell.fill")
- .resizable()
- .frame(width: 138.34, height: 161.72)
- .foregroundColor(Color("HPrimaryColor"))
-
- Spacer().frame(height: 121.28)
-
- Button(action: {
- viewModel.moveToNextPage() // 세 번째 페이지로 전환
- }) {
- ZStack {
- Rectangle()
- .foregroundColor(.clear)
- .frame(width: 361, height: 58)
- .background(Color.white)
- .cornerRadius(10)
- .opacity(0.28)
-
- Text("확인")
- .font(Font.custom("Spoqa Han Sans Neo", size: 18).weight(.medium))
- .multilineTextAlignment(.center)
- .foregroundColor(.white)
- }
- }
-
- Spacer()
- }
+ OnboardingPrivacyView(viewModel:viewModel)
.tag(1)
- // 추가 페이지들도 동일한 방식으로 추가...
+
+ OnboardingWarningView(viewModel:viewModel)
+ .tag(2)
// 마지막 온보딩 페이지
- VStack {
- Spacer().frame(height: 59)
-
- HStack {
- Spacer().frame(width: 19)
-
- Text("정확한 위치 확인을 위해서\n거치대를 활용해주세요")
- .font(
- Font.custom("Spoqa Han Sans Neo", size: 25)
- .weight(.bold)
- )
- .foregroundColor(Color("HWhite"))
-
- Spacer()
- }
-
- Spacer().frame(height: 21)
-
- HStack {
- Spacer().frame(width: 16)
-
- Text("더 안전하고 정확한 경고 알림을 받기 위해\n스마트폰을 거치대에 고정해 주행해 주세요.")
- .font(Font.custom("Spoqa Han Sans Neo", size: 14))
- .foregroundColor(Color(red: 0.7, green: 0.7, blue: 0.7))
- .frame(width: 345, alignment: .topLeading)
-
- Spacer()
- }
-
- Spacer().frame(height: 139)
-
- Image(systemName: "iphone.gen1.radiowaves.left.and.right")
- .resizable()
- .frame(width: 135.84, height: 180)
- .foregroundColor(Color("HPrimaryColor"))
-
- Spacer().frame(height: 162)
-
- Button(action: {
- viewModel.moveToHome() // 홈 화면으로 전환
- }) {
- ZStack {
- Rectangle()
- .foregroundColor(.clear)
- .frame(width: 361, height: 58)
- .background(Color.white)
- .cornerRadius(10)
- .opacity(0.28)
-
- Text("확인")
- .font(Font.custom("Spoqa Han Sans Neo", size: 18).weight(.medium))
- .multilineTextAlignment(.center)
- .foregroundColor(.white)
- }
- }
-
- Spacer()
- }
- .tag(4)
+ OnboardingStandRecommendView(viewModel: viewModel)
+ .tag(3)
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
.frame(maxWidth: .infinity, maxHeight: .infinity)
- .background(Color(red: 28/255, green: 34/255, blue: 46/255, opacity: 1))
.edgesIgnoringSafeArea(.all)
}
}
+
+#Preview {
+ OnboardingView(viewModel: OnboardingViewModel(appRootManager: AppRootManager()))
+}
diff --git a/hearo/hearo/Sources/Presentations/OnBoarding/View/OnboardingWarningView.swift b/hearo/hearo/Sources/Presentations/OnBoarding/View/OnboardingWarningView.swift
new file mode 100644
index 0000000..bbfe3e1
--- /dev/null
+++ b/hearo/hearo/Sources/Presentations/OnBoarding/View/OnboardingWarningView.swift
@@ -0,0 +1,66 @@
+//
+// OnboardingWarningView.swift
+// hearo
+//
+// Created by 규북 on 10/25/24.
+//
+
+import SwiftUI
+
+struct OnboardingWarningView: View {
+ @StateObject var viewModel: OnboardingViewModel
+ var body: some View {
+ VStack {
+ Spacer().frame(height: 59)
+
+ HStack {
+ Spacer().frame(width: 16)
+
+ Text("우리의 경고 알림은 \n \"보조수단\"일 뿐입니다.")
+ .font(
+ Font.custom("Spoqa Han Sans Neo", size: 25)
+ .weight(.bold)
+ )
+
+ Spacer()
+ }
+
+ Spacer().frame(height: 19)
+
+ Text("· 이 앱은 위험 신호를 보조적으로 알리는 도구입니다.\n\n"+"· 사용자의 안전 주의는 가장 중요한 요소입니다.\n 항상 주변을 확인해주세요.")
+ .foregroundStyle(Color("HGray2"))
+ .padding(.leading,40)
+ .padding(.trailing)
+
+
+ Spacer().frame(height: 139)
+
+ Image(systemName: "exclamationmark.triangle.fill")
+ .resizable()
+ .frame(width: 138.34, height: 161.72)
+ .foregroundColor(Color(red: 255/255, green: 216/255, blue: 19/255, opacity: 1))
+
+ Spacer().frame(height: 121.28)
+
+ Button(action: {
+ viewModel.moveToNextPage() // 다음 페이지로 전환
+ }) {
+ ZStack {
+ Rectangle()
+ .frame(width: 361, height: 58)
+ .background(Color.white)
+ .cornerRadius(10)
+ .opacity(0.28)
+
+ Text("다음")
+ .font(Font.custom("Spoqa Han Sans Neo", size: 18).weight(.medium))
+ .multilineTextAlignment(.center)
+ }
+ }
+ }
+ }
+}
+
+#Preview {
+ OnboardingWarningView(viewModel: OnboardingViewModel(appRootManager: AppRootManager()))
+}
diff --git a/hearo/hearo/Sources/Presentations/OnBoarding/View/OnboardingWelcomeView.swift b/hearo/hearo/Sources/Presentations/OnBoarding/View/OnboardingWelcomeView.swift
new file mode 100644
index 0000000..f71a563
--- /dev/null
+++ b/hearo/hearo/Sources/Presentations/OnBoarding/View/OnboardingWelcomeView.swift
@@ -0,0 +1,55 @@
+//
+// OnboardingWelcomeView.swift
+// hearo
+//
+// Created by 규북 on 10/25/24.
+//
+
+import SwiftUI
+
+struct OnboardingWelcomeView: View {
+ var body: some View {
+ VStack {
+ Spacer().frame(height: 59)
+
+ HStack {
+ Spacer().frame(width: 16)
+
+ Text("당신을 위한 소리 감지 앱\n히어로드에 오신 걸 환영합니다!")
+ .font(
+ Font.custom("Spoqa Han Sans Neo", size: 25)
+ .weight(.bold)
+ )
+
+ Spacer()
+ }
+
+ Spacer().frame(height: 19)
+
+ HStack {
+ Spacer().frame(width: 16)
+
+ Text("히어로드는 주행 중 위험 신호를 실시간으로 감지해\n시각과 진동으로 알려드립니다.")
+ .font(Font.custom("Spoqa Han Sans Neo", size: 13))
+ .foregroundColor(Color("HGray2"))
+ .frame(width: 295, alignment: .topLeading)
+
+ Spacer()
+ }
+
+ Spacer().frame(height: 102)
+
+ VStack(alignment: .leading) {
+ Text("서비스 이용 약관에 동의합니다.")
+ Text("히어로 이용 약관에 동의하시면 '앱 시작하기'를 눌러주세요.")
+ }
+
+ Spacer().frame(height: 120)
+ }
+ }
+}
+
+
+#Preview {
+ OnboardingWelcomeView()
+}
diff --git a/hearo/hearo/Sources/Presentations/OnBoarding/ViewModel/OnboardingViewModel.swift b/hearo/hearo/Sources/Presentations/OnBoarding/ViewModel/OnboardingViewModel.swift
index 7ca3745..03bc959 100644
--- a/hearo/hearo/Sources/Presentations/OnBoarding/ViewModel/OnboardingViewModel.swift
+++ b/hearo/hearo/Sources/Presentations/OnBoarding/ViewModel/OnboardingViewModel.swift
@@ -20,6 +20,7 @@ class OnboardingViewModel: ObservableObject {
}
func moveToHome() {
+ UserDefaults.standard.set(true, forKey: "hasSeenOnboarding")
appRootManager.currentRoot = .home
}
}
diff --git a/hearo/hearo/Sources/Presentations/Splash/View/SplashView.swift b/hearo/hearo/Sources/Presentations/Splash/View/SplashView.swift
index ecff404..45d9c39 100644
--- a/hearo/hearo/Sources/Presentations/Splash/View/SplashView.swift
+++ b/hearo/hearo/Sources/Presentations/Splash/View/SplashView.swift
@@ -7,48 +7,53 @@
import SwiftUI
struct SplashView: View {
- var appRootManager: AppRootManager // 인자로 받기
- @State private var isActive = false // 스플래시 화면이 끝났는지 여부를 관리하는 상태
-
- var body: some View {
- ZStack {
- VStack {
- if isActive {
- // 상태를 즉시 변경하여 OnboardingView로 전환
- // EmptyView를 사용하지 않고 상태만 변경
- Color.clear
- .onAppear {
- appRootManager.currentRoot = .onboarding // 상태 변경
- }
- } else {
-
- Spacer().frame(height: 217)
-
- Text("Hearoad")
- .font(Font.custom("Inter", size: 34.80519)
- .weight(.medium)
- )
- .multilineTextAlignment(.center)
- .foregroundColor(Color("HWhite"))
-
- Spacer()
-
- Text("Copyright 2024. DaQman2ni in all rights reserved.")
- .font(Font.custom("Spoqa Han Sans Neo", size: 12))
- .multilineTextAlignment(.center)
- .foregroundColor(Color("HWhite"))
-
- Spacer().frame(height: 44)
- .onAppear {
- // 2초 후 isActive 상태를 true로 변경
- DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
- self.isActive = true
- }
- }
+ var appRootManager: AppRootManager // 인자로 받기
+ @State private var isActive = false // 스플래시 화면이 끝났는지 여부를 관리하는 상태
+
+ var body: some View {
+ ZStack {
+
+ Color("HPrimaryColor")
+ .ignoresSafeArea(.all)
+
+ VStack {
+ if isActive {
+ Color.clear
+ .onAppear {
+ appRootManager.determineNextRoot() // splash 후 다음 화면 결정
+ }
+ } else {
+
+ Spacer().frame(height: 217)
+
+ Text("Hearoad")
+ .font(Font.custom("Inter", size: 34.80519)
+ .weight(.medium)
+ )
+ .multilineTextAlignment(.center)
+ .foregroundColor(Color("HWhite"))
+
+ Spacer()
+
+ Text("Copyright 2024. DaQman2ni in all rights reserved.")
+ .font(Font.custom("Spoqa Han Sans Neo", size: 12))
+ .multilineTextAlignment(.center)
+ .foregroundColor(Color("HWhite"))
+
+ Spacer().frame(height: 44)
+ .onAppear {
+ // 2초 후 isActive 상태를 true로 변경
+ DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
+ self.isActive = true
+ }
}
}
- }
- .frame(width: 393, height: 852)
- .background(Color(red: 28/255, green: 34/255, blue: 46/255, opacity: 1))
+ }
}
+
+ }
+}
+
+#Preview {
+ SplashView(appRootManager: AppRootManager())
}
diff --git a/hearo/hearo/Sources/Presentations/Working/ViewModel/HornSoundDetector.swift b/hearo/hearo/Sources/Presentations/Working/ViewModel/HornSoundDetector.swift
index b14000f..3bdb498 100644
--- a/hearo/hearo/Sources/Presentations/Working/ViewModel/HornSoundDetector.swift
+++ b/hearo/hearo/Sources/Presentations/Working/ViewModel/HornSoundDetector.swift
@@ -178,31 +178,31 @@ class HornSoundDetector: NSObject, ObservableObject {
}
extension HornSoundDetector: SNResultsObserving {
- func request(_ request: SNRequest, didProduce result: SNResult) {
- guard let result = result as? SNClassificationResult else { return }
-
- let topClassifications = result.classifications.prefix(3)
-
- DispatchQueue.main.async {
- // 첫 번째 분류를 가장 신뢰도 높은 것으로 설정
- if let topClassification = topClassifications.first(where: { classification in
- return classification.identifier == "Bicyclebell" || classification.identifier == "Carhorn" || classification.identifier == "Siren"
- }) {
- self.topClassification = topClassification // 가장 높은 분류 저장
- self.classificationResult = "소리: \(topClassification.identifier), 신뢰도: \(topClassification.confidence)"
- }
-
- for (index, classification) in topClassifications.enumerated() {
- if classification.identifier == "Bicyclebell" || classification.identifier == "Carhorn" || classification.identifier == "Siren" {
- print("소리: \(classification.identifier), 신뢰도: \(classification.confidence)")
-
- // 경적 및 사이렌 소리 감지
- if classification.confidence >= 1.0 {
- self.mlConfidences[index] = classification.confidence
- self.checkAllChannelsConfidence() // 모든 채널 신뢰도 확인
- }
+ func request(_ request: SNRequest, didProduce result: SNResult) {
+ guard let result = result as? SNClassificationResult else { return }
+
+ let topClassifications = result.classifications.prefix(3)
+
+ DispatchQueue.main.async {
+ // 첫 번째 분류를 가장 신뢰도 높은 것으로 설정
+ if let topClassification = topClassifications.first(where: { classification in
+ return classification.identifier == "Bicyclebell" || classification.identifier == "Carhorn" || classification.identifier == "Siren"
+ }) {
+ self.topClassification = topClassification // 가장 높은 분류 저장
+ self.classificationResult = topClassification.identifier // 소리 종류만 저장
+ }
+
+ for (index, classification) in topClassifications.enumerated() {
+ if classification.identifier == "Bicyclebell" || classification.identifier == "Carhorn" || classification.identifier == "Siren" {
+ // 경적 및 사이렌 소리 감지
+ if classification.confidence >= 1.0 {
+ self.mlConfidences[index] = classification.confidence
+ self.checkAllChannelsConfidence() // 모든 채널 신뢰도 확인
+ }
+ }
+ }
}
}
}
- }
-}
+
+
diff --git a/hearo/hearo/Sources/Presentations/Working/ViewModel/SoundDetectorViewModel.swift b/hearo/hearo/Sources/Presentations/Working/ViewModel/SoundDetectorViewModel.swift
index 6eb4bc5..37d1479 100644
--- a/hearo/hearo/Sources/Presentations/Working/ViewModel/SoundDetectorViewModel.swift
+++ b/hearo/hearo/Sources/Presentations/Working/ViewModel/SoundDetectorViewModel.swift
@@ -86,7 +86,9 @@ class SoundDetectorViewModel: NSObject, ObservableObject, WCSessionDelegate {
highestConfidenceIndex < classificationResults.count {
let highestConfidenceSound = classificationResults[highestConfidenceIndex]
- let message = ["alert": "경고: \(highestConfidenceSound) 소리 감지됨"]
+
+ // **신뢰도 없이 소리 종류만 전송**
+ let message = ["alert": highestConfidenceSound]
WCSession.default.sendMessage(message, replyHandler: nil) { error in
print("애플워치로 경고 메시지 전송 오류: \(error.localizedDescription)")
@@ -110,15 +112,15 @@ class SoundDetectorViewModel: NSObject, ObservableObject, WCSessionDelegate {
// 필수 WCSessionDelegate 메서드 구현
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
- print("WCSession 활성화 완료. 상태: \(activationState)")
+ print("MLWCSession 활성화 완료. 상태: \(activationState)")
}
func sessionDidBecomeInactive(_ session: WCSession) {
- print("WCSession 비활성화됨")
+ print("MLWCSession 비활성화됨")
}
func sessionDidDeactivate(_ session: WCSession) {
- print("WCSession 비활성화됨. 다시 활성화")
+ print("MLWCSession 비활성화됨. 다시 활성화")
WCSession.default.activate()
}
}
diff --git a/hearo/hearo/Sources/Presentations/Working/ViewModel/WorkingViewModel.swift b/hearo/hearo/Sources/Presentations/Working/ViewModel/WorkingViewModel.swift
index 93ed8a2..0fe573e 100644
--- a/hearo/hearo/Sources/Presentations/Working/ViewModel/WorkingViewModel.swift
+++ b/hearo/hearo/Sources/Presentations/Working/ViewModel/WorkingViewModel.swift
@@ -8,6 +8,7 @@ import Foundation
import Combine
import SwiftUI
import AVFoundation
+import AudioToolbox
@@ -64,5 +65,8 @@ class WorkingViewModel: ObservableObject {
func finishRecording() {
appRootManager.currentRoot = .finish
stopRecording()
+
+ triggerErrorHaptic()
}
}
+