Skip to content

Commit

Permalink
Merge pull request #226 from APP-iOS2/refactor/timerView
Browse files Browse the repository at this point in the history
Refactor/timerview
  • Loading branch information
hyeonghwan authored Jan 16, 2024
2 parents c77e10f + 73ed7fd commit a095bf4
Show file tree
Hide file tree
Showing 14 changed files with 393 additions and 413 deletions.
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

0 comments on commit a095bf4

Please sign in to comment.