Skip to content

Commit

Permalink
Merge pull request #28 from Team-HMH/feat/#21-ChallengeView
Browse files Browse the repository at this point in the history
[Feat/#21] 챌린지뷰 구현 및 스크린타임 API 연결
  • Loading branch information
Zoe0929 authored May 14, 2024
2 parents 91c9369 + 18f6b12 commit 90bad1c
Show file tree
Hide file tree
Showing 27 changed files with 743 additions and 86 deletions.
33 changes: 33 additions & 0 deletions HMH_iOS/HMHDeviceActivityReport/ActivityModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// ActivityModel.swift
// HMHDeviceActivityReport
//
// Created by 이지희 on 5/12/24.
//

import Foundation

import ManagedSettings

struct ActivityReport {
let totalDuration: TimeInterval
let apps: [AppDeviceActivity]
}

struct AppDeviceActivity: Identifiable {
var id: String
var displayName: String
var duration: TimeInterval
var numberOfPickups: Int
var token: ApplicationToken?
}

extension TimeInterval {
/// TimeInterval 타입 값을 00:00 형식의 String으로 변환해주는 메서드
func toString() -> String {
let time = NSInteger(self)
let minutes = (time / 60) % 60
let hours = (time / 3600)
return String(format: "%0.2d시간 %0.2d분", hours,minutes)
}
}
52 changes: 52 additions & 0 deletions HMH_iOS/HMHDeviceActivityReport/AppActivityReport.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//
// AppActivityReport.swift
// HMHDeviceActivityReport
//
// Created by 이지희 on 5/13/24.
//


import SwiftUI
import DeviceActivity

struct AppActivityReport: DeviceActivityReportScene {
let context : DeviceActivityReport.Context = .appActivity
let content: (ActivityReport) -> AppActivityView

func makeConfiguration(representing data: DeviceActivityResults<DeviceActivityData>) async -> ActivityReport {

var totalActivityDuration: Double = 0
var list: [AppDeviceActivity] = []

/// DeviceActivityResults 데이터에서 화면에 보여주기 위해 필요한 내용을 추출해줍니다.
for await eachData in data {
/// 특정 시간 간격 동안 사용자의 활동
for await activitySegment in eachData.activitySegments {
/// 활동 세그먼트 동안 사용자의 카테고리 별 Device Activity
for await categoryActivity in activitySegment.categories {
/// 이 카테고리의 totalActivityDuration에 기여한 사용자의 application Activity
for await applicationActivity in categoryActivity.applications {
let appName = (applicationActivity.application.localizedDisplayName ?? "nil") /// 앱 이름
let bundle = (applicationActivity.application.bundleIdentifier ?? "nil") /// 앱 번들id
let duration = applicationActivity.totalActivityDuration /// 앱의 total activity 기간
totalActivityDuration += duration
let numberOfPickups = applicationActivity.numberOfPickups /// 앱에 대해 직접적인 pickup 횟수
let token = applicationActivity.application.token /// 앱의 토큰
let appActivity = AppDeviceActivity(
id: bundle,
displayName: appName,
duration: duration,
numberOfPickups: numberOfPickups,
token: token
)
list.append(appActivity)
}
}

}
}

/// 필터링된 ActivityReport 데이터들을 반환
return ActivityReport(totalDuration: totalActivityDuration, apps: list)
}
}
84 changes: 57 additions & 27 deletions HMH_iOS/HMHDeviceActivityReport/AppActivityView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,38 +9,68 @@ import SwiftUI
import FamilyControls

struct AppActivityView: View {
let totalActivity: String

var activityReport: ActivityReport
var body: some View {
if let timeString = convertStringToTime(totalActivity) {
Text("\(timeString) 사용")
.font(.title2_semibold_24)
.foregroundStyle(.whiteText)
.frame(maxWidth: .infinity, alignment: .leading)
Text("앱별 사용시간")
VStack(spacing: 4) {
List {
Section {
ForEach(activityReport.apps) { eachApp in
ListRow(eachApp: eachApp)
}
}
}
}
}
}

extension AppActivityView {
func convertStringToTime(_ string: String) -> String? {
let components = string.components(separatedBy: " ")
guard components.count == 3,
let hours = Int(components[0].replacingOccurrences(of: "h", with: "")),
let minutes = Int(components[1].replacingOccurrences(of: "m", with: "")),
let seconds = Int(components[2].replacingOccurrences(of: "s", with: "")) else {
return nil
struct ListRow: View {
var eachApp: AppDeviceActivity

var body: some View {
VStack(spacing: 4) {
HStack(spacing: 0) {
if let token = eachApp.token {
Label(token)
.labelStyle(.iconOnly)
.offset(x: -4)
}
Text(eachApp.displayName)
Spacer()
VStack(alignment: .trailing, spacing: 2) {
HStack(spacing: 4) {
Text("화면 깨우기")
.font(.footnote)
.foregroundColor(.secondary)
.frame(width: 72, alignment: .leading)
Text("\(eachApp.numberOfPickups)")
.font(.headline)
.bold()
.frame(minWidth: 52, alignment: .trailing)
}
HStack(spacing: 4) {
Text("모니터링 시간")
.font(.footnote)
.foregroundColor(.secondary)
.frame(width: 72, alignment: .leading)
Text(String(eachApp.duration.toString()))
.font(.headline)
.bold()
.frame(minWidth: 52, alignment: .trailing)
}
}
}
HStack {
Text("앱 ID")
.font(.footnote)
.foregroundColor(.secondary)
Text(eachApp.id)
.font(.footnote)
.foregroundColor(.secondary)
.bold()
Spacer()
}
}

let hoursText = String(format: "%02d", hours)
let minutesText = String(format: "%02d", minutes)

return "\(hoursText)시간 \(minutesText)"
.background(.clear)
}
}

// In order to support previews for your extension's custom views, make sure its source files are
// members of your app's Xcode target as well as members of your extension's target. You can use
// Xcode's File Inspector to modify a file's Target Membership.
#Preview {
AppActivityView(totalActivity: "1h 23m")
}
3 changes: 3 additions & 0 deletions HMH_iOS/HMHDeviceActivityReport/HMHDeviceActivityReport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ struct HMHDeviceActivityReport: DeviceActivityReportExtension {
TotalActivityReport { totalActivity in
TotalActivityView(totalActivity: totalActivity)
}
AppActivityReport { appActivity in
AppActivityView(activityReport: appActivity)
}
// Add more reports here...
}
}
1 change: 1 addition & 0 deletions HMH_iOS/HMHDeviceActivityReport/TotalActivityReport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ extension DeviceActivityReport.Context {
// your extension's corresponding DeviceActivityReportScene to render the contents of the
// report.
static let totalActivity = Self("Total Activity")
static let appActivity = Self("App Activity")
}

struct TotalActivityReport: DeviceActivityReportScene {
Expand Down
57 changes: 54 additions & 3 deletions HMH_iOS/HMH_iOS.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@
3601C8362BC6E8FB005790F7 /* HomeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3601C8352BC6E8FB005790F7 /* HomeViewModel.swift */; };
3601C8382BC6EC5C005790F7 /* UsageTimeListItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3601C8372BC6EC5C005790F7 /* UsageTimeListItemView.swift */; };
361C61EA2BD6B7F200EF0D8B /* Font.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BD2D6772B9F39EF00733843 /* Font.swift */; };
361C61EB2BD6B7FF00EF0D8B /* TotalActivityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 368C5D9A2BCC41990035A1AC /* TotalActivityView.swift */; };
361C61EC2BD6B8CC00EF0D8B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 365CD6C02B7E4C5100245CDD /* Assets.xcassets */; };
361C61ED2BD6BA6900EF0D8B /* Pretendard-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = 0BD2D6702B9F385000733843 /* Pretendard-Regular.otf */; };
361C61EE2BD6BA6C00EF0D8B /* Pretendard-SemiBold.otf in Resources */ = {isa = PBXBuildFile; fileRef = 0BD2D6712B9F385100733843 /* Pretendard-SemiBold.otf */; };
Expand All @@ -71,6 +70,13 @@
365CD6D82B7E4D0700245CDD /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 365CD6D72B7E4D0700245CDD /* Kingfisher */; };
365CD6DB2B7E4D3600245CDD /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 365CD6DA2B7E4D3600245CDD /* SnapKit */; };
365CD6DE2B7E4D4E00245CDD /* Then in Frameworks */ = {isa = PBXBuildFile; productRef = 365CD6DD2B7E4D4E00245CDD /* Then */; };
367243B22BE9ABCC007A5A7B /* ChallengeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 367243B12BE9ABCC007A5A7B /* ChallengeViewModel.swift */; };
367243B62BED3830007A5A7B /* ScreenTimeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 367243B52BED3830007A5A7B /* ScreenTimeViewModel.swift */; };
367244152BF0956B007A5A7B /* PointView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 367244142BF0956B007A5A7B /* PointView.swift */; };
367244172BF095AF007A5A7B /* PointViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 367244162BF095AF007A5A7B /* PointViewModel.swift */; };
367244192BF102A6007A5A7B /* ActivityModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 367244182BF102A6007A5A7B /* ActivityModel.swift */; };
3672441C2BF13048007A5A7B /* AppActivityReport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3672441B2BF13048007A5A7B /* AppActivityReport.swift */; };
3672441E2BF14133007A5A7B /* TotalActivityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 368C5D9A2BCC41990035A1AC /* TotalActivityView.swift */; };
367243D12BEDE9F6007A5A7B /* NetworkHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 367243CA2BEDE9F5007A5A7B /* NetworkHelper.swift */; };
367243D22BEDE9F6007A5A7B /* Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = 367243CB2BEDE9F5007A5A7B /* Config.swift */; };
367243D32BEDE9F6007A5A7B /* ResponseData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 367243CC2BEDE9F5007A5A7B /* ResponseData.swift */; };
Expand Down Expand Up @@ -102,6 +108,7 @@
3672440F2BF08664007A5A7B /* NetworkProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3672440E2BF08664007A5A7B /* NetworkProvider.swift */; };
367244112BF08684007A5A7B /* BaseModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 367244102BF08684007A5A7B /* BaseModel.swift */; };
367244132BF086A7007A5A7B /* AuthInterceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 367244122BF086A7007A5A7B /* AuthInterceptor.swift */; };

