Skip to content

Commit

Permalink
Merge pull request #124 from DeveloperAcademy-POSTECH/feat/#120-watch…
Browse files Browse the repository at this point in the history
…Connection

[Feat] watch 연동 구현
  • Loading branch information
strlla4536 authored Dec 4, 2024
2 parents 90f13ac + d7aeb82 commit 70b54da
Show file tree
Hide file tree
Showing 8 changed files with 329 additions and 233 deletions.
226 changes: 140 additions & 86 deletions hearo/HearoadWatch Watch App/WatchSessionManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,104 +9,158 @@ import WatchKit
import WatchConnectivity

class WatchSessionManager: NSObject, ObservableObject, WCSessionDelegate {
static let shared = WatchSessionManager() // 싱글톤 인스턴스 생성
static let shared = WatchSessionManager() // 싱글톤 인스턴스 생성

@Published var alertMessage: String = " " // 기본 메시지
@Published var isAlerting: Bool = false // 알림 상태 확인
@Published var isIOSConnected: Bool = false // iOS 연결 상태

private override init() {
super.init()

@Published var alertMessage: String = " " // 기본 메시지
@Published var isAlerting: Bool = false // 알림 상태 확인

private override init() {
super.init()

if WCSession.isSupported() {
WCSession.default.delegate = self
WCSession.default.activate()
}
if WCSession.isSupported() {
let session = WCSession.default
session.delegate = self
session.activate()
}
}

// MARK: - iOS에서 데이터 수신
// iOS에서 경고 메시지를 수신하는 메서드
func session(_ session: WCSession, didReceiveMessage message: [String: Any]) {
if let alert = message["alert"] as? String {
DispatchQueue.main.async {
self.showAlert(with: alert) // 메시지 수신 후 즉각적으로 알림 표시
print("애플워치 - 메시지 수신: \(alert)")
}
}
}

func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) {
if let alert = applicationContext["alert"] as? String {
DispatchQueue.main.async {
self.showAlert(with: alert)
print("워치 앱 - ApplicationContext 데이터 수신: \(alert)")
}
}

// iOS에서 경고 메시지를 수신하는 메서드
func session(_ session: WCSession, didReceiveMessage message: [String: Any]) {
if let alert = message["alert"] as? String {
DispatchQueue.main.async {
self.showAlert(with: alert) // 메시지 수신 후 즉각적으로 알림 표시
print("애플워치 - 메시지 수신: \(alert)")
}
}
if let highestConfidenceSound = applicationContext["highestConfidenceSound"] as? String {
DispatchQueue.main.async {
self.showAlert(with: highestConfidenceSound) // 수신한 소리를 알림으로 표시
print("애플워치 - applicationContext 데이터 수신: \(highestConfidenceSound)")
}
}
}

// iOS 연결 상태 변경 시 호출
func sessionReachabilityDidChange(_ session: WCSession) {
DispatchQueue.main.async {
self.isIOSConnected = session.isReachable
print("iOS 연결 상태 변경됨: \(session.isReachable ? "연결됨" : "연결되지 않음")")

// iOS로 연결 상태 변경 메시지 전송
self.sendMessageToIOS(key: "connectionStatus", value: session.isReachable ? "connected" : "disconnected")
}
}


// MARK: - iOS로 데이터 전송
func sendMessageToIOS(key: String, value: String) {
guard WCSession.default.activationState == .activated else {
print("WCSession이 활성화되지 않아 데이터 전송 실패")
return
}

// 이미지 이름을 반환하는 메서드
func alertImageName() -> String {
switch alertMessage {
case "Carhorn":
return "Car" // carhorn 이미지 이름
case "Siren":
return "Siren" // siren 이미지 이름
case "Bicyclebell":
return "Bicycle" // bicycle 이미지 이름
default:
return "Car" // 기본 알림 아이콘
}
if WCSession.default.isReachable {
let message = [key: value]
WCSession.default.sendMessage(message, replyHandler: nil) { error in
print("워치 앱에서 iOS로 메시지 전송 실패: \(error.localizedDescription)")
}
} else {
print("iOS 앱이 연결되지 않아 데이터 전송 실패")
}

// 3초 동안 알림 표시 후 기본 상태로 복구
func showAlert(with message: String) {
// "녹음 시작 전" 메시지는 무시
guard message != "녹음 시작 전" else {
print("메시지 무시: \(message)")
return
}

alertMessage = message
isAlerting = true

// 강한 진동 알림 발생
playUrgentHapticPattern()

// UI 업데이트: 빨간 배경, 아이콘 표시
DispatchQueue.main.async {
print("UI 업데이트 - 배경색: 빨간색, 메시지: \(message)")
}

// 3초 후에 기본 상태로 복구
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
self.resetAlert()
}
}

func updateApplicationContext(key: String, value: String) {
do {
try WCSession.default.updateApplicationContext([key: value])
print("워치 앱에서 iOS로 ApplicationContext 전송 성공")
} catch {
print("워치 앱에서 ApplicationContext 전송 실패: \(error.localizedDescription)")
}

// 긴급 상황을 위한 강한 진동 패턴
func playUrgentHapticPattern() {
// 반복 횟수 및 간격 설정
let repeatCount = 30 // 진동 반복 횟수
let interval: TimeInterval = 0.05 // 반복 간격 (0.1초)

// 반복적으로 강한 진동을 재생하는 패턴
for i in 0..<repeatCount {
DispatchQueue.main.asyncAfter(deadline: .now() + (interval * Double(i))) {
WKInterfaceDevice.current().play(.failure) // 강한 피드백을 주는 진동
}
}
}


// MARK: - 알림 표시 메서드
// 이미지 이름을 반환하는 메서드
func alertImageName() -> String {
switch alertMessage {
case "Carhorn":
return "Car" // carhorn 이미지 이름
case "Siren":
return "Siren" // siren 이미지 이름
case "Bicyclebell":
return "Bicycle" // bicycle 이미지 이름
default:
return "Car" // 기본 알림 아이콘
}
}

// 3초 동안 알림 표시 후 기본 상태로 복구
func showAlert(with message: String) {
// "녹음 시작 전" 메시지는 무시
guard message != "녹음 시작 전" else {
print("메시지 무시: \(message)")
return
}

alertMessage = message
isAlerting = true

// 기본 상태로 복구하는 메서드
func resetAlert() {
alertMessage = "인식중"
isAlerting = false
print("UI 상태 복구 - 배경색: 검정, 메시지: 기본 상태")
// 강한 진동 알림 발생
playUrgentHapticPattern()

// UI 업데이트: 빨간 배경, 아이콘 표시
DispatchQueue.main.async {
print("UI 업데이트 - 배경색: 빨간색, 메시지: \(message)")
}

// WCSession 활성화 완료 시 호출되는 메서드
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
print("애플워치 - WCSession 활성화 완료. 상태: \(activationState.rawValue)")
if let error = error {
print("애플워치 - 활성화 오류: \(error.localizedDescription)")
}
// 3초 후에 기본 상태로 복구
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
self.resetAlert()
}
}

// 긴급 상황을 위한 강한 진동 패턴
func playUrgentHapticPattern() {
// 반복 횟수 및 간격 설정
let repeatCount = 30 // 진동 반복 횟수
let interval: TimeInterval = 0.05 // 반복 간격 (0.1초)

func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) {
if let highestConfidenceSound = applicationContext["highestConfidenceSound"] as? String {
DispatchQueue.main.async {
self.showAlert(with: highestConfidenceSound) // 수신한 소리를 알림으로 표시
print("애플워치 - applicationContext 데이터 수신: \(highestConfidenceSound)")
}
}
// 반복적으로 강한 진동을 재생하는 패턴
for i in 0..<repeatCount {
DispatchQueue.main.asyncAfter(deadline: .now() + (interval * Double(i))) {
WKInterfaceDevice.current().play(.failure) // 강한 피드백을 주는 진동
}
}
}

// 기본 상태로 복구하는 메서드
func resetAlert() {
alertMessage = "인식중"
isAlerting = false
print("UI 상태 복구 - 배경색: 검정, 메시지: 기본 상태")
}

// MARK: - WCSessionDelegate 메서드
// WCSession 활성화 완료 시 호출되는 메서드
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
if activationState == .activated {
print("워치 앱에서 WCSession으로 활성화 성공")
}
if let error = error {
print("워치 앱에서 WCSession 활성화 실패: \(error.localizedDescription)")
}
}

}
28 changes: 14 additions & 14 deletions hearo/hearo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1056,7 +1056,7 @@
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "hearo/Preview\\ Content HearoadWatch\\ Watch\\ App";
DEVELOPMENT_TEAM = J5N8Y9F8Z8;
DEVELOPMENT_TEAM = 2H928F4Z7L;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = hearo/Info.plist;
Expand Down Expand Up @@ -1093,7 +1093,7 @@
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "hearo/Preview\\ Content HearoadWatch\\ Watch\\ App";
DEVELOPMENT_TEAM = J5N8Y9F8Z8;
DEVELOPMENT_TEAM = 2H928F4Z7L;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = hearo/Info.plist;
Expand Down Expand Up @@ -1128,7 +1128,7 @@
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = J5N8Y9F8Z8;
DEVELOPMENT_TEAM = 2H928F4Z7L;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 17.5;
MARKETING_VERSION = 1.0;
Expand All @@ -1147,7 +1147,7 @@
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = J5N8Y9F8Z8;
DEVELOPMENT_TEAM = 2H928F4Z7L;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 17.5;
MARKETING_VERSION = 1.0;
Expand All @@ -1165,7 +1165,7 @@
buildSettings = {
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = J5N8Y9F8Z8;
DEVELOPMENT_TEAM = 2H928F4Z7L;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.ada.hearoad.UITests;
Expand All @@ -1182,7 +1182,7 @@
buildSettings = {
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = J5N8Y9F8Z8;
DEVELOPMENT_TEAM = 2H928F4Z7L;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.ada.hearoad.UITests;
Expand All @@ -1202,7 +1202,7 @@
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "hearo HearoadWatch\\ Watch\\ App/Preview\\ Content";
DEVELOPMENT_TEAM = J5N8Y9F8Z8;
DEVELOPMENT_TEAM = 2H928F4Z7L;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "HearoadWatch-Watch-App-Info.plist";
Expand Down Expand Up @@ -1233,7 +1233,7 @@
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "hearo HearoadWatch\\ Watch\\ App/Preview\\ Content";
DEVELOPMENT_TEAM = J5N8Y9F8Z8;
DEVELOPMENT_TEAM = 2H928F4Z7L;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "HearoadWatch-Watch-App-Info.plist";
Expand Down Expand Up @@ -1262,7 +1262,7 @@
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = J5N8Y9F8Z8;
DEVELOPMENT_TEAM = 2H928F4Z7L;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.ada.hearoad.watchkitextension;
Expand All @@ -1282,7 +1282,7 @@
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = J5N8Y9F8Z8;
DEVELOPMENT_TEAM = 2H928F4Z7L;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.ada.hearoad.watchkitextension;
Expand All @@ -1301,7 +1301,7 @@
buildSettings = {
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = J5N8Y9F8Z8;
DEVELOPMENT_TEAM = 2H928F4Z7L;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = "com.ada.hearoad.Watch-Watch-AppUITests";
Expand All @@ -1320,7 +1320,7 @@
buildSettings = {
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = J5N8Y9F8Z8;
DEVELOPMENT_TEAM = 2H928F4Z7L;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = "com.ada.hearoad.Watch-Watch-AppUITests";
Expand All @@ -1341,7 +1341,7 @@
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = J5N8Y9F8Z8;
DEVELOPMENT_TEAM = 2H928F4Z7L;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = LiveActivity/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = LiveActivity;
Expand Down Expand Up @@ -1369,7 +1369,7 @@
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = J5N8Y9F8Z8;
DEVELOPMENT_TEAM = 2H928F4Z7L;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = LiveActivity/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = LiveActivity;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{
"images" : [
{
"filename" : "Icon 오후 9.05.06 3.png",
"filename" : "Icon 오후 9.05.06 3.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Icon 오후 9.05.06 4.png",
"filename" : "Icon 오후 9.05.06 4.png",
"idiom" : "universal",
"scale" : "2x"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"scale" : "2x"
},
{
"filename" : "시작하기.svg",
"filename" : "시작하기.svg",
"idiom" : "universal",
"scale" : "3x"
}
Expand Down
Loading

0 comments on commit 70b54da

Please sign in to comment.