Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Refactor/timerView] TimerView 컴포넌트 래픽토링 #225

Closed
wants to merge 11 commits into from
20 changes: 20 additions & 0 deletions Pickle/Pickle.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,9 @@
96BF92932AC27EAC00277131 /* RealmSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 96BF92922AC27EAC00277131 /* RealmSwift */; settings = {ATTRIBUTES = (Required, ); }; };
96C64FE32ADC2C450042204A /* CustomSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96C64FE22ADC2C450042204A /* CustomSheetView.swift */; };
96C716DB2AC1614F00B4B8E0 /* RegisterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96C716DA2AC1614F00B4B8E0 /* RegisterView.swift */; };
96C83B902B55915A00262F19 /* TimerTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96C83B8F2B55915A00262F19 /* TimerTitleView.swift */; };
96C83B922B5591BC00262F19 /* CircleTimerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96C83B912B5591BC00262F19 /* CircleTimerView.swift */; };
96C83B942B5598F300262F19 /* TimerCompleteButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96C83B932B5598F300262F19 /* TimerCompleteButton.swift */; };
96CE29C32ADE63A40070EF92 /* PizzaObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96CE29C22ADE63A40070EF92 /* PizzaObject.swift */; };
96CE29C52ADE66B50070EF92 /* PizzaRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96CE29C42ADE66B50070EF92 /* PizzaRepository.swift */; };
96CE29C72ADEC6560070EF92 /* PizzaStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96CE29C62ADEC6560070EF92 /* PizzaStore.swift */; };
Expand Down Expand Up @@ -424,6 +427,9 @@
96B7AE6C2B023DFC001A3987 /* CurrentPizzaObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrentPizzaObject.swift; sourceTree = "<group>"; };
96C64FE22ADC2C450042204A /* CustomSheetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomSheetView.swift; sourceTree = "<group>"; };
96C716DA2AC1614F00B4B8E0 /* RegisterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegisterView.swift; sourceTree = "<group>"; };
96C83B8F2B55915A00262F19 /* TimerTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimerTitleView.swift; sourceTree = "<group>"; };
96C83B912B5591BC00262F19 /* CircleTimerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircleTimerView.swift; sourceTree = "<group>"; };
96C83B932B5598F300262F19 /* TimerCompleteButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimerCompleteButton.swift; sourceTree = "<group>"; };
96CE29C22ADE63A40070EF92 /* PizzaObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PizzaObject.swift; sourceTree = "<group>"; };
96CE29C42ADE66B50070EF92 /* PizzaRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PizzaRepository.swift; sourceTree = "<group>"; };
96CE29C62ADEC6560070EF92 /* PizzaStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PizzaStore.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1068,6 +1074,16 @@
path = Dependency;
sourceTree = "<group>";
};
96C83B8E2B5590E100262F19 /* Component */ = {
isa = PBXGroup;
children = (
96C83B8F2B55915A00262F19 /* TimerTitleView.swift */,
96C83B932B5598F300262F19 /* TimerCompleteButton.swift */,
96C83B912B5591BC00262F19 /* CircleTimerView.swift */,
);
path = Component;
sourceTree = "<group>";
};
96C872C92AE0BD85007BD155 /* MissionView */ = {
isa = PBXGroup;
children = (
Expand All @@ -1094,6 +1110,7 @@
children = (
2137D58E2AC1476000AFE225 /* TimerView.swift */,
A0052FB32ACD48A3005B3263 /* TimerReportView.swift */,
96C83B8E2B5590E100262F19 /* Component */,
);
path = TimerView;
sourceTree = "<group>";
Expand Down Expand Up @@ -1350,6 +1367,7 @@
960C41922ACE41E1009A0093 /* Object+Extensions.swift in Sources */,
968EB22D2AEE63110025BEF8 /* NavigationStore.swift in Sources */,
2137D58F2AC1476000AFE225 /* TimerView.swift in Sources */,
96C83B942B5598F300262F19 /* TimerCompleteButton.swift in Sources */,
962FFD8F2AF7979C00AE4963 /* Realm+Async.swift in Sources */,
96CF08EE2AD933A400648B59 /* DotCircleView.swift in Sources */,
96635F652ADF9F380072F21E /* Status.swift in Sources */,
Expand Down Expand Up @@ -1388,6 +1406,7 @@
962FFD842AF6537D00AE4963 /* PickleTestApp.swift in Sources */,
96A1D4A32ACFD97A003BC207 /* BehaviorMissionRepository.swift in Sources */,
2137D5A82AC14C7A00AFE225 /* Font.swift in Sources */,
96C83B902B55915A00262F19 /* TimerTitleView.swift in Sources */,
A0052FB42ACD48A3005B3263 /* TimerReportView.swift in Sources */,
2137D58D2AC1475800AFE225 /* UpdateTodoView.swift in Sources */,
96AAF8322AFD509200410881 /* Mediator.swift in Sources */,
Expand Down Expand Up @@ -1426,6 +1445,7 @@
961296962AC16FBE009A9815 /* BackColorModifier.swift in Sources */,
21FA26772AC2A10800877122 /* Todo.swift in Sources */,
96AAF8302AFD3A7B00410881 /* Dictionary.swift in Sources */,
96C83B922B5591BC00262F19 /* CircleTimerView.swift in Sources */,
96A1D4AA2AD176B6003BC207 /* Injection+Key.swift in Sources */,
96B7AE6D2B023DFC001A3987 /* CurrentPizzaObject.swift in Sources */,
96CE29C32ADE63A40070EF92 /* PizzaObject.swift in Sources */,
Expand Down
23 changes: 23 additions & 0 deletions Pickle/Pickle/Extension/Date+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,27 @@ extension Date {
return month
}
}

static func convertSecondsToTime(timeInSecond: TimeInterval) -> String {
let hours: Int = Int(timeInSecond / 3600)
let minutes: Int = Int(timeInSecond - Double(hours) * 3600) / 60
let seconds: Int = Int(timeInSecond.truncatingRemainder(dividingBy: 60))

if timeInSecond >= 3600 {
return String(format: "%02i:%02i:%02i", hours, minutes, seconds)
} else {
return String(format: "%02i:%02i", minutes, seconds)
}
}

static func convertTargetTimeToString(timeInSecond: TimeInterval) -> String {
let hours: Int = Int(timeInSecond / 3600)
let minutes: Int = Int(timeInSecond - Double(hours) * 3600) / 60

if timeInSecond >= 3600 {
return String(format: "%i시간 %i분", hours, minutes)
} else {
return String(format: "%i분", minutes)
}
}
}
10 changes: 0 additions & 10 deletions Pickle/Pickle/Screen/App/MySceneDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,3 @@
import Foundation
import UIKit

