From 6374b46aed57c357948916c9a4fa11bff0b58ee5 Mon Sep 17 00:00:00 2001 From: grace Date: Mon, 13 Mar 2023 21:19:03 +0900 Subject: [PATCH 1/8] Feat: send message --- Emotilt/Emotilt.xcodeproj/project.pbxproj | 10 ++- Emotilt/Emotilt/Info.plist | 10 +-- .../Emotilt/Network/MPCSessionManager.swift | 6 +- .../Emotilt/Network/PeerSessionManager.swift | 23 +++-- .../Emotilt/ViewModels/HomeViewModel.swift | 83 ++++++++++++++++++- Emotilt/Emotilt/Views/HomeView.swift | 25 +++++- 6 files changed, 135 insertions(+), 22 deletions(-) diff --git a/Emotilt/Emotilt.xcodeproj/project.pbxproj b/Emotilt/Emotilt.xcodeproj/project.pbxproj index 7885d2f..ab01efd 100644 --- a/Emotilt/Emotilt.xcodeproj/project.pbxproj +++ b/Emotilt/Emotilt.xcodeproj/project.pbxproj @@ -354,10 +354,11 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"Emotilt/Preview Content\""; - DEVELOPMENT_TEAM = H3J2ZWWZQV; + DEVELOPMENT_TEAM = 2CV8U244AV; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = Emotilt/Info.plist; + INFOPLIST_KEY_NSLocalNetworkUsageDescription = "Emotilt에서 로컬 네트워크를 이용하고자 합니다."; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; @@ -368,7 +369,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.peng.Emotilt; + PRODUCT_BUNDLE_IDENTIFIER = com.peng.Emotilted; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; @@ -388,10 +389,11 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"Emotilt/Preview Content\""; - DEVELOPMENT_TEAM = H3J2ZWWZQV; + DEVELOPMENT_TEAM = 2CV8U244AV; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = Emotilt/Info.plist; + INFOPLIST_KEY_NSLocalNetworkUsageDescription = "Emotilt에서 로컬 네트워크를 이용하고자 합니다."; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; @@ -402,7 +404,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.peng.Emotilt; + PRODUCT_BUNDLE_IDENTIFIER = com.peng.Emotilted; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; diff --git a/Emotilt/Emotilt/Info.plist b/Emotilt/Emotilt/Info.plist index e89e01e..ceac6a1 100644 --- a/Emotilt/Emotilt/Info.plist +++ b/Emotilt/Emotilt/Info.plist @@ -2,16 +2,14 @@ - SERVICE_NAME - $(SERVICE_NAME) - SERVICE_IDENTIFIER - $(SERVICE_IDENTIFIER) - NSLocalNetworkUsageDescription - Emotilt에서 로컬 네트워크를 이용하고자 합니다. NSBonjourServices $(BONJOUR_SERVICES) + SERVICE_IDENTIFIER + $(SERVICE_IDENTIFIER) + SERVICE_NAME + $(SERVICE_NAME) UIApplicationSceneManifest UIApplicationSupportsMultipleScenes diff --git a/Emotilt/Emotilt/Network/MPCSessionManager.swift b/Emotilt/Emotilt/Network/MPCSessionManager.swift index 73d72c3..b107a5a 100644 --- a/Emotilt/Emotilt/Network/MPCSessionManager.swift +++ b/Emotilt/Emotilt/Network/MPCSessionManager.swift @@ -71,21 +71,25 @@ class MPCSessionManager: NSObject { } /// Send message to a specific peer - func sendMessage(_ message: Message, to peerID: MCPeerID) { + func sendMessage(_ message: Message, to peerID: MCPeerID, completion: (Bool) -> ()) { if !session.connectedPeers.contains(peerID) { print("unconnected peer") + completion(false) return } guard let data = try? JSONEncoder().encode(message) else { // fail to encode Message into data + completion(false) return } do { try session.send(data, toPeers: [peerID], with: .reliable) + completion(true) } catch let error { print(error) + completion(false) } } } diff --git a/Emotilt/Emotilt/Network/PeerSessionManager.swift b/Emotilt/Emotilt/Network/PeerSessionManager.swift index ccc515e..cc7ca6c 100644 --- a/Emotilt/Emotilt/Network/PeerSessionManager.swift +++ b/Emotilt/Emotilt/Network/PeerSessionManager.swift @@ -53,19 +53,24 @@ class PeerSessionManager: NSObject { }.store(in: &bag) } - func sendMessageToNearestPeer(_ message: Message) { + //TODO: nearestPeerToken should be direction first, then distance + func sendMessageToNearestPeer(_ message: Message, completion: (Bool) -> ()) { guard let token = nearestPeerToken else { + completion(false) print("no nearest peer's token") return } - print("trying to send to \(token)") + //print("trying to send to \(token)") guard let peerID = peerList.first(where: { $0.token == token })?.id else { + completion(false) print("no matching peer in peerList") return } - mpcSessionManager.sendMessage(message, to: peerID) + mpcSessionManager.sendMessage(message, to: peerID) { success in + completion(success) + } } /// 새로운 Peer를 추가하고 연결받을 준비를 합니다. @@ -129,8 +134,16 @@ extension PeerSessionManager: NISessionDelegate { /// Sort nearbyObjects by direction and return the nearest object's discoveryToken private func getNearestPeer(from nearbyObjects: [NINearbyObject]) -> NIDiscoveryToken { - let directions = nearbyObjects.sorted { $0.distance ?? .zero < $1.distance ?? .zero } - return directions[0].discoveryToken + let directionFiltered = nearbyObjects.filter { $0.direction != nil } + if (!directionFiltered.isEmpty){ + let directionSorted = directionFiltered.sorted(by: { + norm_one($0.direction!) < norm_one($1.direction!) + }) + return directionSorted.first!.discoveryToken + }else{ + let distanceSorted = nearbyObjects.sorted { $0.distance ?? .zero < $1.distance ?? .zero } + return distanceSorted[0].discoveryToken + } } } diff --git a/Emotilt/Emotilt/ViewModels/HomeViewModel.swift b/Emotilt/Emotilt/ViewModels/HomeViewModel.swift index a02f738..d3ce0c6 100644 --- a/Emotilt/Emotilt/ViewModels/HomeViewModel.swift +++ b/Emotilt/Emotilt/ViewModels/HomeViewModel.swift @@ -6,6 +6,16 @@ // import Foundation +import CoreMotion +import UIKit + +enum viewState { + case sendingSuccess //메세지 보내기 성공 직후 + 5초 + case sendingFailure //메세지 보내기 실패 직후 + 5초 + case motionDetectFailure //장전 이후 모션 감지 실패 + case sendingTimer //장전 단계 + case none //디폴트 화면 +} class HomeViewModel: BaseViewModel, ObservableObject { /// 연결된 peer 목록 @@ -18,14 +28,83 @@ class HomeViewModel: BaseViewModel, ObservableObject { receivedMessage != nil } + //MARK: - 뷰 관련 값 + @Published var emoji : String = "" + @Published var content : String = "" + + //MARK: - 메세지 전송 관련 값 + let motionManager = CMMotionManager() + @Published var sendTimer: Timer? + @Published var currentState: viewState = .none + @Published var isSending: Bool = false + @Published var isAccelerating: Bool = false + @Published var accelerationRate: Double = 0.0 + @Published var counter: Int = 0 + @Published var isReadyForSending: Bool = false + var isDetected: Bool = false + override init(peerSessionManager: PeerSessionManager) { super.init(peerSessionManager: peerSessionManager) peerSessionManager.$peerList.assign(to: &$peerList) peerSessionManager.$receivedMessage.assign(to: &$receivedMessage) } +} + +//MARK: - message send logic +extension HomeViewModel { + + ///장전을 시작하고 모션을 감지함 + func detectAcceleration(){ + isDetected = false + isSending = true + currentState = .sendingTimer + + motionManager + .startAccelerometerUpdates(to: OperationQueue.current!, withHandler: { [self] (motion, error) in + isAccelerating = true + accelerationRate = (motion?.acceleration.x)! + 1 + if ((motion?.acceleration.x)! > 0.35){ + isDetected = true + motionManager.stopAccelerometerUpdates() + UINotificationFeedbackGenerator().notificationOccurred(.success) + sendMessage() + } + }) + startTimer() + } + + ///5초 카운트다운을 시작함 + func startTimer(){ + if (counter == 0){ counter = 5 } + sendTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { tempTimer in + if (self.counter > 0){ self.counter = 1 } + if (self.counter == 0 && !self.isDetected){ + self.currentState = .motionDetectFailure + self.stopTimer() + } + } + } + + func stopTimer(){ + DispatchQueue.main.asyncAfter(deadline: .now() + 3.0){ + self.currentState = .none + self.isSending = false + self.sendTimer?.invalidate() + self.sendTimer = nil + self.emoji = "" + self.content = "" + self.currentState = .none + self.motionManager.stopAccelerometerUpdates() + self.isAccelerating = false + } + } - func sendMessage(_ message: Message) { - peerSessionManager.sendMessageToNearestPeer(message) + func sendMessage(){ + let message = Message(emoji: emoji, content: content) + peerSessionManager.sendMessageToNearestPeer(message){ success in + self.currentState = (success) ? .sendingSuccess : .sendingFailure + } + stopTimer() } } diff --git a/Emotilt/Emotilt/Views/HomeView.swift b/Emotilt/Emotilt/Views/HomeView.swift index eb2ccb9..7e7542d 100644 --- a/Emotilt/Emotilt/Views/HomeView.swift +++ b/Emotilt/Emotilt/Views/HomeView.swift @@ -10,15 +10,31 @@ import SwiftUI struct HomeView: View { @ObservedObject var viewModel: HomeViewModel - @State private var isEmojiSheetOpen: Bool = false - @State private var emoji: String = "" - @State private var content: String = "" var body: some View { VStack(alignment: .center) { Spacer().frame(height: 24) + //MARK: - viewState label + switch (viewModel.currentState){ + case .sendingTimer: + Text("\(viewModel.counter)") + .font(.system(size: 40, weight: .bold)) + case .sendingSuccess: + Text("메세지 전송 성공!") + .font(.system(size: 20, weight: .bold)) + case .sendingFailure: + Text("메세지 전송 실패 ㅠ") + .font(.system(size: 20, weight: .bold)) + case .motionDetectFailure: + Text("메시지를 보내려면 흔들어주세요!") + .font(.system(size: 20, weight: .bold)) + //TODO: 흔드는 애니메이션이나 가이드 넣기 + case .none: + Text("") + } + if let message = viewModel.receivedMessage { Text(message.emoji) .font(.system(size: 42)) @@ -32,7 +48,8 @@ struct HomeView: View { Spacer() RoundedButton(label: "Send") { - viewModel.sendMessage(.init(emoji: "🤔", content: "Nyam")) + //viewModel.detectAcceleration() + viewModel.sendMessage() } Spacer().frame(height: 16) From 75cd0f30ab6dbd1b603e126f80e0ec8cb06f3082 Mon Sep 17 00:00:00 2001 From: grace Date: Mon, 13 Mar 2023 21:19:51 +0900 Subject: [PATCH 2/8] fix: bundle name --- Emotilt/Emotilt.xcodeproj/project.pbxproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Emotilt/Emotilt.xcodeproj/project.pbxproj b/Emotilt/Emotilt.xcodeproj/project.pbxproj index ab01efd..e7b8fcf 100644 --- a/Emotilt/Emotilt.xcodeproj/project.pbxproj +++ b/Emotilt/Emotilt.xcodeproj/project.pbxproj @@ -354,7 +354,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"Emotilt/Preview Content\""; - DEVELOPMENT_TEAM = 2CV8U244AV; + DEVELOPMENT_TEAM = ""; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = Emotilt/Info.plist; @@ -369,7 +369,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.peng.Emotilted; + PRODUCT_BUNDLE_IDENTIFIER = com.peng.Emotilt; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; @@ -389,7 +389,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"Emotilt/Preview Content\""; - DEVELOPMENT_TEAM = 2CV8U244AV; + DEVELOPMENT_TEAM = ""; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = Emotilt/Info.plist; @@ -404,7 +404,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.peng.Emotilted; + PRODUCT_BUNDLE_IDENTIFIER = com.peng.Emotilt; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; From 9070b422148147fa337da512e61530674f59a36c Mon Sep 17 00:00:00 2001 From: grace Date: Tue, 14 Mar 2023 13:03:03 +0900 Subject: [PATCH 3/8] fix team name --- Emotilt/Emotilt.xcodeproj/project.pbxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emotilt/Emotilt.xcodeproj/project.pbxproj b/Emotilt/Emotilt.xcodeproj/project.pbxproj index 437ef83..42dc168 100644 --- a/Emotilt/Emotilt.xcodeproj/project.pbxproj +++ b/Emotilt/Emotilt.xcodeproj/project.pbxproj @@ -405,7 +405,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"Emotilt/Preview Content\""; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = H3J2ZWWZQV; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = Emotilt/Info.plist; From eec49d875dd9474ff9b23df79bbbe8e0777bf7ce Mon Sep 17 00:00:00 2001 From: grace Date: Tue, 14 Mar 2023 14:07:45 +0900 Subject: [PATCH 4/8] fix ui --- Emotilt/Emotilt/Views/HomeView.swift | 119 +++++++++++++-------------- 1 file changed, 57 insertions(+), 62 deletions(-) diff --git a/Emotilt/Emotilt/Views/HomeView.swift b/Emotilt/Emotilt/Views/HomeView.swift index 992221c..a02015d 100644 --- a/Emotilt/Emotilt/Views/HomeView.swift +++ b/Emotilt/Emotilt/Views/HomeView.swift @@ -12,18 +12,12 @@ struct HomeView: View { @ObservedObject var viewModel: HomeViewModel @State private var isEmojiSheetOpen: Bool = false -<<<<<<< HEAD -======= - @State private var emoji: String = "" - @State private var content: String = "" @FocusState private var isTextFieldFocused: Bool ->>>>>>> main var body: some View { VStack(alignment: .center) { Spacer() -<<<<<<< HEAD //MARK: - viewState label switch (viewModel.currentState){ case .sendingTimer: @@ -38,86 +32,87 @@ struct HomeView: View { case .motionDetectFailure: Text("메시지를 보내려면 흔들어주세요!") .font(.system(size: 20, weight: .bold)) - //TODO: 흔드는 애니메이션이나 가이드 넣기 case .none: Text("") } - if let message = viewModel.receivedMessage { - Text(message.emoji) - .font(.system(size: 42)) -======= ZStack { Button { isEmojiSheetOpen = true } label: { ZStack { - if emoji.isEmpty { - RoundedRectangle(cornerRadius: 32) - .fill(.tertiary.opacity(0.4)) - .frame(width: 168, height: 168) + if viewModel.emoji.isEmpty { + RoundedRectangle(cornerRadius: 32) + .fill(.tertiary.opacity(0.4)) + .frame(width: 168, height: 168) + } + + Text(viewModel.emoji) + .font(.system(size: 168)) } - - Text(emoji) - .font(.system(size: 168)) } - } ->>>>>>> main + + + ZStack { + //DirectionIndicatorView() + } + }.frame(width: 280, height: 280) - ZStack { -// DirectionIndicatorView() - } - }.frame(width: 280, height: 280) - - Group { - TextField("", text: $content, axis: .vertical) - .placeholder(when: content.isEmpty && !isTextFieldFocused) { + Group { + if #available(iOS 16.0, *) { + TextField("", text: $viewModel + .content, axis: .vertical) + .placeholder(when: viewModel.content.isEmpty && !isTextFieldFocused) { Text("20자 이내") .foregroundColor(.gray) .opacity(0.8) - } - .focused($isTextFieldFocused) - .font(.system(size: 24, weight: .bold)) - .lineLimit(2) - .frame(height: 64) - .multilineTextAlignment(.center) - .onReceive(Just(content)) { _ in - if content.count > 20 { - content = String(content.prefix(20)) } - } - .submitLabel(.done) - .onChange(of: content) { text in - if text.last == "\n" { - content = String(text.dropLast()) - UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to:nil, from:nil, for:nil) + .focused($isTextFieldFocused) + .font(.system(size: 24, weight: .bold)) + .lineLimit(2) + .frame(height: 64) + .multilineTextAlignment(.center) + .onReceive(Just($viewModel.content)) { _ in + if viewModel.content.count > 20 { + viewModel.content = String(viewModel.content.prefix(20)) + } } + .submitLabel(.done) + .onChange(of: viewModel.content) { text in + if text.last == "\n" { + viewModel.content = String(text.dropLast()) + UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to:nil, from:nil, for:nil) + } + } + } else { + // Fallback on earlier versions } + } + + Spacer() + Spacer() + + RoundedButton(label: "Send") { + //viewModel.detectAcceleration() + viewModel.sendMessage() + } } - - Spacer() - Spacer() - - RoundedButton(label: "Send") { - //viewModel.detectAcceleration() - viewModel.sendMessage() - } - - } - .padding(.horizontal, 36) - .padding(.vertical, 24) - .onTapGesture(perform: { - UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to:nil, from:nil, for:nil) - }) - .sheet(isPresented: $isEmojiSheetOpen) { - EmojiSheetView(selected: $emoji) + .padding(.horizontal, 36) + .padding(.vertical, 24) + .onTapGesture(perform: { + UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to:nil, from:nil, for:nil) + }) + .sheet(isPresented: $isEmojiSheetOpen) { + EmojiSheetView(selected: $viewModel.emoji) + } } } -} - + + struct ContentView_Previews: PreviewProvider { static var previews: some View { HomeView(viewModel: .init(peerSessionManager: .debug)) } } + From 27c3ce7bcad589a1e659fedf84e9615623367474 Mon Sep 17 00:00:00 2001 From: grace Date: Tue, 14 Mar 2023 20:50:48 +0900 Subject: [PATCH 5/8] =?UTF-8?q?=EB=B7=B0=EB=AA=A8=EB=8D=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Emotilt/ViewModels/HomeViewModel.swift | 72 +------- Emotilt/Emotilt/Views/HomeView.swift | 158 +++++++++++++----- 2 files changed, 119 insertions(+), 111 deletions(-) diff --git a/Emotilt/Emotilt/ViewModels/HomeViewModel.swift b/Emotilt/Emotilt/ViewModels/HomeViewModel.swift index d3ce0c6..b71d74d 100644 --- a/Emotilt/Emotilt/ViewModels/HomeViewModel.swift +++ b/Emotilt/Emotilt/ViewModels/HomeViewModel.swift @@ -28,20 +28,7 @@ class HomeViewModel: BaseViewModel, ObservableObject { receivedMessage != nil } - //MARK: - 뷰 관련 값 - @Published var emoji : String = "" - @Published var content : String = "" - - //MARK: - 메세지 전송 관련 값 - let motionManager = CMMotionManager() - @Published var sendTimer: Timer? @Published var currentState: viewState = .none - @Published var isSending: Bool = false - @Published var isAccelerating: Bool = false - @Published var accelerationRate: Double = 0.0 - @Published var counter: Int = 0 - @Published var isReadyForSending: Bool = false - var isDetected: Bool = false override init(peerSessionManager: PeerSessionManager) { super.init(peerSessionManager: peerSessionManager) @@ -49,62 +36,15 @@ class HomeViewModel: BaseViewModel, ObservableObject { peerSessionManager.$peerList.assign(to: &$peerList) peerSessionManager.$receivedMessage.assign(to: &$receivedMessage) } -} - -//MARK: - message send logic -extension HomeViewModel { - - ///장전을 시작하고 모션을 감지함 - func detectAcceleration(){ - isDetected = false - isSending = true - currentState = .sendingTimer - - motionManager - .startAccelerometerUpdates(to: OperationQueue.current!, withHandler: { [self] (motion, error) in - isAccelerating = true - accelerationRate = (motion?.acceleration.x)! + 1 - if ((motion?.acceleration.x)! > 0.35){ - isDetected = true - motionManager.stopAccelerometerUpdates() - UINotificationFeedbackGenerator().notificationOccurred(.success) - sendMessage() - } - }) - startTimer() - } - - ///5초 카운트다운을 시작함 - func startTimer(){ - if (counter == 0){ counter = 5 } - sendTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { tempTimer in - if (self.counter > 0){ self.counter = 1 } - if (self.counter == 0 && !self.isDetected){ - self.currentState = .motionDetectFailure - self.stopTimer() - } - } - } - - func stopTimer(){ - DispatchQueue.main.asyncAfter(deadline: .now() + 3.0){ - self.currentState = .none - self.isSending = false - self.sendTimer?.invalidate() - self.sendTimer = nil - self.emoji = "" - self.content = "" - self.currentState = .none - self.motionManager.stopAccelerometerUpdates() - self.isAccelerating = false - } - } - func sendMessage(){ + func sendMessage(emoji: String, content: String, completion: (Bool) -> ()){ let message = Message(emoji: emoji, content: content) peerSessionManager.sendMessageToNearestPeer(message){ success in - self.currentState = (success) ? .sendingSuccess : .sendingFailure + completion(success) } - stopTimer() } } + + + + diff --git a/Emotilt/Emotilt/Views/HomeView.swift b/Emotilt/Emotilt/Views/HomeView.swift index a02015d..53b9d10 100644 --- a/Emotilt/Emotilt/Views/HomeView.swift +++ b/Emotilt/Emotilt/Views/HomeView.swift @@ -7,6 +7,7 @@ import SwiftUI import Combine +import CoreMotion struct HomeView: View { @@ -14,14 +15,28 @@ struct HomeView: View { @State private var isEmojiSheetOpen: Bool = false @FocusState private var isTextFieldFocused: Bool + //MARK: - 메세지 전송 관련 값 + let motionManager = CMMotionManager() + @State private var sendTimer: Timer? + @State private var currentState: viewState = .none + @State private var isSending: Bool = false + @State private var isAccelerating: Bool = false + @State private var accelerationRate: Double = 0.0 + @State private var counter: Int = 0 + @State private var isReadyForSending: Bool = false + @State var isDetected: Bool = false + + @State var emoji : String = "" + @State var content : String = "" + var body: some View { VStack(alignment: .center) { Spacer() //MARK: - viewState label - switch (viewModel.currentState){ + switch (currentState){ case .sendingTimer: - Text("\(viewModel.counter)") + Text("\(counter)") .font(.system(size: 40, weight: .bold)) case .sendingSuccess: Text("메세지 전송 성공!") @@ -41,28 +56,28 @@ struct HomeView: View { isEmojiSheetOpen = true } label: { ZStack { - if viewModel.emoji.isEmpty { - RoundedRectangle(cornerRadius: 32) - .fill(.tertiary.opacity(0.4)) - .frame(width: 168, height: 168) - } - - Text(viewModel.emoji) - .font(.system(size: 168)) + if emoji.isEmpty { + RoundedRectangle(cornerRadius: 32) + .fill(.tertiary.opacity(0.4)) + .frame(width: 168, height: 168) } + + Text(emoji) + .font(.system(size: 168)) } - - - ZStack { - //DirectionIndicatorView() - } - }.frame(width: 280, height: 280) + } + - Group { - if #available(iOS 16.0, *) { - TextField("", text: $viewModel - .content, axis: .vertical) - .placeholder(when: viewModel.content.isEmpty && !isTextFieldFocused) { + ZStack { + //DirectionIndicatorView() + } + }.frame(width: 280, height: 280) + + Group { + + if #available(iOS 16.0, *) { + TextField("", text: $content, axis: .vertical) + .placeholder(when: content.isEmpty && !isTextFieldFocused){ Text("20자 이내") .foregroundColor(.gray) .opacity(0.8) @@ -72,44 +87,97 @@ struct HomeView: View { .lineLimit(2) .frame(height: 64) .multilineTextAlignment(.center) - .onReceive(Just($viewModel.content)) { _ in - if viewModel.content.count > 20 { - viewModel.content = String(viewModel.content.prefix(20)) + .onReceive(Just($content)) { _ in + if content.count > 20 { + content = String(content.prefix(20)) } } .submitLabel(.done) - .onChange(of: viewModel.content) { text in + .onChange(of: content) { text in if text.last == "\n" { - viewModel.content = String(text.dropLast()) + content = String(text.dropLast()) UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to:nil, from:nil, for:nil) } } - } else { - // Fallback on earlier versions - } - - } - - Spacer() - Spacer() - - RoundedButton(label: "Send") { - //viewModel.detectAcceleration() - viewModel.sendMessage() + } else { + // Fallback on earlier versions } } - .padding(.horizontal, 36) - .padding(.vertical, 24) - .onTapGesture(perform: { - UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to:nil, from:nil, for:nil) - }) - .sheet(isPresented: $isEmojiSheetOpen) { - EmojiSheetView(selected: $viewModel.emoji) + + Spacer() + Spacer() + + RoundedButton(label: "Send") { + detectAcceleration() + } + } + .padding(.horizontal, 36) + .padding(.vertical, 24) + .onTapGesture(perform: { + UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to:nil, from:nil, for:nil) + }) + .sheet(isPresented: $isEmojiSheetOpen) { + EmojiSheetView(selected: $emoji) + } + } +} + +//MARK: - message sending +extension HomeView { + func detectAcceleration(){ + self.isDetected = false + isSending = true + currentState = .sendingTimer + + motionManager + .startAccelerometerUpdates(to: OperationQueue.current!, withHandler: { [self] (motion, error) in + isAccelerating = true + accelerationRate = (motion?.acceleration.x)! + 1 + if ((motion?.acceleration.x)! > 0.35){ + isDetected = true + motionManager.stopAccelerometerUpdates() + UINotificationFeedbackGenerator().notificationOccurred(.success) + sendMessage() } + }) + startTimer() + } + + ///5초 카운트다운을 시작함 + func startTimer(){ + if (counter == 0){ counter = 5 } + sendTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { tempTimer in + if (self.counter > 0){ self.counter -= 1 } + if (self.counter == 0 && !self.isDetected){ + self.currentState = .motionDetectFailure + self.stopTimer() + } } } + func stopTimer(){ + DispatchQueue.main.asyncAfter(deadline: .now() + 3.0){ //3초 딜레이 + self.currentState = .none + self.isSending = false + self.sendTimer?.invalidate() + self.sendTimer = nil + self.emoji = "" + self.content = "" + self.currentState = .none + self.motionManager.stopAccelerometerUpdates() + self.isAccelerating = false + } + } + func sendMessage(){ + viewModel.sendMessage(emoji: emoji, content: content){ success in + self.currentState = (success) ? .sendingSuccess : .sendingFailure + } + stopTimer() + } +} + + struct ContentView_Previews: PreviewProvider { static var previews: some View { HomeView(viewModel: .init(peerSessionManager: .debug)) From 55630eb9f244e894ecaa366af6befc8aa2ccd3d7 Mon Sep 17 00:00:00 2001 From: grace Date: Thu, 23 Mar 2023 09:24:09 +0900 Subject: [PATCH 6/8] fix send message --- Emotilt/Emotilt.xcodeproj/project.pbxproj | 8 ++--- Emotilt/Emotilt/Info.plist | 2 ++ .../Emotilt/Network/MPCSessionManager.swift | 12 ++++--- .../Emotilt/Network/PeerSessionManager.swift | 32 ++++++++++--------- .../Emotilt/ViewModels/HomeViewModel.swift | 11 ++++--- Emotilt/Emotilt/Views/HomeView.swift | 10 +++--- 6 files changed, 41 insertions(+), 34 deletions(-) diff --git a/Emotilt/Emotilt.xcodeproj/project.pbxproj b/Emotilt/Emotilt.xcodeproj/project.pbxproj index 42dc168..a8e123a 100644 --- a/Emotilt/Emotilt.xcodeproj/project.pbxproj +++ b/Emotilt/Emotilt.xcodeproj/project.pbxproj @@ -370,7 +370,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"Emotilt/Preview Content\""; - DEVELOPMENT_TEAM = H3J2ZWWZQV; + DEVELOPMENT_TEAM = 2CV8U244AV; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = Emotilt/Info.plist; @@ -385,7 +385,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.peng.Emotilt; + PRODUCT_BUNDLE_IDENTIFIER = com.peng.Emotilted; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; @@ -405,7 +405,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"Emotilt/Preview Content\""; - DEVELOPMENT_TEAM = H3J2ZWWZQV; + DEVELOPMENT_TEAM = 2CV8U244AV; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = Emotilt/Info.plist; @@ -420,7 +420,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.peng.Emotilt; + PRODUCT_BUNDLE_IDENTIFIER = com.peng.Emotilted; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; diff --git a/Emotilt/Emotilt/Info.plist b/Emotilt/Emotilt/Info.plist index ceac6a1..0880ef2 100644 --- a/Emotilt/Emotilt/Info.plist +++ b/Emotilt/Emotilt/Info.plist @@ -2,6 +2,8 @@ + NSLocationWhenInUseUsageDescription + "Emotilt wants to connect!" NSBonjourServices $(BONJOUR_SERVICES) diff --git a/Emotilt/Emotilt/Network/MPCSessionManager.swift b/Emotilt/Emotilt/Network/MPCSessionManager.swift index b107a5a..6756c1a 100644 --- a/Emotilt/Emotilt/Network/MPCSessionManager.swift +++ b/Emotilt/Emotilt/Network/MPCSessionManager.swift @@ -17,7 +17,9 @@ class MPCSessionManager: NSObject { @Published var received: (MCPeerID, Data)? @Published var connectionState: (MCPeerID, MCSessionState)? + @Published var didSendMessage: Bool? + override init() { self.session = .init(peer: localPeerID, securityIdentity: nil, @@ -71,25 +73,25 @@ class MPCSessionManager: NSObject { } /// Send message to a specific peer - func sendMessage(_ message: Message, to peerID: MCPeerID, completion: (Bool) -> ()) { + func sendMessage(_ message: Message, to peerID: MCPeerID) { if !session.connectedPeers.contains(peerID) { print("unconnected peer") - completion(false) + didSendMessage = false return } guard let data = try? JSONEncoder().encode(message) else { // fail to encode Message into data - completion(false) + didSendMessage = false return } do { try session.send(data, toPeers: [peerID], with: .reliable) - completion(true) + didSendMessage = true } catch let error { print(error) - completion(false) + didSendMessage = false } } } diff --git a/Emotilt/Emotilt/Network/PeerSessionManager.swift b/Emotilt/Emotilt/Network/PeerSessionManager.swift index cc7ca6c..11e2d41 100644 --- a/Emotilt/Emotilt/Network/PeerSessionManager.swift +++ b/Emotilt/Emotilt/Network/PeerSessionManager.swift @@ -21,6 +21,9 @@ class PeerSessionManager: NSObject { /// 현재 내 로컬 디바이스와 가장 가까이 있는 기기의 discoveryToken @Published var nearestPeerToken: NIDiscoveryToken? + + /// 메세지 발신 성공 여부 + @Published var didSendMessage: Bool? private var bag = Set() @@ -51,26 +54,27 @@ class PeerSessionManager: NSObject { self?.receivedMessage = message } }.store(in: &bag) + + mpcSessionManager.$didSendMessage.receive(on: RunLoop.main).sink { [weak self] success in + self?.didSendMessage = success + }.store(in: &bag) } - //TODO: nearestPeerToken should be direction first, then distance - func sendMessageToNearestPeer(_ message: Message, completion: (Bool) -> ()) { + func sendMessageToNearestPeer(_ message: Message) { guard let token = nearestPeerToken else { - completion(false) + didSendMessage = false print("no nearest peer's token") return } - //print("trying to send to \(token)") guard let peerID = peerList.first(where: { $0.token == token })?.id else { - completion(false) + didSendMessage = false print("no matching peer in peerList") return } - mpcSessionManager.sendMessage(message, to: peerID) { success in - completion(success) - } + mpcSessionManager.sendMessage(message, to: peerID) + print("Message success!") } /// 새로운 Peer를 추가하고 연결받을 준비를 합니다. @@ -116,7 +120,6 @@ class PeerSessionManager: NSObject { print("no matching peer") return } - peer.token = token let config = NINearbyPeerConfiguration(peerToken: token) peer.session.run(config) @@ -130,18 +133,17 @@ extension PeerSessionManager: NISessionDelegate { nearestPeerToken = getNearestPeer(from: nearbyObjects) } - // MARK: Get nearest device - /// Sort nearbyObjects by direction and return the nearest object's discoveryToken private func getNearestPeer(from nearbyObjects: [NINearbyObject]) -> NIDiscoveryToken { let directionFiltered = nearbyObjects.filter { $0.direction != nil } - if (!directionFiltered.isEmpty){ - let directionSorted = directionFiltered.sorted(by: { + if !directionFiltered.isEmpty { + let directionSorted = directionFiltered.sorted { norm_one($0.direction!) < norm_one($1.direction!) - }) + } return directionSorted.first!.discoveryToken }else{ - let distanceSorted = nearbyObjects.sorted { $0.distance ?? .zero < $1.distance ?? .zero } + let distanceSorted = nearbyObjects.sorted { + $0.distance ?? .zero < $1.distance ?? .zero } return distanceSorted[0].discoveryToken } } diff --git a/Emotilt/Emotilt/ViewModels/HomeViewModel.swift b/Emotilt/Emotilt/ViewModels/HomeViewModel.swift index b71d74d..0ae46f8 100644 --- a/Emotilt/Emotilt/ViewModels/HomeViewModel.swift +++ b/Emotilt/Emotilt/ViewModels/HomeViewModel.swift @@ -24,6 +24,9 @@ class HomeViewModel: BaseViewModel, ObservableObject { /// 수신한 메시지 @Published var receivedMessage: Message? + /// 메세지 발신 성공 여부 + @Published var didSendMessage: Bool? + var didReceiveMessage: Bool { receivedMessage != nil } @@ -32,16 +35,14 @@ class HomeViewModel: BaseViewModel, ObservableObject { override init(peerSessionManager: PeerSessionManager) { super.init(peerSessionManager: peerSessionManager) - peerSessionManager.$peerList.assign(to: &$peerList) peerSessionManager.$receivedMessage.assign(to: &$receivedMessage) + peerSessionManager.$didSendMessage.assign(to: &$didSendMessage) } - func sendMessage(emoji: String, content: String, completion: (Bool) -> ()){ + func sendMessage(emoji: String, content: String){ let message = Message(emoji: emoji, content: content) - peerSessionManager.sendMessageToNearestPeer(message){ success in - completion(success) - } + peerSessionManager.sendMessageToNearestPeer(message) } } diff --git a/Emotilt/Emotilt/Views/HomeView.swift b/Emotilt/Emotilt/Views/HomeView.swift index 53b9d10..b0c0226 100644 --- a/Emotilt/Emotilt/Views/HomeView.swift +++ b/Emotilt/Emotilt/Views/HomeView.swift @@ -125,7 +125,7 @@ struct HomeView: View { //MARK: - message sending extension HomeView { func detectAcceleration(){ - self.isDetected = false + isDetected = false isSending = true currentState = .sendingTimer @@ -170,14 +170,14 @@ extension HomeView { } func sendMessage(){ - viewModel.sendMessage(emoji: emoji, content: content){ success in - self.currentState = (success) ? .sendingSuccess : .sendingFailure + viewModel.sendMessage(emoji: emoji, content: content) + if let didSendMessage = viewModel.didSendMessage { + currentState = didSendMessage ? .sendingSuccess : .sendingFailure + stopTimer() } - stopTimer() } } - struct ContentView_Previews: PreviewProvider { static var previews: some View { HomeView(viewModel: .init(peerSessionManager: .debug)) From f78b57a4bc6c8c4992ef6983622ab9353ce225fd Mon Sep 17 00:00:00 2001 From: grace Date: Thu, 23 Mar 2023 09:33:35 +0900 Subject: [PATCH 7/8] fix bundle name --- Emotilt/Emotilt.xcodeproj/project.pbxproj | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Emotilt/Emotilt.xcodeproj/project.pbxproj b/Emotilt/Emotilt.xcodeproj/project.pbxproj index a8e123a..2de88b6 100644 --- a/Emotilt/Emotilt.xcodeproj/project.pbxproj +++ b/Emotilt/Emotilt.xcodeproj/project.pbxproj @@ -370,11 +370,12 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"Emotilt/Preview Content\""; - DEVELOPMENT_TEAM = 2CV8U244AV; + DEVELOPMENT_TEAM = H3J2ZWWZQV; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = Emotilt/Info.plist; INFOPLIST_KEY_NSLocalNetworkUsageDescription = "Emotilt에서 로컬 네트워크를 이용하고자 합니다."; + INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "\"Emotilt wants to connect!\""; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; @@ -385,7 +386,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.peng.Emotilted; + PRODUCT_BUNDLE_IDENTIFIER = com.peng.Emotilt; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; @@ -405,11 +406,12 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"Emotilt/Preview Content\""; - DEVELOPMENT_TEAM = 2CV8U244AV; + DEVELOPMENT_TEAM = H3J2ZWWZQV; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = Emotilt/Info.plist; INFOPLIST_KEY_NSLocalNetworkUsageDescription = "Emotilt에서 로컬 네트워크를 이용하고자 합니다."; + INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "\"Emotilt wants to connect!\""; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; @@ -420,7 +422,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.peng.Emotilted; + PRODUCT_BUNDLE_IDENTIFIER = com.peng.Emotilt; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; From 4c8d7c80534673fd42dc83deb0adeea70f2954b3 Mon Sep 17 00:00:00 2001 From: grace Date: Wed, 5 Apr 2023 20:38:14 +0900 Subject: [PATCH 8/8] change to swiftUI style --- Emotilt/Emotilt/Info.plist | 2 - .../Emotilt/Network/PeerSessionManager.swift | 7 +- .../Emotilt/ViewModels/HomeViewModel.swift | 2 +- Emotilt/Emotilt/Views/HomeView.swift | 72 ++++++++++--------- 4 files changed, 45 insertions(+), 38 deletions(-) diff --git a/Emotilt/Emotilt/Info.plist b/Emotilt/Emotilt/Info.plist index 0880ef2..ceac6a1 100644 --- a/Emotilt/Emotilt/Info.plist +++ b/Emotilt/Emotilt/Info.plist @@ -2,8 +2,6 @@ - NSLocationWhenInUseUsageDescription - "Emotilt wants to connect!" NSBonjourServices $(BONJOUR_SERVICES) diff --git a/Emotilt/Emotilt/Network/PeerSessionManager.swift b/Emotilt/Emotilt/Network/PeerSessionManager.swift index 11e2d41..0a3bcef 100644 --- a/Emotilt/Emotilt/Network/PeerSessionManager.swift +++ b/Emotilt/Emotilt/Network/PeerSessionManager.swift @@ -141,10 +141,11 @@ extension PeerSessionManager: NISessionDelegate { norm_one($0.direction!) < norm_one($1.direction!) } return directionSorted.first!.discoveryToken - }else{ + } else { let distanceSorted = nearbyObjects.sorted { - $0.distance ?? .zero < $1.distance ?? .zero } - return distanceSorted[0].discoveryToken + $0.distance ?? .zero < $1.distance ?? .zero + } + return distanceSorted.first!.discoveryToken } } } diff --git a/Emotilt/Emotilt/ViewModels/HomeViewModel.swift b/Emotilt/Emotilt/ViewModels/HomeViewModel.swift index 0ae46f8..1f788e6 100644 --- a/Emotilt/Emotilt/ViewModels/HomeViewModel.swift +++ b/Emotilt/Emotilt/ViewModels/HomeViewModel.swift @@ -1,4 +1,4 @@ -// +///Users/grace/Library/Mobile Documents/com~apple~CloudDocs/Desktop/EmoTilt_Xcode13.4/EmoTilt_Xcode13.4/Config.xcconfig // HomeViewModel.swift // Emotilt // diff --git a/Emotilt/Emotilt/Views/HomeView.swift b/Emotilt/Emotilt/Views/HomeView.swift index b0c0226..d8187e1 100644 --- a/Emotilt/Emotilt/Views/HomeView.swift +++ b/Emotilt/Emotilt/Views/HomeView.swift @@ -19,12 +19,12 @@ struct HomeView: View { let motionManager = CMMotionManager() @State private var sendTimer: Timer? @State private var currentState: viewState = .none - @State private var isSending: Bool = false - @State private var isAccelerating: Bool = false - @State private var accelerationRate: Double = 0.0 - @State private var counter: Int = 0 - @State private var isReadyForSending: Bool = false - @State var isDetected: Bool = false + @State private var isSending = false + @State private var isAccelerating = false + @State private var accelerationRate = 0.0 + @State private var counter = 0 + @State private var isReadyForSending = false + @State var isDetected = false @State var emoji : String = "" @State var content : String = "" @@ -34,7 +34,7 @@ struct HomeView: View { Spacer() //MARK: - viewState label - switch (currentState){ + switch currentState { case .sendingTimer: Text("\(counter)") .font(.system(size: 40, weight: .bold)) @@ -48,7 +48,7 @@ struct HomeView: View { Text("메시지를 보내려면 흔들어주세요!") .font(.system(size: 20, weight: .bold)) case .none: - Text("") + EmptyView() } ZStack { @@ -124,55 +124,63 @@ struct HomeView: View { //MARK: - message sending extension HomeView { - func detectAcceleration(){ + func detectAcceleration() { isDetected = false isSending = true currentState = .sendingTimer - motionManager - .startAccelerometerUpdates(to: OperationQueue.current!, withHandler: { [self] (motion, error) in + motionManager.startAccelerometerUpdates(to: OperationQueue.current!) { [self] motion, error in + withAnimation { isAccelerating = true accelerationRate = (motion?.acceleration.x)! + 1 - if ((motion?.acceleration.x)! > 0.35){ + if (motion?.acceleration.x)! > 0.35 { isDetected = true motionManager.stopAccelerometerUpdates() UINotificationFeedbackGenerator().notificationOccurred(.success) sendMessage() } - }) + } + } + startTimer() } ///5초 카운트다운을 시작함 - func startTimer(){ - if (counter == 0){ counter = 5 } + func startTimer() { + if counter == 0 { counter = 5 } sendTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { tempTimer in - if (self.counter > 0){ self.counter -= 1 } - if (self.counter == 0 && !self.isDetected){ - self.currentState = .motionDetectFailure - self.stopTimer() + withAnimation { + if self.counter > 0 { self.counter -= 1 } + if self.counter == 0 && !self.isDetected { + self.currentState = .motionDetectFailure + self.stopTimer() + } } } } - func stopTimer(){ - DispatchQueue.main.asyncAfter(deadline: .now() + 3.0){ //3초 딜레이 - self.currentState = .none - self.isSending = false - self.sendTimer?.invalidate() - self.sendTimer = nil - self.emoji = "" - self.content = "" - self.currentState = .none - self.motionManager.stopAccelerometerUpdates() - self.isAccelerating = false + func stopTimer() { + DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) { // 3초 딜레이 + withAnimation { + self.currentState = .none + self.isSending = false + self.sendTimer?.invalidate() + self.sendTimer = nil + self.emoji = "" + self.content = "" + self.currentState = .none + self.motionManager.stopAccelerometerUpdates() + self.isAccelerating = false + } } } - func sendMessage(){ + func sendMessage() { viewModel.sendMessage(emoji: emoji, content: content) if let didSendMessage = viewModel.didSendMessage { - currentState = didSendMessage ? .sendingSuccess : .sendingFailure + withAnimation { + currentState = didSendMessage ? .sendingSuccess : .sendingFailure + } stopTimer() } }