368C5D972BCC41990035A1AC /* HMHDeviceActivityReport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 368C5D962BCC41990035A1AC /* HMHDeviceActivityReport.swift */; };
368C5D992BCC41990035A1AC /* TotalActivityReport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 368C5D982BCC41990035A1AC /* TotalActivityReport.swift */; };
368C5D9B2BCC41990035A1AC /* TotalActivityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 368C5D9A2BCC41990035A1AC /* TotalActivityView.swift */; };
Expand Down Expand Up @@ -194,6 +201,13 @@
365CD6BE2B7E4C4F00245CDD /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
365CD6C02B7E4C5100245CDD /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
365CD6C32B7E4C5100245CDD /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };

367243B12BE9ABCC007A5A7B /* ChallengeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChallengeViewModel.swift; sourceTree = "<group>"; };
367243B52BED3830007A5A7B /* ScreenTimeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenTimeViewModel.swift; sourceTree = "<group>"; };
367244142BF0956B007A5A7B /* PointView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointView.swift; sourceTree = "<group>"; };
367244162BF095AF007A5A7B /* PointViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointViewModel.swift; sourceTree = "<group>"; };
367244182BF102A6007A5A7B /* ActivityModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityModel.swift; sourceTree = "<group>"; };
3672441B2BF13048007A5A7B /* AppActivityReport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppActivityReport.swift; sourceTree = "<group>"; };
367243CA2BEDE9F5007A5A7B /* NetworkHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkHelper.swift; sourceTree = "<group>"; };
367243CB2BEDE9F5007A5A7B /* Config.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Config.swift; sourceTree = "<group>"; };
367243CC2BEDE9F5007A5A7B /* ResponseData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResponseData.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -486,14 +500,14 @@
365CD6E22B7E4F4300245CDD /* Presentation */ = {
isa = PBXGroup;
children = (
367243AC2BE93383007A5A7B /* Challenge */,
0B5140522BE3A25900C78B9F /* Login */,
368C5D6C2BCC1BA00035A1AC /* Home */,
0B82ECAA2BC9141D002D5CF3 /* MyPage */,
0BC0E5A42BB03E4F00FB0330 /* Onboarding */,
365CD6BE2B7E4C4F00245CDD /* ContentView.swift */,
0B3C296A2BA01BD200435B30 /* TabBarView.swift */,
0B3C29722BA01DCE00435B30 /* CustomTabView.swift */,
0B3C296C2BA01C2000435B30 /* ChallengeView.swift */,
0B3C29702BA01C3000435B30 /* MyPageView.swift */,
368CAADE2BB9975C00FA83B3 /* NavigationBarView.swift */,
0BF56C922BC39127003ECFB1 /* PickerView.swift */,
Expand Down Expand Up @@ -586,6 +600,34 @@
path = Service;
sourceTree = "<group>";
};
367243AC2BE93383007A5A7B /* Challenge */ = {
isa = PBXGroup;
children = (
367243AE2BE9339A007A5A7B /* ViewModels */,
367243AD2BE93390007A5A7B /* Views */,
);
path = Challenge;
sourceTree = "<group>";
};
367243AD2BE93390007A5A7B /* Views */ = {
isa = PBXGroup;
children = (
0B3C296C2BA01C2000435B30 /* ChallengeView.swift */,
367244142BF0956B007A5A7B /* PointView.swift */,
);
path = Views;
sourceTree = "<group>";
};
367243AE2BE9339A007A5A7B /* ViewModels */ = {
isa = PBXGroup;
children = (
367243B12BE9ABCC007A5A7B /* ChallengeViewModel.swift */,
367243B52BED3830007A5A7B /* ScreenTimeViewModel.swift */,
367244162BF095AF007A5A7B /* PointViewModel.swift */,
);
path = ViewModels;
sourceTree = "<group>";
};
367243D82BEDEAC9007A5A7B /* Auth */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -636,11 +678,13 @@
isa = PBXGroup;
children = (
368C5D962BCC41990035A1AC /* HMHDeviceActivityReport.swift */,
367244182BF102A6007A5A7B /* ActivityModel.swift */,
3672441B2BF13048007A5A7B /* AppActivityReport.swift */,
36EB6EBF2BDCC65600E8C939 /* AppActivityView.swift */,
368C5D982BCC41990035A1AC /* TotalActivityReport.swift */,
368C5D9A2BCC41990035A1AC /* TotalActivityView.swift */,
368C5D9C2BCC41990035A1AC /* Info.plist */,
368C5D9D2BCC41990035A1AC /* HMHDeviceActivityReport.entitlements */,
36EB6EBF2BDCC65600E8C939 /* AppActivityView.swift */,
);
path = HMHDeviceActivityReport;
sourceTree = "<group>";
Expand Down Expand Up @@ -804,7 +848,10 @@
files = (
0BF56C972BC3B871003ECFB1 /* StoryContentView.swift in Sources */,
0B3C296D2BA01C2000435B30 /* ChallengeView.swift in Sources */,
367243B22BE9ABCC007A5A7B /* ChallengeViewModel.swift in Sources */,
3601C8362BC6E8FB005790F7 /* HomeViewModel.swift in Sources */,
3672441E2BF14133007A5A7B /* TotalActivityView.swift in Sources */,
367243B62BED3830007A5A7B /* ScreenTimeViewModel.swift in Sources */,
367243FC2BEDEB32007A5A7B /* HomeChallengeResponseDTO.swift in Sources */,
365CD6BF2B7E4C4F00245CDD /* ContentView.swift in Sources */,
0B82ECB22BC916C6002D5CF3 /* MyPageButton.swift in Sources */,
Expand Down Expand Up @@ -833,10 +880,12 @@
0BD2D67C2B9F5A7B00733843 /* String.swift in Sources */,
367244112BF08684007A5A7B /* BaseModel.swift in Sources */,
0BC0E5BD2BB0571E00FB0330 /* NextButtonView.swift in Sources */,
367244152BF0956B007A5A7B /* PointView.swift in Sources */,
0BC0E5A92BB03EE400FB0330 /* OnboardingModel.swift in Sources */,
0B8693772BE5CB46000D7CE1 /* SwipeView.swift in Sources */,
0B86937B2BE727DA000D7CE1 /* SocialLoginModel.swift in Sources */,
0BD2D6782B9F39EF00733843 /* Font.swift in Sources */,
367244172BF095AF007A5A7B /* PointViewModel.swift in Sources */,
0B7646BB2BB13F6100C56D7A /* SurveyButton.swift in Sources */,
0BC0E5B52BB04BD100FB0330 /* GoalTimeView.swift in Sources */,
0BC0E5AB2BB03F8100FB0330 /* OnboardingViewModel.swift in Sources */,
Expand Down Expand Up @@ -876,6 +925,8 @@
files = (
361C61EA2BD6B7F200EF0D8B /* Font.swift in Sources */,
368C5D9B2BCC41990035A1AC /* TotalActivityView.swift in Sources */,
3672441C2BF13048007A5A7B /* AppActivityReport.swift in Sources */,
367244192BF102A6007A5A7B /* ActivityModel.swift in Sources */,
36EB6EC02BDCC65600E8C939 /* AppActivityView.swift in Sources */,
368C5D972BCC41990035A1AC /* HMHDeviceActivityReport.swift in Sources */,
368C5D992BCC41990035A1AC /* TotalActivityReport.swift in Sources */,
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"images" : [
{
"filename" : "ChallengeBackground.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "ChallengeBackground@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "ChallengeBackground@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"images" : [
{
"filename" : "addAppButton.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "addAppButton@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "addAppButton@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 90bad1c

Please # to comment.