class MySceneDelegate: UIResponder, UIWindowSceneDelegate {
func sceneDidEnterBackground(_ scene: UIScene) {
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
appDelegate.scheduleAppRefresh()
appDelegate.scheduleProcessingTaskIfNeeded()
} else {
Log.error("AppDelegate를 찾을 수 없습니다.")
}
}
}
91 changes: 6 additions & 85 deletions Pickle/Pickle/Screen/App/PickleApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,78 +20,8 @@ class AppDelegate: NSObject, UIApplicationDelegate {
PickleApp.setUpDependency()
let _ = RealmMigrator()

// App Refresh Task
BGTaskScheduler.shared.register(forTaskWithIdentifier: "com.ddudios.realpizza.refresh_badge", using: nil) { task in
self.handleAppRefresh(task: task as! BGAppRefreshTask)
}

// Processing Task
BGTaskScheduler.shared.register(forTaskWithIdentifier: "com.ddudios.realpizza.refresh_process", using: nil) { task in
self.handleProcessingTask(task: task as! BGProcessingTask) // 타입 캐스팅 유의 (BG'Processing'Task)
}

return true
}

func handleAppRefresh(task: BGAppRefreshTask) {
// 다음 동작 수행, 반복시 필요
scheduleAppRefresh()

task.expirationHandler = {
task.setTaskCompleted(success: false)
}

// 가벼운 백그라운드 작업 작성
task.setTaskCompleted(success: false)
}

