├── ScreenTimeAPIExample
├── ScreenTimeAPIExample
│ ├── Resources
│ │ ├── Assets.xcassets
│ │ │ ├── Contents.json
│ │ │ ├── curby.imageset
│ │ │ │ ├── curby-21.png
│ │ │ │ └── Contents.json
│ │ │ ├── AccentColor.colorset
│ │ │ │ └── Contents.json
│ │ │ └── AppIcon.appiconset
│ │ │ │ └── Contents.json
│ │ ├── Info.plist
│ │ └── Base.lproj
│ │ │ └── LaunchScreen.storyboard
│ ├── Sources
│ │ ├── Extensions.swift
│ │ ├── BlockingApplicationModel.swift
│ │ ├── SwiftUIView.swift
│ │ ├── YouTubeBlocker.swift
│ │ └── ViewController.swift
│ ├── ScreenTimeAPIExample.entitlements
│ └── Application
│ │ ├── SceneDelegate.swift
│ │ └── AppDelegate.swift
├── ScreenTimeAPIExample.xcodeproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ └── project.pbxproj
├── ScreenTimeAPIExampleUITests
│ ├── ScreenTimeAPIExampleUITestsLaunchTests.swift
│ └── ScreenTimeAPIExampleUITests.swift
└── ScreenTimeAPIExampleTests
│ └── ScreenTimeAPIExampleTests.swift
├── .gitignore
└── README.md
/ScreenTimeAPIExample/ScreenTimeAPIExample/Resources/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/ScreenTimeAPIExample/ScreenTimeAPIExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ScreenTimeAPIExample/ScreenTimeAPIExample/Resources/Assets.xcassets/curby.imageset/curby-21.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doyeonjeong/ScreenTimeAPI-Example/HEAD/ScreenTimeAPIExample/ScreenTimeAPIExample/Resources/Assets.xcassets/curby.imageset/curby-21.png
--------------------------------------------------------------------------------
/ScreenTimeAPIExample/ScreenTimeAPIExample/Resources/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/ScreenTimeAPIExample/ScreenTimeAPIExample/Sources/Extensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Extensions.swift
3 | // ScreenTimeAPIExample
4 | //
5 | // Created by Doyeon on 2023/04/26.
6 | //
7 |
8 | import DeviceActivity
9 |
10 | extension DeviceActivityName {
11 | static let daily = Self("daily")
12 | }
13 |
--------------------------------------------------------------------------------
/ScreenTimeAPIExample/ScreenTimeAPIExample/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "platform" : "ios",
6 | "size" : "1024x1024"
7 | }
8 | ],
9 | "info" : {
10 | "author" : "xcode",
11 | "version" : 1
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/ScreenTimeAPIExample/ScreenTimeAPIExample/ScreenTimeAPIExample.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.developer.family-controls
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ScreenTimeAPIExample/ScreenTimeAPIExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ScreenTimeAPIExample/ScreenTimeAPIExample/Resources/Assets.xcassets/curby.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "filename" : "curby-21.png",
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ScreenTimeAPIExample/ScreenTimeAPIExample/Sources/BlockingApplicationModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MyModel.swift
3 | // ScreenTimeAPIExample
4 | //
5 | // Created by Doyeon on 2023/04/26.
6 | //
7 |
8 | import Foundation
9 | import FamilyControls
10 | import ManagedSettings
11 |
12 | final class BlockingApplicationModel: ObservableObject {
13 | static let shared = BlockingApplicationModel()
14 |
15 | @Published var newSelection: FamilyActivitySelection = .init()
16 |
17 | var selectedAppsTokens: Set {
18 | newSelection.applicationTokens
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/ScreenTimeAPIExample/ScreenTimeAPIExample/Resources/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | UIApplicationSceneManifest
6 |
7 | UIApplicationSupportsMultipleScenes
8 |
9 | UISceneConfigurations
10 |
11 | UIWindowSceneSessionRoleApplication
12 |
13 |
14 | UISceneConfigurationName
15 | Default Configuration
16 | UISceneDelegateClassName
17 | $(PRODUCT_MODULE_NAME).SceneDelegate
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/ScreenTimeAPIExample/ScreenTimeAPIExampleUITests/ScreenTimeAPIExampleUITestsLaunchTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ScreenTimeAPIExampleUITestsLaunchTests.swift
3 | // ScreenTimeAPIExampleUITests
4 | //
5 | // Created by Doyeon on 2023/04/24.
6 | //
7 |
8 | import XCTest
9 |
10 | final class ScreenTimeAPIExampleUITestsLaunchTests: XCTestCase {
11 |
12 | override class var runsForEachTargetApplicationUIConfiguration: Bool {
13 | true
14 | }
15 |
16 | override func setUpWithError() throws {
17 | continueAfterFailure = false
18 | }
19 |
20 | func testLaunch() throws {
21 | let app = XCUIApplication()
22 | app.launch()
23 |
24 | // Insert steps here to perform after app launch but before taking a screenshot,
25 | // such as logging into a test account or navigating somewhere in the app
26 |
27 | let attachment = XCTAttachment(screenshot: app.screenshot())
28 | attachment.name = "Launch Screen"
29 | attachment.lifetime = .keepAlways
30 | add(attachment)
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/ScreenTimeAPIExample/ScreenTimeAPIExample/Sources/SwiftUIView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftUIView.swift
3 | // ScreenTimeAPIExample
4 | //
5 | // Created by Doyeon on 2023/04/25.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct SwiftUIView: View {
11 |
12 | @EnvironmentObject var model: BlockingApplicationModel
13 | @State var isPresented = false
14 |
15 | var body: some View {
16 | VStack {
17 | Button(action: { isPresented.toggle() }) {
18 | Text("차단할 앱 목록 확인하기 🤗")
19 | .font(.system(size: 18, weight: .bold))
20 | .foregroundColor(.white)
21 | .padding(.vertical, 12)
22 | .padding(.horizontal, 24)
23 | .background(Color.blue)
24 | .cornerRadius(8)
25 | }
26 | .familyActivityPicker(isPresented: $isPresented, selection: $model.newSelection)
27 | }
28 | }
29 | }
30 |
31 | struct SwiftUIView_Previews: PreviewProvider {
32 | static var previews: some View {
33 | SwiftUIView()
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/ScreenTimeAPIExample/ScreenTimeAPIExample/Application/SceneDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | class SceneDelegate: UIResponder, UIWindowSceneDelegate {
4 |
5 | var window: UIWindow?
6 |
7 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
8 |
9 | let viewController = ViewController()
10 | self.window = makeWindow(scene: scene)
11 |
12 | configure(
13 | window: window,
14 | rootViewController: viewController
15 | )
16 | }
17 | }
18 |
19 | // MARK: - Private Function
20 | extension SceneDelegate {
21 | private func makeWindow(scene: UIScene) -> UIWindow? {
22 | guard let windowScene = (scene as? UIWindowScene) else { return nil }
23 | return UIWindow(windowScene: windowScene)
24 | }
25 |
26 | private func configure(
27 | window: UIWindow?,
28 | rootViewController: UIViewController
29 | ) {
30 | guard let window = window else { return }
31 | window.backgroundColor = .white
32 | window.overrideUserInterfaceStyle = .light
33 | window.rootViewController = rootViewController
34 | window.makeKeyAndVisible()
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/ScreenTimeAPIExample/ScreenTimeAPIExampleTests/ScreenTimeAPIExampleTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ScreenTimeAPIExampleTests.swift
3 | // ScreenTimeAPIExampleTests
4 | //
5 | // Created by Doyeon on 2023/04/24.
6 | //
7 |
8 | import XCTest
9 | @testable import ScreenTimeAPIExample
10 |
11 | final class ScreenTimeAPIExampleTests: XCTestCase {
12 |
13 | override func setUpWithError() throws {
14 | // Put setup code here. This method is called before the invocation of each test method in the class.
15 | }
16 |
17 | override func tearDownWithError() throws {
18 | // Put teardown code here. This method is called after the invocation of each test method in the class.
19 | }
20 |
21 | func testExample() throws {
22 | // This is an example of a functional test case.
23 | // Use XCTAssert and related functions to verify your tests produce the correct results.
24 | // Any test you write for XCTest can be annotated as throws and async.
25 | // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error.
26 | // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards.
27 | }
28 |
29 | func testPerformanceExample() throws {
30 | // This is an example of a performance test case.
31 | self.measure {
32 | // Put the code you want to measure the time of here.
33 | }
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/ScreenTimeAPIExample/ScreenTimeAPIExample/Application/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // ScreenTimeAPIExample
4 | //
5 | // Created by Doyeon on 2023/04/24.
6 | //
7 |
8 | import UIKit
9 |
10 | @main
11 | class AppDelegate: UIResponder, UIApplicationDelegate {
12 |
13 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
14 | // Override point for customization after application launch.
15 | return true
16 | }
17 |
18 | // MARK: UISceneSession Lifecycle
19 |
20 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
21 | // Called when a new scene session is being created.
22 | // Use this method to select a configuration to create the new scene with.
23 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
24 | }
25 |
26 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {
27 | // Called when the user discards a scene session.
28 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
29 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
30 | }
31 |
32 | }
33 |
34 |
--------------------------------------------------------------------------------
/ScreenTimeAPIExample/ScreenTimeAPIExampleUITests/ScreenTimeAPIExampleUITests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ScreenTimeAPIExampleUITests.swift
3 | // ScreenTimeAPIExampleUITests
4 | //
5 | // Created by Doyeon on 2023/04/24.
6 | //
7 |
8 | import XCTest
9 |
10 | final class ScreenTimeAPIExampleUITests: XCTestCase {
11 |
12 | override func setUpWithError() throws {
13 | // Put setup code here. This method is called before the invocation of each test method in the class.
14 |
15 | // In UI tests it is usually best to stop immediately when a failure occurs.
16 | continueAfterFailure = false
17 |
18 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
19 | }
20 |
21 | override func tearDownWithError() throws {
22 | // Put teardown code here. This method is called after the invocation of each test method in the class.
23 | }
24 |
25 | func testExample() throws {
26 | // UI tests must launch the application that they test.
27 | let app = XCUIApplication()
28 | app.launch()
29 |
30 | // Use XCTAssert and related functions to verify your tests produce the correct results.
31 | }
32 |
33 | func testLaunchPerformance() throws {
34 | if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) {
35 | // This measures how long it takes to launch your application.
36 | measure(metrics: [XCTApplicationLaunchMetric()]) {
37 | XCUIApplication().launch()
38 | }
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/ScreenTimeAPIExample/ScreenTimeAPIExample/Resources/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/ScreenTimeAPIExample/ScreenTimeAPIExample/Sources/YouTubeBlocker.swift:
--------------------------------------------------------------------------------
1 | //
2 | // YouTubeBlocker.swift
3 | // ScreenTimeAPIExample
4 | //
5 | // Created by Doyeon on 2023/04/26.
6 | //
7 |
8 | import Foundation
9 | import ManagedSettings
10 | import DeviceActivity
11 |
12 | struct YouTubeBlocker {
13 |
14 | let store = ManagedSettingsStore()
15 | let model = BlockingApplicationModel.shared
16 |
17 | // 앱 차단 로직
18 | func block(completion: @escaping (Result) -> Void) {
19 | // 선택한 앱 토큰 가져오기
20 | let selectedAppTokens = model.selectedAppsTokens
21 |
22 | // DeviceActivityCenter를 사용하여 모든 선택한 앱 토큰에 대한 액티비티 차단
23 | let deviceActivityCenter = DeviceActivityCenter()
24 |
25 | // 모니터 DeviceActivitySchedule 설정
26 | let blockSchedule = DeviceActivitySchedule(
27 | intervalStart: DateComponents(hour: 0, minute: 0),
28 | intervalEnd: DateComponents(hour: 23, minute: 59),
29 | repeats: true
30 | )
31 |
32 | store.shield.applications = selectedAppTokens
33 | do {
34 | try deviceActivityCenter.startMonitoring(DeviceActivityName.daily, during: blockSchedule)
35 | // 모니터링이 돼서 리포트가 됐을 때 사용시간이 증가하는시점에 YouTube 사용시간에 대한 트래킹 트리거
36 | // 3시간 잡았는데 -6분 -> 2시간 45분
37 | // 토탈시간 이벤트는 시간~끝이 있고 값은 몇분 사용했다는 결과를 반환할 것 같다.
38 | // 내가 마이너스 한 시간이랑, API에서 반환한 시간이랑 같은지 체크 Validate
39 | // 맞으면 상관없지만, 만약 다르다면 API에서 측정한 시간 기준으로 재할당하기! (사용자가 보는 기록을 기준으로)
40 | // 0분이 딱 됐을 때, 블락이 시작되고
41 | // 앱이 켜졌을 때 시간이 맞는지 체크하고 푸시를 받아야하나? 말아야하나? 체크
42 | // 앱이 꺼지면 푸시 스케줄링 지우고, 대기열 다 지우고
43 | // 푸시 정책: 반감기 (1분 미만일 때에만 1분에 1번으로 조절해주기) -> 초단위 / 2
44 | // 열품타를 보고 하고싶었던 것 -> 다이나믹 아일랜드에서 시간을 캡쳐해줌
45 | } catch {
46 | completion(.failure(error))
47 | return
48 | }
49 | completion(.success(()))
50 | }
51 |
52 | func unblockAllApps() {
53 | store.shield.applications = []
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## User settings
6 | xcuserdata/
7 |
8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
9 | *.xcscmblueprint
10 | *.xccheckout
11 |
12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
13 | build/
14 | DerivedData/
15 | *.moved-aside
16 | *.pbxuser
17 | !default.pbxuser
18 | *.mode1v3
19 | !default.mode1v3
20 | *.mode2v3
21 | !default.mode2v3
22 | *.perspectivev3
23 | !default.perspectivev3
24 |
25 | ## Obj-C/Swift specific
26 | *.hmap
27 |
28 | ## App packaging
29 | *.ipa
30 | *.dSYM.zip
31 | *.dSYM
32 |
33 | ## Playgrounds
34 | timeline.xctimeline
35 | playground.xcworkspace
36 |
37 | # Swift Package Manager
38 | #
39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
40 | # Packages/
41 | # Package.pins
42 | # Package.resolved
43 | # *.xcodeproj
44 | #
45 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
46 | # hence it is not needed unless you have added a package configuration file to your project
47 | # .swiftpm
48 |
49 | .build/
50 |
51 | # CocoaPods
52 | #
53 | # We recommend against adding the Pods directory to your .gitignore. However
54 | # you should judge for yourself, the pros and cons are mentioned at:
55 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
56 | #
57 | # Pods/
58 | #
59 | # Add this line if you want to avoid checking in source code from the Xcode workspace
60 | # *.xcworkspace
61 |
62 | # Carthage
63 | #
64 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
65 | # Carthage/Checkouts
66 |
67 | Carthage/Build/
68 |
69 | # Accio dependency management
70 | Dependencies/
71 | .accio/
72 |
73 | # fastlane
74 | #
75 | # It is recommended to not store the screenshots in the git repo.
76 | # Instead, use fastlane to re-generate the screenshots whenever they are needed.
77 | # For more information about the recommended setup visit:
78 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
79 |
80 | fastlane/report.xml
81 | fastlane/Preview.html
82 | fastlane/screenshots/**/*.png
83 | fastlane/test_output
84 |
85 | # Code Injection
86 | #
87 | # After new code Injection tools there's a generated folder /iOSInjectionProject
88 | # https://github.com/johnno1962/injectionforxcode
89 |
90 | iOSInjectionProject/
91 |
--------------------------------------------------------------------------------
/ScreenTimeAPIExample/ScreenTimeAPIExample/Sources/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // ScreenTimeAPIExample
4 | //
5 | // Created by Doyeon on 2023/04/24.
6 | //
7 |
8 | import UIKit
9 | import FamilyControls
10 | import SwiftUI
11 |
12 | final class ViewController: UIViewController {
13 |
14 | // MARK: - Properties
15 | var hostingController: UIHostingController?
16 |
17 | private let _center = AuthorizationCenter.shared
18 | private let _youTubeBlocker = YouTubeBlocker()
19 |
20 | private lazy var _contentView: UIHostingController = {
21 | let model = BlockingApplicationModel.shared
22 | let hostingController = UIHostingController(
23 | rootView: SwiftUIView()
24 | .environmentObject(model)
25 | )
26 | return hostingController
27 | }()
28 |
29 | private let _blockButton: UIButton = {
30 | let button = UIButton(type: .system)
31 | button.setTitle("차단하기", for: .normal)
32 | button.titleLabel?.font = .systemFont(ofSize: 18, weight: .bold)
33 | button.setTitleColor(.white, for: .normal)
34 | button.backgroundColor = .red
35 | button.layer.cornerRadius = 8
36 | button.translatesAutoresizingMaskIntoConstraints = false
37 | return button
38 | }()
39 |
40 | private let _releaseButton: UIButton = {
41 | let button = UIButton(type: .system)
42 | button.setTitle("해제하기", for: .normal)
43 | button.titleLabel?.font = .systemFont(ofSize: 18, weight: .bold)
44 | button.setTitleColor(.white, for: .normal)
45 | button.backgroundColor = .blue
46 | button.layer.cornerRadius = 8
47 | button.translatesAutoresizingMaskIntoConstraints = false
48 | return button
49 | }()
50 |
51 | private let _buttonStackView: UIStackView = {
52 | let stackView = UIStackView()
53 | stackView.axis = .vertical
54 | stackView.spacing = 10
55 | stackView.distribution = .fillEqually
56 | stackView.translatesAutoresizingMaskIntoConstraints = false
57 | return stackView
58 | }()
59 |
60 | override func viewDidLoad() {
61 | super.viewDidLoad()
62 | _setup()
63 | }
64 |
65 | override func viewDidAppear(_ animated: Bool) {
66 | super.viewDidAppear(animated)
67 | _requestAuthorization()
68 | }
69 | }
70 |
71 | // MARK: - Setup
72 | extension ViewController {
73 | private func _setup() {
74 | _addSubviews()
75 | _setConstraints()
76 | _addTargets()
77 | }
78 |
79 | private func _addTargets() {
80 | _blockButton.addTarget(self, action: #selector(_tappedBlockButton), for: .touchUpInside)
81 | _releaseButton.addTarget(self, action: #selector(_tappedReleaseButton), for: .touchUpInside)
82 | }
83 |
84 | private func _addSubviews() {
85 | _buttonStackView.addArrangedSubview(_blockButton)
86 | _buttonStackView.addArrangedSubview(_releaseButton)
87 | view.addSubview(_buttonStackView)
88 | addChild(_contentView)
89 | view.addSubview(_contentView.view)
90 | }
91 |
92 | private func _setConstraints() {
93 |
94 | _contentView.view.translatesAutoresizingMaskIntoConstraints = false
95 | NSLayoutConstraint.activate([
96 | _contentView.view.topAnchor.constraint(equalTo: view.topAnchor),
97 | _contentView.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
98 | _contentView.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
99 | _contentView.view.bottomAnchor.constraint(equalTo: _blockButton.topAnchor)
100 | ])
101 |
102 | NSLayoutConstraint.activate([
103 | _buttonStackView.topAnchor.constraint(equalTo: view.bottomAnchor, constant: -160),
104 | _buttonStackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 60),
105 | _buttonStackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -60),
106 | _buttonStackView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -60)
107 | ])
108 | }
109 | }
110 |
111 | // MARK: - Actions
112 | extension ViewController {
113 | @objc private func _tappedBlockButton() {
114 | _youTubeBlocker.block { result in
115 | switch result {
116 | case .success():
117 | print("차단 성공")
118 | case .failure(let error):
119 | print("차단 실패: \(error.localizedDescription)")
120 | }
121 | }
122 | }
123 |
124 | @objc private func _tappedReleaseButton() {
125 | print("차단 해제")
126 | _youTubeBlocker.unblockAllApps()
127 | }
128 |
129 | private func _requestAuthorization() {
130 | Task {
131 | do {
132 | try await _center.requestAuthorization(for: .individual)
133 | } catch {
134 | print(error.localizedDescription)
135 | }
136 | }
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ScreenTimeAPI-Example
2 | Screen Time API 를 이용해서 특정 앱 Block 해보기
3 |
4 | ## 기존 스크린 타임 : 비밀번호로 해제 가능
5 | 
6 | 
7 |
8 | ## 스크린 타임 API 사용 : 비밀번호 입력할 수 없는 상태로 제한됨
9 | 
10 | 
11 |
12 |
13 | ## 작동순서
14 | 1. 앱이 실행되면 ViewController의 `viewDidLoad()` 메서드가 호출되어 초기 설정이 수행됩니다.
15 | 2. 사용자가 "차단할 앱 목록 확인하기" 버튼을 클릭하면 `SwiftUIView`에서 팝업이 표시되고, 사용자는 차단하려는 앱 목록을 선택할 수 있습니다.
16 | 3. 사용자가 앱 목록을 선택하면 `BlockingApplicationModel`에 선택한 앱 목록이 저장됩니다.
17 | 4. 사용자가 '차단하기' 버튼을 누르면 `ViewController`에서 `YouTubeBlocker`의 `block()` 메서드가 호출되어 선택한 앱을 차단합니다.
18 | 5. 사용자가 '해제하기' 버튼을 누르면 `ViewController`에서 `YouTubeBlocker`의 `unblockAllApps()` 메서드가 호출되어 모든 앱의 차단을 해제합니다.
19 |
20 | ### BlockingApplicationModel
21 | - 앱 차단을 위한 데이터 모델, 사용자가 선택한 앱 목록 저장
22 | - ObservableObject 프로토콜 채택, 선택한 앱 목록이 변경되면 자동으로 관련된 뷰 업데이트
23 |
24 |
25 | 소스 코드
26 |
27 | ```swift
28 | final class BlockingApplicationModel: ObservableObject {
29 | static let shared = BlockingApplicationModel()
30 |
31 | @Published var newSelection: FamilyActivitySelection = .init()
32 |
33 | var selectedAppsTokens: Set {
34 | newSelection.applicationTokens
35 | }
36 | }
37 | ```
38 |
39 |
40 | ### YouTubeBlocker
41 | - 앱 차단을 수행하는 로직
42 | - `block()` : 선택한 앱을 차단하는 기능
43 | - `unblockAllApps()` : 모든 앱의 차단을 해제하는 기능
44 |
45 |
46 | 소스 코드
47 |
48 | ```swift
49 | struct YouTubeBlocker {
50 |
51 | let store = ManagedSettingsStore()
52 | let model = BlockingApplicationModel.shared
53 |
54 | func block(completion: @escaping (Result) -> Void) {
55 |
56 | let selectedAppTokens = model.selectedAppsTokens
57 |
58 | // DeviceActivityCenter를 사용하여 모든 선택한 앱 토큰에 대한 액티비티 차단
59 | let deviceActivityCenter = DeviceActivityCenter()
60 |
61 | // 모니터 DeviceActivitySchedule 설정
62 | let blockSchedule = DeviceActivitySchedule(
63 | intervalStart: DateComponents(hour: 0, minute: 0),
64 | intervalEnd: DateComponents(hour: 23, minute: 59),
65 | repeats: true
66 | )
67 |
68 | store.shield.applications = selectedAppTokens
69 | do {
70 | try deviceActivityCenter.startMonitoring(DeviceActivityName.daily, during: blockSchedule)
71 | } catch {
72 | completion(.failure(error))
73 | return
74 | }
75 | completion(.success(()))
76 | }
77 |
78 | func unblockAllApps() {
79 | store.shield.applications = []
80 | }
81 |
82 | }
83 | ```
84 |
85 |
86 | ### SwiftUIView
87 | - 사용자 인터페이스를 구성하는 SwiftUI 뷰
88 | - 버튼을 통해 앱 목록을 확인할 수 있음
89 | - 선택한 앱 목록을 BlockingApplicationModel에 저장
90 |
91 |
92 | 소스 코드
93 |
94 | ```swift
95 | struct SwiftUIView: View {
96 |
97 | @EnvironmentObject var model: BlockingApplicationModel
98 | @State var isPresented = false
99 |
100 | var body: some View {
101 | VStack {
102 | Button(action: { isPresented.toggle() }) {
103 | Text("차단할 앱 목록 확인하기 🤗")
104 | //(생략)
105 | }
106 | .familyActivityPicker(isPresented: $isPresented, selection: $model.newSelection)
107 | }
108 | }
109 | }
110 |
111 | ```
112 |
113 |
114 | ### ViewController
115 | - 앱 차단과 관련된 액션을 처리하는 뷰 컨트롤러
116 | - '차단하기' 버튼 : YouTubeBlocker의 block() 메서드가 호출되어 선택한 앱을 차단
117 | - '해제하기' 버튼 : unblockAllApps() 메서드를 호출하여 모든 앱의 차단을 해제
118 |
119 |
120 | 소스 코드
121 |
122 | ```swift
123 | final class ViewController: UIViewController {
124 |
125 | // MARK: - Properties
126 | var hostingController: UIHostingController?
127 |
128 | private let _center = AuthorizationCenter.shared
129 | private let _youTubeBlocker = YouTubeBlocker()
130 |
131 | private lazy var _contentView: UIHostingController = {
132 | let model = BlockingApplicationModel.shared
133 | let hostingController = UIHostingController(
134 | rootView: SwiftUIView()
135 | .environmentObject(model)
136 | )
137 | return hostingController
138 | }()
139 |
140 | private let _blockButton: UIButton = {
141 | let button = UIButton(type: .system)
142 | button.setTitle("차단하기", for: .normal)
143 | // (생략)
144 | return button
145 | }()
146 |
147 | private let _releaseButton: UIButton = {
148 | let button = UIButton(type: .system)
149 | button.setTitle("해제하기", for: .normal)
150 | // (생략)
151 | return button
152 | }()
153 |
154 | private let _buttonStackView: UIStackView = {
155 | let stackView = UIStackView()
156 | // (생략)
157 | return stackView
158 | }()
159 |
160 | override func viewDidLoad() {
161 | super.viewDidLoad()
162 | _setup()
163 | }
164 |
165 | override func viewDidAppear(_ animated: Bool) {
166 | super.viewDidAppear(animated)
167 | _requestAuthorization()
168 | }
169 | }
170 |
171 | // MARK: - Setup
172 | extension ViewController {
173 | private func _setup() {
174 | _addSubviews()
175 | _setConstraints()
176 | _addTargets()
177 | }
178 |
179 | private func _addTargets() {
180 | _blockButton.addTarget(self, action: #selector(_tappedBlockButton), for: .touchUpInside)
181 | _releaseButton.addTarget(self, action: #selector(_tappedReleaseButton), for: .touchUpInside)
182 | }
183 |
184 | private func _addSubviews() {
185 | // (생략)
186 | }
187 |
188 | private func _setConstraints() {
189 | // (생략)
190 | }
191 | }
192 |
193 | // MARK: - Actions
194 | extension ViewController {
195 | @objc private func _tappedBlockButton() {
196 | _youTubeBlocker.block { result in
197 | switch result {
198 | case .success():
199 | print("차단 성공")
200 | case .failure(let error):
201 | print("차단 실패: \(error.localizedDescription)")
202 | }
203 | }
204 | }
205 |
206 | @objc private func _tappedReleaseButton() {
207 | print("차단 해제")
208 | _youTubeBlocker.unblockAllApps()
209 | }
210 |
211 | private func _requestAuthorization() {
212 | Task {
213 | do {
214 | try await _center.requestAuthorization(for: .individual)
215 | } catch {
216 | print(error.localizedDescription)
217 | }
218 | }
219 | }
220 | }
221 | ```
222 |
223 |
--------------------------------------------------------------------------------
/ScreenTimeAPIExample/ScreenTimeAPIExample.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 56;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 8F56E03C29F7808A005ED24E /* SwiftUIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F56E03B29F7808A005ED24E /* SwiftUIView.swift */; };
11 | 8F7092DE29F8FD450040955D /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F7092DD29F8FD450040955D /* Extensions.swift */; };
12 | 8F7092E229F9567C0040955D /* YouTubeBlocker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F7092E129F9567C0040955D /* YouTubeBlocker.swift */; };
13 | 8F7092E429F956BA0040955D /* BlockingApplicationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F7092E329F956BA0040955D /* BlockingApplicationModel.swift */; };
14 | 8F7DEE7B29F680CC0017CD69 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F7DEE7A29F680CC0017CD69 /* AppDelegate.swift */; };
15 | 8F7DEE7D29F680CC0017CD69 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F7DEE7C29F680CC0017CD69 /* SceneDelegate.swift */; };
16 | 8F7DEE7F29F680CC0017CD69 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F7DEE7E29F680CC0017CD69 /* ViewController.swift */; };
17 | 8F7DEE8429F680CF0017CD69 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8F7DEE8329F680CF0017CD69 /* Assets.xcassets */; };
18 | 8F7DEE8729F680CF0017CD69 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8F7DEE8529F680CF0017CD69 /* LaunchScreen.storyboard */; };
19 | 8F7DEE9229F680CF0017CD69 /* ScreenTimeAPIExampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F7DEE9129F680CF0017CD69 /* ScreenTimeAPIExampleTests.swift */; };
20 | 8F7DEE9C29F680CF0017CD69 /* ScreenTimeAPIExampleUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F7DEE9B29F680CF0017CD69 /* ScreenTimeAPIExampleUITests.swift */; };
21 | 8F7DEE9E29F680CF0017CD69 /* ScreenTimeAPIExampleUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F7DEE9D29F680CF0017CD69 /* ScreenTimeAPIExampleUITestsLaunchTests.swift */; };
22 | 8F7DEEAE29F692920017CD69 /* FamilyControls.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8F7DEEAD29F692920017CD69 /* FamilyControls.framework */; };
23 | 8F7DEEB029F692A80017CD69 /* DeviceActivity.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8F7DEEAF29F692A80017CD69 /* DeviceActivity.framework */; };
24 | /* End PBXBuildFile section */
25 |
26 | /* Begin PBXContainerItemProxy section */
27 | 8F7DEE8E29F680CF0017CD69 /* PBXContainerItemProxy */ = {
28 | isa = PBXContainerItemProxy;
29 | containerPortal = 8F7DEE6F29F680CC0017CD69 /* Project object */;
30 | proxyType = 1;
31 | remoteGlobalIDString = 8F7DEE7629F680CC0017CD69;
32 | remoteInfo = ScreenTimeAPIExample;
33 | };
34 | 8F7DEE9829F680CF0017CD69 /* PBXContainerItemProxy */ = {
35 | isa = PBXContainerItemProxy;
36 | containerPortal = 8F7DEE6F29F680CC0017CD69 /* Project object */;
37 | proxyType = 1;
38 | remoteGlobalIDString = 8F7DEE7629F680CC0017CD69;
39 | remoteInfo = ScreenTimeAPIExample;
40 | };
41 | /* End PBXContainerItemProxy section */
42 |
43 | /* Begin PBXFileReference section */
44 | 8F56E03829F7654C005ED24E /* ScreenTimeAPIExample.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ScreenTimeAPIExample.entitlements; sourceTree = ""; };
45 | 8F56E03B29F7808A005ED24E /* SwiftUIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIView.swift; sourceTree = ""; };
46 | 8F7092DD29F8FD450040955D /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; };
47 | 8F7092E129F9567C0040955D /* YouTubeBlocker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YouTubeBlocker.swift; sourceTree = ""; };
48 | 8F7092E329F956BA0040955D /* BlockingApplicationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockingApplicationModel.swift; sourceTree = ""; };
49 | 8F7DEE7729F680CC0017CD69 /* ScreenTimeAPIExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ScreenTimeAPIExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
50 | 8F7DEE7A29F680CC0017CD69 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
51 | 8F7DEE7C29F680CC0017CD69 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; };
52 | 8F7DEE7E29F680CC0017CD69 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
53 | 8F7DEE8329F680CF0017CD69 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
54 | 8F7DEE8629F680CF0017CD69 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
55 | 8F7DEE8829F680CF0017CD69 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
56 | 8F7DEE8D29F680CF0017CD69 /* ScreenTimeAPIExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ScreenTimeAPIExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
57 | 8F7DEE9129F680CF0017CD69 /* ScreenTimeAPIExampleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenTimeAPIExampleTests.swift; sourceTree = ""; };
58 | 8F7DEE9729F680CF0017CD69 /* ScreenTimeAPIExampleUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ScreenTimeAPIExampleUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
59 | 8F7DEE9B29F680CF0017CD69 /* ScreenTimeAPIExampleUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenTimeAPIExampleUITests.swift; sourceTree = ""; };
60 | 8F7DEE9D29F680CF0017CD69 /* ScreenTimeAPIExampleUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenTimeAPIExampleUITestsLaunchTests.swift; sourceTree = ""; };
61 | 8F7DEEAD29F692920017CD69 /* FamilyControls.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FamilyControls.framework; path = System/Library/Frameworks/FamilyControls.framework; sourceTree = SDKROOT; };
62 | 8F7DEEAF29F692A80017CD69 /* DeviceActivity.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DeviceActivity.framework; path = System/Library/Frameworks/DeviceActivity.framework; sourceTree = SDKROOT; };
63 | /* End PBXFileReference section */
64 |
65 | /* Begin PBXFrameworksBuildPhase section */
66 | 8F7DEE7429F680CC0017CD69 /* Frameworks */ = {
67 | isa = PBXFrameworksBuildPhase;
68 | buildActionMask = 2147483647;
69 | files = (
70 | 8F7DEEAE29F692920017CD69 /* FamilyControls.framework in Frameworks */,
71 | 8F7DEEB029F692A80017CD69 /* DeviceActivity.framework in Frameworks */,
72 | );
73 | runOnlyForDeploymentPostprocessing = 0;
74 | };
75 | 8F7DEE8A29F680CF0017CD69 /* Frameworks */ = {
76 | isa = PBXFrameworksBuildPhase;
77 | buildActionMask = 2147483647;
78 | files = (
79 | );
80 | runOnlyForDeploymentPostprocessing = 0;
81 | };
82 | 8F7DEE9429F680CF0017CD69 /* Frameworks */ = {
83 | isa = PBXFrameworksBuildPhase;
84 | buildActionMask = 2147483647;
85 | files = (
86 | );
87 | runOnlyForDeploymentPostprocessing = 0;
88 | };
89 | /* End PBXFrameworksBuildPhase section */
90 |
91 | /* Begin PBXGroup section */
92 | 8F56E03229F74D67005ED24E /* Sources */ = {
93 | isa = PBXGroup;
94 | children = (
95 | 8F7DEE7E29F680CC0017CD69 /* ViewController.swift */,
96 | 8F56E03B29F7808A005ED24E /* SwiftUIView.swift */,
97 | 8F7092DD29F8FD450040955D /* Extensions.swift */,
98 | 8F7092E129F9567C0040955D /* YouTubeBlocker.swift */,
99 | 8F7092E329F956BA0040955D /* BlockingApplicationModel.swift */,
100 | );
101 | path = Sources;
102 | sourceTree = "";
103 | };
104 | 8F7DEE6E29F680CC0017CD69 = {
105 | isa = PBXGroup;
106 | children = (
107 | 8F7DEE7929F680CC0017CD69 /* ScreenTimeAPIExample */,
108 | 8F7DEE9029F680CF0017CD69 /* ScreenTimeAPIExampleTests */,
109 | 8F7DEE9A29F680CF0017CD69 /* ScreenTimeAPIExampleUITests */,
110 | 8F7DEE7829F680CC0017CD69 /* Products */,
111 | 8F7DEEAC29F692920017CD69 /* Frameworks */,
112 | );
113 | sourceTree = "";
114 | };
115 | 8F7DEE7829F680CC0017CD69 /* Products */ = {
116 | isa = PBXGroup;
117 | children = (
118 | 8F7DEE7729F680CC0017CD69 /* ScreenTimeAPIExample.app */,
119 | 8F7DEE8D29F680CF0017CD69 /* ScreenTimeAPIExampleTests.xctest */,
120 | 8F7DEE9729F680CF0017CD69 /* ScreenTimeAPIExampleUITests.xctest */,
121 | );
122 | name = Products;
123 | sourceTree = "";
124 | };
125 | 8F7DEE7929F680CC0017CD69 /* ScreenTimeAPIExample */ = {
126 | isa = PBXGroup;
127 | children = (
128 | 8F56E03829F7654C005ED24E /* ScreenTimeAPIExample.entitlements */,
129 | 8F56E03229F74D67005ED24E /* Sources */,
130 | 8F7DEEAB29F682AB0017CD69 /* Resources */,
131 | 8F7DEEAA29F681400017CD69 /* Application */,
132 | );
133 | path = ScreenTimeAPIExample;
134 | sourceTree = "";
135 | };
136 | 8F7DEE9029F680CF0017CD69 /* ScreenTimeAPIExampleTests */ = {
137 | isa = PBXGroup;
138 | children = (
139 | 8F7DEE9129F680CF0017CD69 /* ScreenTimeAPIExampleTests.swift */,
140 | );
141 | path = ScreenTimeAPIExampleTests;
142 | sourceTree = "";
143 | };
144 | 8F7DEE9A29F680CF0017CD69 /* ScreenTimeAPIExampleUITests */ = {
145 | isa = PBXGroup;
146 | children = (
147 | 8F7DEE9B29F680CF0017CD69 /* ScreenTimeAPIExampleUITests.swift */,
148 | 8F7DEE9D29F680CF0017CD69 /* ScreenTimeAPIExampleUITestsLaunchTests.swift */,
149 | );
150 | path = ScreenTimeAPIExampleUITests;
151 | sourceTree = "";
152 | };
153 | 8F7DEEAA29F681400017CD69 /* Application */ = {
154 | isa = PBXGroup;
155 | children = (
156 | 8F7DEE7C29F680CC0017CD69 /* SceneDelegate.swift */,
157 | 8F7DEE7A29F680CC0017CD69 /* AppDelegate.swift */,
158 | );
159 | path = Application;
160 | sourceTree = "";
161 | };
162 | 8F7DEEAB29F682AB0017CD69 /* Resources */ = {
163 | isa = PBXGroup;
164 | children = (
165 | 8F7DEE8329F680CF0017CD69 /* Assets.xcassets */,
166 | 8F7DEE8529F680CF0017CD69 /* LaunchScreen.storyboard */,
167 | 8F7DEE8829F680CF0017CD69 /* Info.plist */,
168 | );
169 | path = Resources;
170 | sourceTree = "";
171 | };
172 | 8F7DEEAC29F692920017CD69 /* Frameworks */ = {
173 | isa = PBXGroup;
174 | children = (
175 | 8F7DEEAF29F692A80017CD69 /* DeviceActivity.framework */,
176 | 8F7DEEAD29F692920017CD69 /* FamilyControls.framework */,
177 | );
178 | name = Frameworks;
179 | sourceTree = "";
180 | };
181 | /* End PBXGroup section */
182 |
183 | /* Begin PBXNativeTarget section */
184 | 8F7DEE7629F680CC0017CD69 /* ScreenTimeAPIExample */ = {
185 | isa = PBXNativeTarget;
186 | buildConfigurationList = 8F7DEEA129F680CF0017CD69 /* Build configuration list for PBXNativeTarget "ScreenTimeAPIExample" */;
187 | buildPhases = (
188 | 8F7DEE7329F680CC0017CD69 /* Sources */,
189 | 8F7DEE7429F680CC0017CD69 /* Frameworks */,
190 | 8F7DEE7529F680CC0017CD69 /* Resources */,
191 | );
192 | buildRules = (
193 | );
194 | dependencies = (
195 | );
196 | name = ScreenTimeAPIExample;
197 | productName = ScreenTimeAPIExample;
198 | productReference = 8F7DEE7729F680CC0017CD69 /* ScreenTimeAPIExample.app */;
199 | productType = "com.apple.product-type.application";
200 | };
201 | 8F7DEE8C29F680CF0017CD69 /* ScreenTimeAPIExampleTests */ = {
202 | isa = PBXNativeTarget;
203 | buildConfigurationList = 8F7DEEA429F680CF0017CD69 /* Build configuration list for PBXNativeTarget "ScreenTimeAPIExampleTests" */;
204 | buildPhases = (
205 | 8F7DEE8929F680CF0017CD69 /* Sources */,
206 | 8F7DEE8A29F680CF0017CD69 /* Frameworks */,
207 | 8F7DEE8B29F680CF0017CD69 /* Resources */,
208 | );
209 | buildRules = (
210 | );
211 | dependencies = (
212 | 8F7DEE8F29F680CF0017CD69 /* PBXTargetDependency */,
213 | );
214 | name = ScreenTimeAPIExampleTests;
215 | productName = ScreenTimeAPIExampleTests;
216 | productReference = 8F7DEE8D29F680CF0017CD69 /* ScreenTimeAPIExampleTests.xctest */;
217 | productType = "com.apple.product-type.bundle.unit-test";
218 | };
219 | 8F7DEE9629F680CF0017CD69 /* ScreenTimeAPIExampleUITests */ = {
220 | isa = PBXNativeTarget;
221 | buildConfigurationList = 8F7DEEA729F680CF0017CD69 /* Build configuration list for PBXNativeTarget "ScreenTimeAPIExampleUITests" */;
222 | buildPhases = (
223 | 8F7DEE9329F680CF0017CD69 /* Sources */,
224 | 8F7DEE9429F680CF0017CD69 /* Frameworks */,
225 | 8F7DEE9529F680CF0017CD69 /* Resources */,
226 | );
227 | buildRules = (
228 | );
229 | dependencies = (
230 | 8F7DEE9929F680CF0017CD69 /* PBXTargetDependency */,
231 | );
232 | name = ScreenTimeAPIExampleUITests;
233 | productName = ScreenTimeAPIExampleUITests;
234 | productReference = 8F7DEE9729F680CF0017CD69 /* ScreenTimeAPIExampleUITests.xctest */;
235 | productType = "com.apple.product-type.bundle.ui-testing";
236 | };
237 | /* End PBXNativeTarget section */
238 |
239 | /* Begin PBXProject section */
240 | 8F7DEE6F29F680CC0017CD69 /* Project object */ = {
241 | isa = PBXProject;
242 | attributes = {
243 | BuildIndependentTargetsInParallel = 1;
244 | LastSwiftUpdateCheck = 1420;
245 | LastUpgradeCheck = 1420;
246 | TargetAttributes = {
247 | 8F7DEE7629F680CC0017CD69 = {
248 | CreatedOnToolsVersion = 14.2;
249 | };
250 | 8F7DEE8C29F680CF0017CD69 = {
251 | CreatedOnToolsVersion = 14.2;
252 | TestTargetID = 8F7DEE7629F680CC0017CD69;
253 | };
254 | 8F7DEE9629F680CF0017CD69 = {
255 | CreatedOnToolsVersion = 14.2;
256 | TestTargetID = 8F7DEE7629F680CC0017CD69;
257 | };
258 | };
259 | };
260 | buildConfigurationList = 8F7DEE7229F680CC0017CD69 /* Build configuration list for PBXProject "ScreenTimeAPIExample" */;
261 | compatibilityVersion = "Xcode 14.0";
262 | developmentRegion = en;
263 | hasScannedForEncodings = 0;
264 | knownRegions = (
265 | en,
266 | Base,
267 | );
268 | mainGroup = 8F7DEE6E29F680CC0017CD69;
269 | productRefGroup = 8F7DEE7829F680CC0017CD69 /* Products */;
270 | projectDirPath = "";
271 | projectRoot = "";
272 | targets = (
273 | 8F7DEE7629F680CC0017CD69 /* ScreenTimeAPIExample */,
274 | 8F7DEE8C29F680CF0017CD69 /* ScreenTimeAPIExampleTests */,
275 | 8F7DEE9629F680CF0017CD69 /* ScreenTimeAPIExampleUITests */,
276 | );
277 | };
278 | /* End PBXProject section */
279 |
280 | /* Begin PBXResourcesBuildPhase section */
281 | 8F7DEE7529F680CC0017CD69 /* Resources */ = {
282 | isa = PBXResourcesBuildPhase;
283 | buildActionMask = 2147483647;
284 | files = (
285 | 8F7DEE8729F680CF0017CD69 /* LaunchScreen.storyboard in Resources */,
286 | 8F7DEE8429F680CF0017CD69 /* Assets.xcassets in Resources */,
287 | );
288 | runOnlyForDeploymentPostprocessing = 0;
289 | };
290 | 8F7DEE8B29F680CF0017CD69 /* Resources */ = {
291 | isa = PBXResourcesBuildPhase;
292 | buildActionMask = 2147483647;
293 | files = (
294 | );
295 | runOnlyForDeploymentPostprocessing = 0;
296 | };
297 | 8F7DEE9529F680CF0017CD69 /* Resources */ = {
298 | isa = PBXResourcesBuildPhase;
299 | buildActionMask = 2147483647;
300 | files = (
301 | );
302 | runOnlyForDeploymentPostprocessing = 0;
303 | };
304 | /* End PBXResourcesBuildPhase section */
305 |
306 | /* Begin PBXSourcesBuildPhase section */
307 | 8F7DEE7329F680CC0017CD69 /* Sources */ = {
308 | isa = PBXSourcesBuildPhase;
309 | buildActionMask = 2147483647;
310 | files = (
311 | 8F7DEE7F29F680CC0017CD69 /* ViewController.swift in Sources */,
312 | 8F7092E229F9567C0040955D /* YouTubeBlocker.swift in Sources */,
313 | 8F7092DE29F8FD450040955D /* Extensions.swift in Sources */,
314 | 8F7DEE7B29F680CC0017CD69 /* AppDelegate.swift in Sources */,
315 | 8F56E03C29F7808A005ED24E /* SwiftUIView.swift in Sources */,
316 | 8F7DEE7D29F680CC0017CD69 /* SceneDelegate.swift in Sources */,
317 | 8F7092E429F956BA0040955D /* BlockingApplicationModel.swift in Sources */,
318 | );
319 | runOnlyForDeploymentPostprocessing = 0;
320 | };
321 | 8F7DEE8929F680CF0017CD69 /* Sources */ = {
322 | isa = PBXSourcesBuildPhase;
323 | buildActionMask = 2147483647;
324 | files = (
325 | 8F7DEE9229F680CF0017CD69 /* ScreenTimeAPIExampleTests.swift in Sources */,
326 | );
327 | runOnlyForDeploymentPostprocessing = 0;
328 | };
329 | 8F7DEE9329F680CF0017CD69 /* Sources */ = {
330 | isa = PBXSourcesBuildPhase;
331 | buildActionMask = 2147483647;
332 | files = (
333 | 8F7DEE9C29F680CF0017CD69 /* ScreenTimeAPIExampleUITests.swift in Sources */,
334 | 8F7DEE9E29F680CF0017CD69 /* ScreenTimeAPIExampleUITestsLaunchTests.swift in Sources */,
335 | );
336 | runOnlyForDeploymentPostprocessing = 0;
337 | };
338 | /* End PBXSourcesBuildPhase section */
339 |
340 | /* Begin PBXTargetDependency section */
341 | 8F7DEE8F29F680CF0017CD69 /* PBXTargetDependency */ = {
342 | isa = PBXTargetDependency;
343 | target = 8F7DEE7629F680CC0017CD69 /* ScreenTimeAPIExample */;
344 | targetProxy = 8F7DEE8E29F680CF0017CD69 /* PBXContainerItemProxy */;
345 | };
346 | 8F7DEE9929F680CF0017CD69 /* PBXTargetDependency */ = {
347 | isa = PBXTargetDependency;
348 | target = 8F7DEE7629F680CC0017CD69 /* ScreenTimeAPIExample */;
349 | targetProxy = 8F7DEE9829F680CF0017CD69 /* PBXContainerItemProxy */;
350 | };
351 | /* End PBXTargetDependency section */
352 |
353 | /* Begin PBXVariantGroup section */
354 | 8F7DEE8529F680CF0017CD69 /* LaunchScreen.storyboard */ = {
355 | isa = PBXVariantGroup;
356 | children = (
357 | 8F7DEE8629F680CF0017CD69 /* Base */,
358 | );
359 | name = LaunchScreen.storyboard;
360 | sourceTree = "";
361 | };
362 | /* End PBXVariantGroup section */
363 |
364 | /* Begin XCBuildConfiguration section */
365 | 8F7DEE9F29F680CF0017CD69 /* Debug */ = {
366 | isa = XCBuildConfiguration;
367 | buildSettings = {
368 | ALWAYS_SEARCH_USER_PATHS = NO;
369 | CLANG_ANALYZER_NONNULL = YES;
370 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
371 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
372 | CLANG_ENABLE_MODULES = YES;
373 | CLANG_ENABLE_OBJC_ARC = YES;
374 | CLANG_ENABLE_OBJC_WEAK = YES;
375 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
376 | CLANG_WARN_BOOL_CONVERSION = YES;
377 | CLANG_WARN_COMMA = YES;
378 | CLANG_WARN_CONSTANT_CONVERSION = YES;
379 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
380 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
381 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
382 | CLANG_WARN_EMPTY_BODY = YES;
383 | CLANG_WARN_ENUM_CONVERSION = YES;
384 | CLANG_WARN_INFINITE_RECURSION = YES;
385 | CLANG_WARN_INT_CONVERSION = YES;
386 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
387 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
388 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
389 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
390 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
391 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
392 | CLANG_WARN_STRICT_PROTOTYPES = YES;
393 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
394 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
395 | CLANG_WARN_UNREACHABLE_CODE = YES;
396 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
397 | COPY_PHASE_STRIP = NO;
398 | DEBUG_INFORMATION_FORMAT = dwarf;
399 | ENABLE_STRICT_OBJC_MSGSEND = YES;
400 | ENABLE_TESTABILITY = YES;
401 | GCC_C_LANGUAGE_STANDARD = gnu11;
402 | GCC_DYNAMIC_NO_PIC = NO;
403 | GCC_NO_COMMON_BLOCKS = YES;
404 | GCC_OPTIMIZATION_LEVEL = 0;
405 | GCC_PREPROCESSOR_DEFINITIONS = (
406 | "DEBUG=1",
407 | "$(inherited)",
408 | );
409 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
410 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
411 | GCC_WARN_UNDECLARED_SELECTOR = YES;
412 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
413 | GCC_WARN_UNUSED_FUNCTION = YES;
414 | GCC_WARN_UNUSED_VARIABLE = YES;
415 | IPHONEOS_DEPLOYMENT_TARGET = 16.2;
416 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
417 | MTL_FAST_MATH = YES;
418 | ONLY_ACTIVE_ARCH = YES;
419 | SDKROOT = iphoneos;
420 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
421 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
422 | };
423 | name = Debug;
424 | };
425 | 8F7DEEA029F680CF0017CD69 /* Release */ = {
426 | isa = XCBuildConfiguration;
427 | buildSettings = {
428 | ALWAYS_SEARCH_USER_PATHS = NO;
429 | CLANG_ANALYZER_NONNULL = YES;
430 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
431 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
432 | CLANG_ENABLE_MODULES = YES;
433 | CLANG_ENABLE_OBJC_ARC = YES;
434 | CLANG_ENABLE_OBJC_WEAK = YES;
435 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
436 | CLANG_WARN_BOOL_CONVERSION = YES;
437 | CLANG_WARN_COMMA = YES;
438 | CLANG_WARN_CONSTANT_CONVERSION = YES;
439 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
440 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
441 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
442 | CLANG_WARN_EMPTY_BODY = YES;
443 | CLANG_WARN_ENUM_CONVERSION = YES;
444 | CLANG_WARN_INFINITE_RECURSION = YES;
445 | CLANG_WARN_INT_CONVERSION = YES;
446 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
447 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
448 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
449 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
450 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
451 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
452 | CLANG_WARN_STRICT_PROTOTYPES = YES;
453 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
454 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
455 | CLANG_WARN_UNREACHABLE_CODE = YES;
456 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
457 | COPY_PHASE_STRIP = NO;
458 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
459 | ENABLE_NS_ASSERTIONS = NO;
460 | ENABLE_STRICT_OBJC_MSGSEND = YES;
461 | GCC_C_LANGUAGE_STANDARD = gnu11;
462 | GCC_NO_COMMON_BLOCKS = YES;
463 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
464 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
465 | GCC_WARN_UNDECLARED_SELECTOR = YES;
466 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
467 | GCC_WARN_UNUSED_FUNCTION = YES;
468 | GCC_WARN_UNUSED_VARIABLE = YES;
469 | IPHONEOS_DEPLOYMENT_TARGET = 16.2;
470 | MTL_ENABLE_DEBUG_INFO = NO;
471 | MTL_FAST_MATH = YES;
472 | SDKROOT = iphoneos;
473 | SWIFT_COMPILATION_MODE = wholemodule;
474 | SWIFT_OPTIMIZATION_LEVEL = "-O";
475 | VALIDATE_PRODUCT = YES;
476 | };
477 | name = Release;
478 | };
479 | 8F7DEEA229F680CF0017CD69 /* Debug */ = {
480 | isa = XCBuildConfiguration;
481 | buildSettings = {
482 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
483 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
484 | CODE_SIGN_ENTITLEMENTS = ScreenTimeAPIExample/ScreenTimeAPIExample.entitlements;
485 | CODE_SIGN_STYLE = Automatic;
486 | CURRENT_PROJECT_VERSION = 1;
487 | DEVELOPMENT_TEAM = 62SDS28JUT;
488 | GENERATE_INFOPLIST_FILE = YES;
489 | INFOPLIST_FILE = ScreenTimeAPIExample/Resources/Info.plist;
490 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
491 | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
492 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
493 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
494 | IPHONEOS_DEPLOYMENT_TARGET = 16.0;
495 | LD_RUNPATH_SEARCH_PATHS = (
496 | "$(inherited)",
497 | "@executable_path/Frameworks",
498 | );
499 | MARKETING_VERSION = 1.0;
500 | PRODUCT_BUNDLE_IDENTIFIER = com.doyeonjeong.ScreenTimeAPIExample;
501 | PRODUCT_NAME = "$(TARGET_NAME)";
502 | SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
503 | SUPPORTS_MACCATALYST = NO;
504 | SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
505 | SWIFT_EMIT_LOC_STRINGS = YES;
506 | SWIFT_VERSION = 5.0;
507 | TARGETED_DEVICE_FAMILY = 1;
508 | };
509 | name = Debug;
510 | };
511 | 8F7DEEA329F680CF0017CD69 /* Release */ = {
512 | isa = XCBuildConfiguration;
513 | buildSettings = {
514 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
515 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
516 | CODE_SIGN_ENTITLEMENTS = ScreenTimeAPIExample/ScreenTimeAPIExample.entitlements;
517 | CODE_SIGN_STYLE = Automatic;
518 | CURRENT_PROJECT_VERSION = 1;
519 | DEVELOPMENT_TEAM = 62SDS28JUT;
520 | GENERATE_INFOPLIST_FILE = YES;
521 | INFOPLIST_FILE = ScreenTimeAPIExample/Resources/Info.plist;
522 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
523 | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
524 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
525 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
526 | IPHONEOS_DEPLOYMENT_TARGET = 16.0;
527 | LD_RUNPATH_SEARCH_PATHS = (
528 | "$(inherited)",
529 | "@executable_path/Frameworks",
530 | );
531 | MARKETING_VERSION = 1.0;
532 | PRODUCT_BUNDLE_IDENTIFIER = com.doyeonjeong.ScreenTimeAPIExample;
533 | PRODUCT_NAME = "$(TARGET_NAME)";
534 | SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
535 | SUPPORTS_MACCATALYST = NO;
536 | SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
537 | SWIFT_EMIT_LOC_STRINGS = YES;
538 | SWIFT_VERSION = 5.0;
539 | TARGETED_DEVICE_FAMILY = 1;
540 | };
541 | name = Release;
542 | };
543 | 8F7DEEA529F680CF0017CD69 /* Debug */ = {
544 | isa = XCBuildConfiguration;
545 | buildSettings = {
546 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
547 | BUNDLE_LOADER = "$(TEST_HOST)";
548 | CODE_SIGN_STYLE = Automatic;
549 | CURRENT_PROJECT_VERSION = 1;
550 | DEVELOPMENT_TEAM = 62SDS28JUT;
551 | GENERATE_INFOPLIST_FILE = YES;
552 | IPHONEOS_DEPLOYMENT_TARGET = 16.2;
553 | MARKETING_VERSION = 1.0;
554 | PRODUCT_BUNDLE_IDENTIFIER = com.doyeonjeong.ScreenTimeAPIExampleTests;
555 | PRODUCT_NAME = "$(TARGET_NAME)";
556 | SWIFT_EMIT_LOC_STRINGS = NO;
557 | SWIFT_VERSION = 5.0;
558 | TARGETED_DEVICE_FAMILY = "1,2";
559 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ScreenTimeAPIExample.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/ScreenTimeAPIExample";
560 | };
561 | name = Debug;
562 | };
563 | 8F7DEEA629F680CF0017CD69 /* Release */ = {
564 | isa = XCBuildConfiguration;
565 | buildSettings = {
566 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
567 | BUNDLE_LOADER = "$(TEST_HOST)";
568 | CODE_SIGN_STYLE = Automatic;
569 | CURRENT_PROJECT_VERSION = 1;
570 | DEVELOPMENT_TEAM = 62SDS28JUT;
571 | GENERATE_INFOPLIST_FILE = YES;
572 | IPHONEOS_DEPLOYMENT_TARGET = 16.2;
573 | MARKETING_VERSION = 1.0;
574 | PRODUCT_BUNDLE_IDENTIFIER = com.doyeonjeong.ScreenTimeAPIExampleTests;
575 | PRODUCT_NAME = "$(TARGET_NAME)";
576 | SWIFT_EMIT_LOC_STRINGS = NO;
577 | SWIFT_VERSION = 5.0;
578 | TARGETED_DEVICE_FAMILY = "1,2";
579 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ScreenTimeAPIExample.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/ScreenTimeAPIExample";
580 | };
581 | name = Release;
582 | };
583 | 8F7DEEA829F680CF0017CD69 /* Debug */ = {
584 | isa = XCBuildConfiguration;
585 | buildSettings = {
586 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
587 | CODE_SIGN_STYLE = Automatic;
588 | CURRENT_PROJECT_VERSION = 1;
589 | DEVELOPMENT_TEAM = 62SDS28JUT;
590 | GENERATE_INFOPLIST_FILE = YES;
591 | MARKETING_VERSION = 1.0;
592 | PRODUCT_BUNDLE_IDENTIFIER = com.doyeonjeong.ScreenTimeAPIExampleUITests;
593 | PRODUCT_NAME = "$(TARGET_NAME)";
594 | SWIFT_EMIT_LOC_STRINGS = NO;
595 | SWIFT_VERSION = 5.0;
596 | TARGETED_DEVICE_FAMILY = "1,2";
597 | TEST_TARGET_NAME = ScreenTimeAPIExample;
598 | };
599 | name = Debug;
600 | };
601 | 8F7DEEA929F680CF0017CD69 /* Release */ = {
602 | isa = XCBuildConfiguration;
603 | buildSettings = {
604 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
605 | CODE_SIGN_STYLE = Automatic;
606 | CURRENT_PROJECT_VERSION = 1;
607 | DEVELOPMENT_TEAM = 62SDS28JUT;
608 | GENERATE_INFOPLIST_FILE = YES;
609 | MARKETING_VERSION = 1.0;
610 | PRODUCT_BUNDLE_IDENTIFIER = com.doyeonjeong.ScreenTimeAPIExampleUITests;
611 | PRODUCT_NAME = "$(TARGET_NAME)";
612 | SWIFT_EMIT_LOC_STRINGS = NO;
613 | SWIFT_VERSION = 5.0;
614 | TARGETED_DEVICE_FAMILY = "1,2";
615 | TEST_TARGET_NAME = ScreenTimeAPIExample;
616 | };
617 | name = Release;
618 | };
619 | /* End XCBuildConfiguration section */
620 |
621 | /* Begin XCConfigurationList section */
622 | 8F7DEE7229F680CC0017CD69 /* Build configuration list for PBXProject "ScreenTimeAPIExample" */ = {
623 | isa = XCConfigurationList;
624 | buildConfigurations = (
625 | 8F7DEE9F29F680CF0017CD69 /* Debug */,
626 | 8F7DEEA029F680CF0017CD69 /* Release */,
627 | );
628 | defaultConfigurationIsVisible = 0;
629 | defaultConfigurationName = Release;
630 | };
631 | 8F7DEEA129F680CF0017CD69 /* Build configuration list for PBXNativeTarget "ScreenTimeAPIExample" */ = {
632 | isa = XCConfigurationList;
633 | buildConfigurations = (
634 | 8F7DEEA229F680CF0017CD69 /* Debug */,
635 | 8F7DEEA329F680CF0017CD69 /* Release */,
636 | );
637 | defaultConfigurationIsVisible = 0;
638 | defaultConfigurationName = Release;
639 | };
640 | 8F7DEEA429F680CF0017CD69 /* Build configuration list for PBXNativeTarget "ScreenTimeAPIExampleTests" */ = {
641 | isa = XCConfigurationList;
642 | buildConfigurations = (
643 | 8F7DEEA529F680CF0017CD69 /* Debug */,
644 | 8F7DEEA629F680CF0017CD69 /* Release */,
645 | );
646 | defaultConfigurationIsVisible = 0;
647 | defaultConfigurationName = Release;
648 | };
649 | 8F7DEEA729F680CF0017CD69 /* Build configuration list for PBXNativeTarget "ScreenTimeAPIExampleUITests" */ = {
650 | isa = XCConfigurationList;
651 | buildConfigurations = (
652 | 8F7DEEA829F680CF0017CD69 /* Debug */,
653 | 8F7DEEA929F680CF0017CD69 /* Release */,
654 | );
655 | defaultConfigurationIsVisible = 0;
656 | defaultConfigurationName = Release;
657 | };
658 | /* End XCConfigurationList section */
659 | };
660 | rootObject = 8F7DEE6F29F680CC0017CD69 /* Project object */;
661 | }
662 |
--------------------------------------------------------------------------------