├── 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 | ![RPReplay_Final1683190279](https://user-images.githubusercontent.com/108422901/236157002-404c0a4a-a9e6-4e98-b0b3-ea0ae0ef450a.GIF) 6 | ![RPReplay_Final1683190360](https://user-images.githubusercontent.com/108422901/236157048-657616a8-62ff-4164-aa74-d2c531e5821e.GIF) 7 | 8 | ## 스크린 타임 API 사용 : 비밀번호 입력할 수 없는 상태로 제한됨 9 | ![RPReplay_Final1683190312](https://user-images.githubusercontent.com/108422901/236157023-8f36ce1d-59c2-42dd-987c-05143893c496.GIF) 10 | ![RPReplay_Final1683190326](https://user-images.githubusercontent.com/108422901/236157032-9a679b41-58d5-4726-9a85-fdebc25df170.GIF) 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 | --------------------------------------------------------------------------------