func handleProcessingTask(task: BGProcessingTask) {
task.expirationHandler = {
task.setTaskCompleted(success: false)
}

// 무거운 백그라운드 작업 작성
task.setTaskCompleted(success: true)
}

func scheduleAppRefresh() {
let request = BGAppRefreshTaskRequest(identifier: "com.ddudios.realpizza.refresh_badge")

do {
try BGTaskScheduler.shared.submit(request)
} catch {
Log.error("\(Date()): Could not schedule app refresh: \(error)")
}
}

func scheduleProcessingTaskIfNeeded() {

let request = BGProcessingTaskRequest(identifier: "com.ddudios.realpizza.refresh_process")
request.requiresExternalPower = false
request.requiresNetworkConnectivity = false

do {
try BGTaskScheduler.shared.submit(request)
} catch {
Log.error("\(Date()): Could not schedule processing task: \(error)")
}

guard missionStore.timeMissions[0].date.format("yyyy-MM-dd") != Date().format("yyyy-MM-dd") else { return }

BGTaskScheduler.shared.register(forTaskWithIdentifier: "com.ddudios.realpizza.refresh_process", using: nil) { task in
self.handleProcessingTask(task: task as! BGProcessingTask)
}
}

func application(_ application: UIApplication,
configurationForConnecting connectingSceneSession: UISceneSession,
options: UIScene.ConnectionOptions) -> UISceneConfiguration {
let sceneConfiguration = UISceneConfiguration(name: nil, sessionRole: connectingSceneSession.role)

sceneConfiguration.delegateClass = MySceneDelegate.self

return sceneConfiguration
}
}

struct PickleApp: App {
Expand Down Expand Up @@ -129,7 +59,7 @@ struct PickleApp: App {
Text("value ")
} else {
ContentView()
.onAppear { Log.error("contentVIew onAppear"); debugDelete.toggle() } // 내부의 contentView onApper 보다 늦게 실행됨 Debug Delete
.onAppear { debugDelete.toggle() } // 내부의 contentView onApper 보다 늦게 실행됨 Debug Delete
.environmentObject(todoStore)
.environmentObject(missionStore)
.environmentObject(userStore)
Expand All @@ -148,7 +78,6 @@ struct PickleApp: App {
private func backgroundEvent(newScene: ScenePhase) {

if newScene == .background {

backgroundNumber += 1

timerVM.activeNumber += 1
Expand All @@ -161,32 +90,24 @@ struct PickleApp: App {
timerVM.backgroundSpendTime = timerVM.spendTime
timerVM.backgroundTimeExtra = timerVM.timeExtra
}

}

if newScene == .active {
#if DEBUG
print("ACTIVE")

print("activeNumber: \(timerVM.activeNumber)")
print("backgroundNumber: \(backgroundNumber)")
print("isRunTimer: \(isRunTimer)")
#endif
Log.debug("ACTIVE")
Log.debug("activeNumber: \(timerVM.activeNumber)")
Log.debug("backgroundNumber: \(backgroundNumber)")
Log.debug("isRunTimer: \(isRunTimer)")

if isRunTimer {

if timerVM.activeNumber != backgroundNumber {

timerVM.todo = todoStore.getSeletedTodo(id: todoId)
timerVM.showOngoingAlert = true

}

if timerVM.fromBackground {

timerVM.makeRandomSaying()
var currentTime: Date = Date()
var diff = currentTime.timeIntervalSince(timerVM.backgroundTimeStemp)

timerVM.timeRemaining = timerVM.backgroundTimeRemain
timerVM.spendTime = timerVM.backgroundSpendTime
timerVM.spendTime += diff
Expand Down
1 change: 0 additions & 1 deletion Pickle/Pickle/Screen/Home/HomeView/View/HomeView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ struct HomeView: View {
typealias PizzaSelection = PizzaSelectedView.Selection
typealias TodoSelection = UpdateTodoView.Selection
typealias TimerSelection = TodoCellView.Selection
typealias PizzaImage = String

init() {
navigationAppearenceSetting()
Expand Down
142 changes: 142 additions & 0 deletions Pickle/Pickle/Screen/Home/TimerView/Component/CircleTimerView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
//
// CircleTimerView.swift
// Pickle
//
// Created by 박형환 on 1/16/24.
//

import SwiftUI

struct CircleTimerView: View {

@EnvironmentObject var notificationManager: NotificationManager
@EnvironmentObject var todoStore: TodoStore
@EnvironmentObject var timerVM: TimerViewModel

var todo: Todo
private let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
private let completeLimit: TimeInterval = 5 * 60 // 5분 이후
@Binding var state: TimerView.TimerState
@Binding var isRunTimer: Bool
@Binding var backgroundNumber: Int

var body: some View {
ZStack {
Circle()
.fill(.clear)
.frame(width: .screenWidth * 0.75)
.overlay(Circle().stroke(.tertiary, lineWidth: 5))
Circle()
.trim(from: 0, to: progress())
.stroke(Color.pickle, style: StrokeStyle(lineWidth: 5, lineCap: .round))
.frame(width: .screenWidth * 0.75)
.rotationEffect(.degrees(-90))

if state.isStart {
if timerVM.timeRemaining > 0 {
Text(String(format: "%g", timerVM.timeRemaining))
.foregroundColor(.pickle)
.font(.pizzaTimerNum)
.onReceive(timer) { _ in
timerVM.timeRemaining -= 1
}
} else {
Text("시작")
.foregroundColor(.pickle)
.font(.pizzaTimerNum)
.onReceive(timer) { value in
calcRemain()
}
}
} else {
if timerVM.isDecresing {
// 남은시간 줄어드는 타이머
decreasingView
} else {
// 추가시간 늘어나는 타이머
increasingView
}

// 목표시간 명시
Text(Date.convertTargetTimeToString(timeInSecond: todo.targetTime))
.font(.pizzaRegularSmallTitle)
.foregroundColor(.secondary)
.offset(y: 40)
}
}
}

// 남은시간 줄어드는 타이머
private var decreasingView: some View {
Text(Date.convertSecondsToTime(timeInSecond: timerVM.timeRemaining))
.foregroundColor(.pickle)
.font(.pizzaTimerNum)
.onReceive(timer) { _ in
if !state.isComplete || timerVM.isPuase {
timerVM.timeRemaining -= 1

timerVM.spendTime += 1

if timerVM.spendTime > completeLimit { state.isDisabled = false }
if timerVM.timeRemaining <= 0 { turnMode() }
}
}
}

// 추가시간 늘어나는 타이머
private var increasingView: some View {
HStack {
Text("+ \(Date.convertSecondsToTime(timeInSecond: timerVM.timeExtra))")
.foregroundColor(.pickle)
.font(.pizzaTimerNum)
.onReceive(timer) { _ in
// disabled가 풀리기 전에 background 갔다가 오는 경우를 위해
if timerVM.spendTime > completeLimit {
state.isDisabled = false
}
if (!state.isStart && !state.isComplete) || timerVM.isPuase {
timerVM.timeExtra += 1
timerVM.spendTime += 1
}
}
}
}

private func progress() -> CGFloat {
if state.isStart {
return CGFloat(0)
} else {
if timerVM.isDecresing {
return (CGFloat(state.settingTime - timerVM.timeRemaining) / CGFloat(state.settingTime))
} else {
return 1
}
}
}

// 남은 시간 계산하기
// 시작 시 시간시간 업데이트, status ongoing으로
private func calcRemain() {
state.isStart = false

timerVM.onGoingStart(todoStore)
state.realStartTime = Date()
backgroundNumber = 1
isRunTimer = true

state.settingTime = todo.targetTime
timerVM.timeRemaining = state.settingTime
timerVM.spendTime = 0
}



/// 지정해놓은 시간 이 지났을때 decreasing mode 에서 -> increasing mode로 변경
private func turnMode() {
timerVM.isDecresing = false
Task {
try await notificationManager.requestNotiAuthorization()
notificationManager.timerViewPushSetting(LocalNotification.timer)
}
}
}
Loading
Loading