├── .gitignore ├── Example ├── AppDelegate.swift ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ ├── TextPrimary.colorset │ │ └── Contents.json │ ├── background.colorset │ │ └── Contents.json │ └── overlayBackground.colorset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist ├── SceneDelegate.swift ├── Utilities.swift └── ViewController.swift ├── LICENSE ├── Package.swift ├── README.md ├── Sources ├── Yokoso │ ├── Error.swift │ ├── Instruction.swift │ ├── MessageLabel.swift │ ├── OverlayView.swift │ ├── Utilities.swift │ └── Yokoso.swift └── Yokoso_Info.plist └── Yokoso.xcodeproj ├── project.pbxproj ├── project.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ ├── IDEWorkspaceChecks.plist │ └── WorkspaceSettings.xcsettings └── xcshareddata └── xcschemes ├── Example.xcscheme └── Yokoso.xcscheme /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | xcuserdata/ 5 | DerivedData/ 6 | .swiftpm/config/registries.json 7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 8 | .netrc 9 | -------------------------------------------------------------------------------- /Example/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Example 4 | // 5 | // Created by 鈴木 俊裕 on 2022/10/12. 6 | // 7 | 8 | import UIKit 9 | 10 | @main 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | 14 | 15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 16 | // Override point for customization after application launch. 17 | return true 18 | } 19 | 20 | // MARK: UISceneSession Lifecycle 21 | 22 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 23 | // Called when a new scene session is being created. 24 | // Use this method to select a configuration to create the new scene with. 25 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 26 | } 27 | 28 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 29 | // Called when the user discards a scene session. 30 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 31 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 32 | } 33 | 34 | 35 | } 36 | 37 | -------------------------------------------------------------------------------- /Example/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 | -------------------------------------------------------------------------------- /Example/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 | -------------------------------------------------------------------------------- /Example/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Assets.xcassets/TextPrimary.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.129", 9 | "green" : "0.129", 10 | "red" : "0.129" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "1.000", 27 | "green" : "1.000", 28 | "red" : "1.000" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Example/Assets.xcassets/background.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "1.000", 9 | "green" : "1.000", 10 | "red" : "1.000" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "0.129", 27 | "green" : "0.129", 28 | "red" : "0.129" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Example/Assets.xcassets/overlayBackground.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "0.398", 8 | "blue" : "0.000", 9 | "green" : "0.000", 10 | "red" : "0.000" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "0.800", 26 | "blue" : "0.000", 27 | "green" : "0.000", 28 | "red" : "0.000" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Example/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 | -------------------------------------------------------------------------------- /Example/Base.lproj/Main.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 | -------------------------------------------------------------------------------- /Example/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 | UISceneStoryboardFile 19 | Main 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Example/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // Example 4 | // 5 | // Created by 鈴木 俊裕 on 2022/10/12. 6 | // 7 | 8 | import UIKit 9 | 10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 11 | 12 | var window: UIWindow? 13 | 14 | 15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 16 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 17 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 18 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 19 | guard let _ = (scene as? UIWindowScene) else { return } 20 | } 21 | 22 | func sceneDidDisconnect(_ scene: UIScene) { 23 | // Called as the scene is being released by the system. 24 | // This occurs shortly after the scene enters the background, or when its session is discarded. 25 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 26 | // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). 27 | } 28 | 29 | func sceneDidBecomeActive(_ scene: UIScene) { 30 | // Called when the scene has moved from an inactive state to an active state. 31 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 32 | } 33 | 34 | func sceneWillResignActive(_ scene: UIScene) { 35 | // Called when the scene will move from an active state to an inactive state. 36 | // This may occur due to temporary interruptions (ex. an incoming phone call). 37 | } 38 | 39 | func sceneWillEnterForeground(_ scene: UIScene) { 40 | // Called as the scene transitions from the background to the foreground. 41 | // Use this method to undo the changes made on entering the background. 42 | } 43 | 44 | func sceneDidEnterBackground(_ scene: UIScene) { 45 | // Called as the scene transitions from the foreground to the background. 46 | // Use this method to save data, release shared resources, and store enough scene-specific state information 47 | // to restore the scene back to its current state. 48 | } 49 | 50 | 51 | } 52 | 53 | -------------------------------------------------------------------------------- /Example/Utilities.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | extension UIColor { 4 | static let background: UIColor = .init(named: "background")! 5 | static let overlayBackground: UIColor = .init(named: "overlayBackground")! 6 | static let textPrimary: UIColor = .init(named: "textPrimary")! 7 | } 8 | -------------------------------------------------------------------------------- /Example/ViewController.swift: -------------------------------------------------------------------------------- 1 | import Yokoso 2 | import UIKit 3 | 4 | final class ViewController: UIViewController { 5 | 6 | private let manager = InstructionManager(overlayBackgroundColor: .overlayBackground) 7 | 8 | private var isShowingInstruction = false 9 | 10 | private let label1 = UIButton() 11 | private let label2 = UIButton() 12 | private let label3 = UIButton() 13 | private let label4 = UIButton() 14 | private let label5 = UIButton() 15 | 16 | private let vStack: UIStackView = { 17 | let vStack = UIStackView() 18 | vStack.axis = .vertical 19 | vStack.distribution = .fill 20 | vStack.alignment = .leading 21 | vStack.spacing = 0 22 | return vStack 23 | }() 24 | 25 | // MARK: LifeCycle 26 | 27 | override func viewDidLoad() { 28 | super.viewDidLoad() 29 | 30 | configureLayout() 31 | } 32 | 33 | // MARK: Invoking Yokoso Instructions 34 | 35 | private func show(_ instruction: Instruction, onFinish: ((Bool) -> ())? = nil) { 36 | DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(100)) { 37 | self._show(instruction, onFinish: onFinish) 38 | } 39 | } 40 | 41 | private func _show(_ instruction: Instruction, onFinish: ((Bool) -> ())? = nil) { 42 | isShowingInstruction = true 43 | 44 | Task { 45 | do { 46 | try await manager.show( 47 | instruction, 48 | in: view 49 | ) 50 | } catch { 51 | 52 | if let error = error as? InstructionError { 53 | showError(error) 54 | } 55 | } 56 | isShowingInstruction = false 57 | } 58 | } 59 | 60 | private func startI1() { 61 | show( 62 | .init( 63 | message: .init(attributedString: makeMessage("Hi with simple Next button. Tap anywhere to continue."), backgroundColor: .background), 64 | nextButton: .simple("Next"), 65 | sourceView: label1, 66 | cutoutPathExpansion: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10), 67 | cutoutCornerRadius: 10 68 | ) 69 | ) { [weak self] success in 70 | print("finish 1 \(success)") 71 | self?.startI2() 72 | } 73 | } 74 | 75 | private func startI2() { 76 | show( 77 | .init( 78 | message: .init(attributedString: makeMessage("You have to tap \"Next\" to continue.\nTap again to restart these instructions."), backgroundColor: .background), 79 | nextButton: .simple("Next"), 80 | sourceView: label2, 81 | blocksTapOutsideCutoutPath: true, 82 | ignoresTapInsideCutoutPath: true 83 | ) 84 | ) { [weak self] success in 85 | print("finish 2 \(success)") 86 | self?.startI3() 87 | } 88 | } 89 | 90 | private func startI3() { 91 | show( 92 | .init( 93 | message: .init(attributedString: makeMessage("Bottom area is fully customizable.🍣"), backgroundColor: .background), 94 | nextButton: .custom(makeNextButtonView()), 95 | sourceView: label3 96 | ) 97 | ) { success in 98 | print("finish 3 \(success)") 99 | } 100 | } 101 | 102 | private func startI4() { 103 | show( 104 | .init( 105 | message: .init(attributedString: makeMessage("Highlight area is customizable by sourceRect.🍣"), backgroundColor: .background), 106 | nextButton: .custom(makeNextButtonView()), 107 | sourceView: label4, 108 | sourceRect: CGRect(x: 0, y: 0, width: 100, height: 100), 109 | blocksTapOutsideCutoutPath: true, 110 | blocksTapInsideCutoutPath: true 111 | ) 112 | ) { success in 113 | print("finish 4 \(success)") 114 | } 115 | } 116 | 117 | private func startI5() { 118 | show( 119 | .init( 120 | message: .init(attributedString: makeMessage("Test 5"), backgroundColor: .background), 121 | nextButton: .simple("Next"), 122 | sourceView: label5, 123 | sourceRect: CGRect(x: 0, y: 0, width: 100, height: 50), 124 | blocksTapOutsideCutoutPath: true, 125 | blocksTapInsideCutoutPath: true 126 | ) 127 | ) { success in 128 | print("finish 5 \(success)") 129 | } 130 | } 131 | 132 | // MARK: Custom Message and NextButton 133 | 134 | private func makeMessage(_ value: String) -> NSAttributedString { 135 | let attr = NSMutableAttributedString(string: value) 136 | let style = NSMutableParagraphStyle() 137 | style.lineSpacing = 5 138 | attr.addAttributes([ 139 | .foregroundColor: UIColor.textPrimary, 140 | .font: UIFont.systemFont(ofSize: 14), 141 | ], range: NSRangeFromString(value)) 142 | return attr 143 | } 144 | 145 | private func makeNextButtonView() -> UIView { 146 | let hStack: UIStackView = { 147 | let hStack = UIStackView() 148 | hStack.axis = .horizontal 149 | hStack.distribution = .equalSpacing 150 | hStack.alignment = .center 151 | hStack.spacing = 0 152 | return hStack 153 | }() 154 | 155 | let progress = UILabel() 156 | progress.text = "🍣" 157 | let next = UIButton(type: .roundedRect) 158 | next.setTitle("Next", for: .normal) 159 | next.setTitleColor(.systemRed, for: .normal) 160 | let skip = UIButton(type: .roundedRect) 161 | skip.setTitle("Skip", for: .normal) 162 | 163 | next.addTarget(self, action: #selector(nextByCustomButton), for: .touchUpInside) 164 | skip.addTarget(self, action: #selector(skipByCustomButton), for: .touchUpInside) 165 | 166 | [next, progress, skip].forEach { 167 | $0.translatesAutoresizingMaskIntoConstraints = false 168 | hStack.addArrangedSubview($0) 169 | } 170 | 171 | return hStack 172 | } 173 | 174 | @objc private func nextByCustomButton() { 175 | manager.close() 176 | } 177 | 178 | @objc private func skipByCustomButton() { 179 | manager.close() 180 | } 181 | 182 | // MARK: Layout 183 | 184 | private func configureLayout() { 185 | 186 | view.backgroundColor = .background 187 | 188 | do { 189 | label1.setTitleColor(.systemRed, for: .normal) 190 | label1.setTitle("Hello", for: .normal) 191 | label1.backgroundColor = .systemGreen 192 | label1.addTarget(self, action: #selector(tap1), for: .touchUpInside) 193 | } 194 | 195 | do { 196 | label2.backgroundColor = .systemCyan 197 | label2.setTitleColor(.systemBlue, for: .normal) 198 | label2.setTitle("Tap", for: .normal) 199 | label2.addTarget(self, action: #selector(tap2), for: .touchUpInside) 200 | } 201 | 202 | do { 203 | label3.backgroundColor = .systemYellow 204 | label3.setTitleColor(.systemBlue, for: .normal) 205 | label3.setTitle("Custom Next", for: .normal) 206 | label3.addTarget(self, action: #selector(tap3), for: .touchUpInside) 207 | } 208 | 209 | do { 210 | label4.setTitleColor(.systemRed, for: .normal) 211 | label4.setTitle("sourceRect", for: .normal) 212 | label4.backgroundColor = .systemGreen 213 | label4.addTarget(self, action: #selector(tap4), for: .touchUpInside) 214 | } 215 | 216 | do { 217 | label5.setTitleColor(.systemRed, for: .normal) 218 | label5.setTitle("sourceRect", for: .normal) 219 | label5.backgroundColor = .systemGreen 220 | label5.addTarget(self, action: #selector(tap5), for: .touchUpInside) 221 | } 222 | 223 | [label1, label2, label3].forEach { 224 | $0.widthAnchor.constraint(equalToConstant: 100).isActive = true 225 | $0.heightAnchor.constraint(equalToConstant: 100).isActive = true 226 | $0.translatesAutoresizingMaskIntoConstraints = false 227 | vStack.addArrangedSubview($0) 228 | } 229 | [label4].forEach { 230 | $0.widthAnchor.constraint(equalToConstant: 200).isActive = true 231 | $0.heightAnchor.constraint(equalToConstant: 100).isActive = true 232 | $0.translatesAutoresizingMaskIntoConstraints = false 233 | vStack.addArrangedSubview($0) 234 | } 235 | [label5].forEach { 236 | $0.widthAnchor.constraint(equalToConstant: 100).isActive = true 237 | $0.heightAnchor.constraint(equalToConstant: 50).isActive = true 238 | $0.translatesAutoresizingMaskIntoConstraints = false 239 | vStack.addArrangedSubview($0) 240 | } 241 | 242 | vStack.frame = CGRect(origin: originalVStackPoint, size: CGSize(width: 200, height: 450)) 243 | vStack.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(handlePan))) 244 | view.addSubview(vStack) 245 | } 246 | 247 | // MARK: Handing Gesture 248 | 249 | private var originalVStackPoint: CGPoint = .init(x: 50, y: 50) 250 | 251 | @objc private func handlePan(_ panGesture: UIPanGestureRecognizer) { 252 | if panGesture.state == .began { 253 | originalVStackPoint = vStack.frame.origin 254 | } 255 | let t = panGesture.translation(in: view) 256 | let o = originalVStackPoint 257 | vStack.frame.origin = CGPoint(x: t.x + o.x, y: t.y + o.y) 258 | } 259 | 260 | @objc private func tap1() { 261 | print(#function) 262 | if !isShowingInstruction { 263 | startI1() 264 | } 265 | } 266 | 267 | @objc private func tap2() { 268 | print(#function) 269 | if !isShowingInstruction { 270 | startI2() 271 | } 272 | } 273 | 274 | @objc private func tap3() { 275 | print(#function) 276 | if !isShowingInstruction { 277 | startI3() 278 | } 279 | } 280 | 281 | @objc private func tap4() { 282 | print(#function) 283 | if !isShowingInstruction { 284 | startI4() 285 | } 286 | } 287 | 288 | @objc private func tap5() { 289 | print(#function) 290 | if !isShowingInstruction { 291 | startI5() 292 | } 293 | } 294 | 295 | // MARK: Utilities 296 | 297 | private func showError(_ error: InstructionError) { 298 | 299 | let alert = UIAlertController(title: "Error", message: """ 300 | \(error) 301 | You get this error when sourceView is not fully visible inside window bounds. 302 | Remember that same situation applys on window size changes(device rotation or iPad SplitView variants). 303 | NOTE: This error message is demonstration purpose. 304 | """, preferredStyle: .alert) 305 | 306 | alert.addAction(.init(title: "OK", style: .default)) 307 | present(alert, animated: true) 308 | } 309 | } 310 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2024 Toshihiro Suzuki 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.9 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "Yokoso", 8 | platforms: [.iOS(.v14)], 9 | products: [ 10 | // Products define the executables and libraries a package produces, and make them visible to other packages. 11 | .library( 12 | name: "Yokoso", 13 | targets: ["Yokoso"]), 14 | ], 15 | dependencies: [ 16 | // Dependencies declare other packages that this package depends on. 17 | // .package(url: /* package url */, from: "1.0.0"), 18 | ], 19 | targets: [ 20 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 21 | // Targets can depend on other targets in this package, and on products in packages this package depends on. 22 | .target( 23 | name: "Yokoso", 24 | dependencies: []), 25 | ] 26 | ) 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🌸Yokoso🗻 2 | 3 | Yet another super simple spotlight instruction framework for UIKit iOS. 4 | 5 | ## Why Yokoso? 6 | 7 | - [x] Checks if interested view is inside window's bounds 8 | - [x] Supports device rotation / iPad SplitView 9 | - [x] Swift Concurrency 10 | - [ ] Swift 6 Concurrency Check Mode ( work in progress ) 11 | 12 | ## Unique Error Feature 13 | 14 | You don't want to show spotlight on an invisible view, right? 15 | One good thing about Yokoso, is that it checks if the interested view is fully visible to your user. 16 | Otherwise `InstructionError.interestedViewOutOfBounds` is thrown. 17 | With Yokoso, your tutorial UI/UX is better than ever. 18 | 19 | ## Screenshot 20 | 21 | https://user-images.githubusercontent.com/6007952/197309254-e7a0ed85-89c0-4d5a-a022-6e02c1fadb3b.mp4 22 | 23 | ## How to use 24 | 25 | It's super simple. 26 | Import the module, 27 | 28 | ```swift 29 | import Yokoso 30 | ``` 31 | 32 | initialize `InstructionManager`, 33 | 34 | ```swift 35 | let manager = InstructionManager(overlayBackgroundColor: .overlayBackground) 36 | ``` 37 | 38 | then use `InstructionManager`'s interface to show or close your `Instruction`. 39 | 40 | ### Showing single Instruction 41 | 42 | ```swift 43 | do { 44 | 45 | try instructionManager.show( 46 | .init( 47 | message: .init( 48 | attributedString: attributedString, 49 | backgroundColor: uicolor 50 | ), 51 | nextButton: .simple("Next"), 52 | sourceView: label 53 | ), 54 | in: view 55 | ) 56 | 57 | } catch { 58 | if let error = error as? InstructionError { 59 | assertionFailure(error.localizedDescription) 60 | } 61 | } 62 | ``` 63 | 64 | Please refer to the Example app for further customization. 65 | 66 | ## SwiftUI 67 | 68 | You can use Yokoso for SwiftUI view, at least if you're embedding with `UIHostingController`. 69 | 70 | First you need to retrieve interested view's frame via `ObservableObject` + `GeometryReader`. 71 | 72 | ```swift 73 | class ViewModel: ObservableObject { 74 | @Published var interestedViewRect: CGRect? 75 | } 76 | 77 | struct YourView: View { 78 | @ObservedObject var viewModel: ViewModel 79 | var body: some View { 80 | GeometryReader { p in 81 | yourLayout 82 | .onAppear { 83 | viewModel.interestedViewRect = p.frame(in: .global) 84 | } 85 | } 86 | } 87 | 88 | } 89 | ``` 90 | 91 | Then pass that frame to `sourceRect` parameter of `InstructionManager.show`. 92 | 93 | ```swift 94 | try await instructionManager?.show( 95 | .init( 96 | message: .init(attributedString: message, backgroundColor: .v4.monotone8), 97 | nextButton: .custom(...), 98 | sourceView: view, 99 | sourceRect: sourceRect, 100 | blocksTapOutsideCutoutPath: true, 101 | ignoresTapInsideCutoutPath: true 102 | ), 103 | in: view 104 | ) 105 | ``` 106 | 107 | ## Requirements 108 | 109 | - iOS 14+ 110 | 111 | ## Install 112 | 113 | - [x] Swift Package Manager 114 | 115 | ## Contributing 116 | 117 | Any contributions are warmly welcomed.😊 118 | 119 | - Feature Request / Pull Request 120 | - Bug Report 121 | - Question! 122 | 123 | ## LICENSE 124 | 125 | MIT 126 | -------------------------------------------------------------------------------- /Sources/Yokoso/Error.swift: -------------------------------------------------------------------------------- 1 | public enum InstructionError: Error { 2 | case interestedViewOutOfBounds 3 | } 4 | -------------------------------------------------------------------------------- /Sources/Yokoso/Instruction.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public struct Instruction { 4 | 5 | public struct Message { 6 | let attributedString: NSAttributedString 7 | let backgroundColor: UIColor 8 | let maxWidth: CGFloat 9 | 10 | public init( 11 | attributedString: NSAttributedString, 12 | backgroundColor: UIColor, 13 | maxWidth: CGFloat = 375 14 | ) { 15 | self.attributedString = attributedString 16 | self.backgroundColor = backgroundColor 17 | self.maxWidth = maxWidth 18 | } 19 | } 20 | 21 | public enum NextButton { 22 | case simple(String) 23 | case custom(UIView) 24 | } 25 | 26 | let message: Message 27 | let nextButton: NextButton? 28 | let sourceView: UIView 29 | let sourceRect: CGRect? 30 | let cutoutPathExpansion: UIEdgeInsets 31 | let cutoutCornerRadius: CGFloat 32 | let blocksTapOutsideCutoutPath: Bool 33 | let blocksTapInsideCutoutPath: Bool 34 | let ignoresTapInsideCutoutPath: Bool 35 | 36 | /// - parameter message: message struct value 37 | /// - parameter nextButton: NextButton enum value. Default: nil 38 | /// - parameter sourceView: The interested view. 39 | /// - parameter blocksTapOutsideCutoutPath: If true, only tappable inside cutoutPath and does not close by itself. Default: false 40 | public init( 41 | message: Message, 42 | nextButton: NextButton? = nil, 43 | sourceView: UIView, 44 | sourceRect: CGRect? = nil, 45 | cutoutPathExpansion: UIEdgeInsets = .zero, 46 | cutoutCornerRadius: CGFloat = 5, 47 | blocksTapOutsideCutoutPath: Bool = false, 48 | blocksTapInsideCutoutPath: Bool = false, 49 | ignoresTapInsideCutoutPath: Bool = false 50 | ) { 51 | self.blocksTapOutsideCutoutPath = blocksTapOutsideCutoutPath 52 | self.blocksTapInsideCutoutPath = blocksTapInsideCutoutPath 53 | self.ignoresTapInsideCutoutPath = ignoresTapInsideCutoutPath 54 | self.message = message 55 | self.nextButton = nextButton 56 | self.sourceView = sourceView 57 | self.sourceRect = sourceRect 58 | self.cutoutPathExpansion = cutoutPathExpansion 59 | self.cutoutCornerRadius = cutoutCornerRadius 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Sources/Yokoso/MessageLabel.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | final class MessageLabel: UIView { 4 | 5 | let instruction: Instruction 6 | var onTapNext: (() -> ())? 7 | 8 | init(_ instruction: Instruction) { 9 | self.instruction = instruction 10 | 11 | super.init(frame: .zero) 12 | 13 | configureLayout() 14 | } 15 | 16 | required init?(coder: NSCoder) { fatalError() } 17 | 18 | private func configureLayout() { 19 | let message = instruction.message 20 | 21 | layer.cornerRadius = 5 22 | backgroundColor = message.backgroundColor 23 | 24 | let label = UILabel() 25 | label.attributedText = message.attributedString 26 | label.numberOfLines = 0 27 | 28 | let labelPadding: CGFloat = 15 29 | 30 | let vStack: UIStackView = { 31 | let vStack = UIStackView() 32 | vStack.axis = .vertical 33 | vStack.distribution = .fill 34 | vStack.alignment = .fill 35 | vStack.spacing = 0 36 | return vStack 37 | }() 38 | 39 | [label].forEach { 40 | $0.translatesAutoresizingMaskIntoConstraints = false 41 | vStack.addArrangedSubview($0) 42 | } 43 | 44 | let bottom: CGFloat = instruction.nextButton == nil ? labelPadding : 5 45 | 46 | constrainSubview(vStack, top: labelPadding, bottom: bottom, leading: labelPadding, trailing: labelPadding) 47 | 48 | if let nextButton = instruction.nextButton { 49 | switch nextButton { 50 | 51 | case .simple(let nextText): 52 | let button = UIButton(type: .system) 53 | button.setTitle(nextText, for: .normal) 54 | button.addTarget(self, action: #selector(tapNext), for: .touchUpInside) 55 | button.translatesAutoresizingMaskIntoConstraints = false 56 | vStack.addArrangedSubview(button) 57 | 58 | case .custom(let customView): 59 | customView.translatesAutoresizingMaskIntoConstraints = false 60 | vStack.addArrangedSubview(customView) 61 | } 62 | } 63 | 64 | NSLayoutConstraint.activate([ 65 | widthAnchor.constraint(lessThanOrEqualToConstant: message.maxWidth), 66 | ]) 67 | } 68 | 69 | @objc private func tapNext() { 70 | onTapNext?() 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Sources/Yokoso/OverlayView.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | final class OverlayView: UIView { 4 | 5 | var cutoutPath: CGRect? { 6 | didSet { 7 | reloadLayers() 8 | } 9 | } 10 | 11 | var blocksTapInsideCutoutPath: Bool = false 12 | 13 | var onWidthChanged: (() -> ())? 14 | var onHitInsideCutoutPath: (() -> ())? 15 | private var preWidth: CGFloat? 16 | private let cutoutCornerRadius: CGFloat 17 | 18 | init(backgroundColor: UIColor, cutoutCornerRadius: CGFloat) { 19 | self.cutoutCornerRadius = cutoutCornerRadius 20 | super.init(frame: .zero) 21 | 22 | self.backgroundColor = backgroundColor 23 | } 24 | 25 | required init?(coder: NSCoder) { fatalError() } 26 | 27 | override func layoutSubviews() { 28 | super.layoutSubviews() 29 | 30 | let width = frame.width 31 | 32 | if let preWidth, preWidth != width, width > 0 { 33 | onWidthChanged?() 34 | } 35 | 36 | preWidth = width 37 | } 38 | 39 | override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { 40 | if let cutoutPath, cutoutPath.contains(point) { 41 | if !blocksTapInsideCutoutPath { 42 | onHitInsideCutoutPath?() 43 | return nil 44 | } 45 | } 46 | 47 | return super.hitTest(point, with: event) 48 | } 49 | 50 | private func reloadLayers() { 51 | guard let cutoutPath else { return } 52 | 53 | let path = UIBezierPath(roundedRect: cutoutPath, cornerRadius: cutoutCornerRadius) 54 | path.append(UIBezierPath(rect: bounds)) 55 | 56 | let maskLayer = CAShapeLayer() 57 | maskLayer.frame = bounds 58 | maskLayer.fillRule = .evenOdd 59 | maskLayer.path = path.cgPath 60 | 61 | layer.mask = maskLayer 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Sources/Yokoso/Utilities.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | extension NSLayoutConstraint { 4 | func priority(_ priority: UILayoutPriority) -> NSLayoutConstraint { 5 | self.priority = priority 6 | return self 7 | } 8 | } 9 | 10 | extension UIView { 11 | enum Edge { 12 | case top, bottom, leading, trailing 13 | } 14 | 15 | /// subviewを四隅に貼り付ける。 16 | func constrainSubview( 17 | _ view: UIView, 18 | top: CGFloat = 0, 19 | bottom: CGFloat = 0, 20 | leading: CGFloat = 0, 21 | trailing: CGFloat = 0, 22 | againstSafeAreaOf edges: Set = [] 23 | ) { 24 | view.translatesAutoresizingMaskIntoConstraints = false 25 | addSubview(view) 26 | 27 | NSLayoutConstraint.activate([ 28 | view.topAnchor.constraint( 29 | equalTo: edges.contains(.top) ? safeAreaLayoutGuide.topAnchor : topAnchor, 30 | constant: top 31 | ), 32 | view.bottomAnchor.constraint( 33 | equalTo: edges.contains(.bottom) ? safeAreaLayoutGuide.bottomAnchor : bottomAnchor, 34 | constant: -bottom 35 | ), 36 | view.leadingAnchor.constraint( 37 | equalTo: edges.contains(.leading) ? safeAreaLayoutGuide.leadingAnchor : leadingAnchor, 38 | constant: leading 39 | ), 40 | view.trailingAnchor.constraint( 41 | equalTo: edges.contains(.trailing) ? safeAreaLayoutGuide.trailingAnchor : trailingAnchor, 42 | constant: -trailing 43 | ), 44 | ]) 45 | 46 | } 47 | 48 | /// subviewを四隅に貼り付ける。 49 | func constrainSubview( 50 | _ view: UIView, 51 | horizontal: CGFloat = 0, 52 | vertical: CGFloat = 0, 53 | againstSafeAreaOf edges: Set = [] 54 | ) { 55 | constrainSubview( 56 | view, 57 | top: vertical, 58 | bottom: vertical, 59 | leading: horizontal, 60 | trailing: horizontal, 61 | againstSafeAreaOf: edges 62 | ) 63 | } 64 | 65 | func centerSubview(_ view: UIView) { 66 | addSubview(view) 67 | view.translatesAutoresizingMaskIntoConstraints = false 68 | 69 | NSLayoutConstraint.activate([ 70 | view.centerXAnchor.constraint(equalTo: centerXAnchor), 71 | view.centerYAnchor.constraint(equalTo: centerYAnchor), 72 | ]) 73 | } 74 | 75 | } 76 | 77 | extension CGRect { 78 | mutating func expand(_ edgeInsets: UIEdgeInsets) { 79 | self = CGRect( 80 | x: minX - edgeInsets.left, 81 | y: minY - edgeInsets.top, 82 | width: width + edgeInsets.left + edgeInsets.right, 83 | height: height + edgeInsets.top + edgeInsets.bottom 84 | ) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /Sources/Yokoso/Yokoso.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | /// Supporting device rotation. 4 | let overlayOuterMargin: CGFloat = 500 5 | 6 | @MainActor 7 | public final class InstructionManager { 8 | 9 | private weak var window: UIWindow? 10 | private var onFinish: ((Bool) -> ())? 11 | 12 | public var isStarted: Bool { window != nil } 13 | private let overlayBackgroundColor: UIColor 14 | private var instruction: Instruction? 15 | 16 | private var overlay: OverlayView? { 17 | didSet { 18 | guard let w = window, let overlay else { return } 19 | 20 | overlay.alpha = 0 21 | w.addSubview(overlay) 22 | overlay.translatesAutoresizingMaskIntoConstraints = false 23 | 24 | NSLayoutConstraint.activate([ 25 | overlay.topAnchor.constraint(equalTo: w.topAnchor, constant: -overlayOuterMargin), 26 | overlay.bottomAnchor.constraint(equalTo: w.bottomAnchor, constant: overlayOuterMargin), 27 | overlay.leadingAnchor.constraint(equalTo: w.leadingAnchor, constant: -overlayOuterMargin), 28 | overlay.trailingAnchor.constraint(equalTo: w.trailingAnchor, constant: overlayOuterMargin), 29 | ]) 30 | 31 | // HACK: 32 | // Need to fix overlay's frame here. 33 | // I coundn't support device rotation with frame layout, so sticking with AutoLayout. 34 | // Looks like expensive code though. 35 | w.layoutIfNeeded() 36 | 37 | UIView.animate(withDuration: 0.3) { 38 | oldValue?.alpha = 0 39 | overlay.alpha = 1 40 | } completion: { _ in 41 | oldValue?.removeFromSuperview() 42 | } 43 | } 44 | } 45 | 46 | public init(overlayBackgroundColor: UIColor = .black.withAlphaComponent(0.4)) { 47 | self.overlayBackgroundColor = overlayBackgroundColor 48 | } 49 | 50 | public func show(_ instruction: Instruction, in view: UIView) async throws { 51 | try await withCheckedThrowingContinuation { c in 52 | do { 53 | try self.show(instruction, in: view, onFinish: { isSuccessful in 54 | if isSuccessful { 55 | c.resume() 56 | } else { 57 | c.resume(throwing: InstructionError.interestedViewOutOfBounds) 58 | } 59 | }) 60 | } catch { 61 | c.resume(throwing: error) 62 | } 63 | } 64 | } 65 | 66 | /// - throws: `InstructionError.interestedViewOutOfBounds` 67 | public func show( 68 | _ instruction: Instruction, 69 | in view: UIView, 70 | onFinish: @escaping (Bool) -> () 71 | ) throws { 72 | window = view.window 73 | self.onFinish = onFinish 74 | 75 | try _show(instruction) 76 | } 77 | 78 | private func _show(_ instruction: Instruction) throws { 79 | 80 | guard let window else { return } 81 | 82 | guard var sourceViewFrameInWindow = instruction.sourceView.superview?.convert(instruction.sourceView.frame, to: window) else { 83 | return 84 | } 85 | 86 | self.instruction = instruction 87 | 88 | if let sourceRect = instruction.sourceRect { 89 | sourceViewFrameInWindow = CGRect( 90 | x: sourceViewFrameInWindow.minX + sourceRect.minX, 91 | y: sourceViewFrameInWindow.minY + sourceRect.minY, 92 | width: sourceRect.size.width, 93 | height: sourceRect.size.height 94 | ) 95 | } 96 | 97 | sourceViewFrameInWindow.expand(instruction.cutoutPathExpansion) 98 | 99 | if sourceViewFrameInWindow.minX < 0 100 | || sourceViewFrameInWindow.minY < 0 101 | || sourceViewFrameInWindow.maxY > window.bounds.height 102 | || sourceViewFrameInWindow.maxX > window.bounds.width 103 | { 104 | dismissOverlayAnimated() 105 | throw InstructionError.interestedViewOutOfBounds 106 | } 107 | 108 | let overlay = OverlayView( 109 | backgroundColor: overlayBackgroundColor, 110 | cutoutCornerRadius: instruction.cutoutCornerRadius 111 | ) 112 | self.overlay = overlay 113 | 114 | let sourceViewX = sourceViewFrameInWindow.minX + overlayOuterMargin 115 | let sourceViewY = sourceViewFrameInWindow.minY + overlayOuterMargin 116 | let cutoutX = sourceViewX 117 | let cutoutY = sourceViewY 118 | let cutoutCenterX = cutoutX + sourceViewFrameInWindow.width / 2 119 | let cutoutHeight = sourceViewFrameInWindow.height 120 | 121 | let expanded = CGRect( 122 | x: cutoutX, 123 | y: cutoutY, 124 | width: sourceViewFrameInWindow.width, 125 | height: cutoutHeight 126 | ) 127 | 128 | overlay.cutoutPath = expanded 129 | overlay.blocksTapInsideCutoutPath = instruction.blocksTapInsideCutoutPath 130 | 131 | let messageLabel = MessageLabel(instruction) 132 | 133 | if !instruction.blocksTapOutsideCutoutPath || !instruction.blocksTapInsideCutoutPath { 134 | overlay.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(onTapOverlay))) 135 | } 136 | messageLabel.onTapNext = { [weak self] in self?.close() } 137 | 138 | overlay.onHitInsideCutoutPath = { [weak self] in 139 | if !instruction.ignoresTapInsideCutoutPath { 140 | self?.close() 141 | } 142 | } 143 | 144 | overlay.onWidthChanged = { [weak self] in 145 | 146 | // - NOTE: Supporting device rotation / iPad SplitView 147 | // - HACK: Layout is not fixed in original window at this time, so check interested frame after some delay. 148 | DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(50)) { [weak self] in 149 | guard let me = self else { return } 150 | 151 | do { 152 | try me._show(instruction) 153 | } catch { 154 | me.close(unexpectedly: true) 155 | } 156 | } 157 | 158 | } 159 | 160 | let arrow = UIView() 161 | arrow.layer.cornerRadius = 5 162 | arrow.backgroundColor = instruction.message.backgroundColor 163 | arrow.transform = CGAffineTransform(rotationAngle: 45 * CGFloat.pi / 180) 164 | 165 | [messageLabel, arrow].forEach { 166 | $0.translatesAutoresizingMaskIntoConstraints = false 167 | } 168 | 169 | let rectHeight: CGFloat = 30 170 | let arrowHeight: CGFloat = rectHeight * sqrt(2) / 2 171 | 172 | overlay.addSubview(arrow) // NOTE: `arrow` has to be below mesasge `container` 173 | overlay.addSubview(messageLabel) 174 | 175 | NSLayoutConstraint.activate([ 176 | arrow.widthAnchor.constraint(equalToConstant: 30), 177 | arrow.heightAnchor.constraint(equalToConstant: 30), 178 | arrow.centerXAnchor.constraint(equalTo: overlay.leadingAnchor, constant: cutoutCenterX).priority(.defaultLow), 179 | arrow.leadingAnchor.constraint(greaterThanOrEqualTo: window.safeAreaLayoutGuide.leadingAnchor, constant: 5), 180 | arrow.trailingAnchor.constraint(lessThanOrEqualTo: window.safeAreaLayoutGuide.trailingAnchor, constant: -5), 181 | arrow.leadingAnchor.constraint(greaterThanOrEqualTo: messageLabel.leadingAnchor, constant: 10), 182 | arrow.trailingAnchor.constraint(lessThanOrEqualTo: messageLabel.trailingAnchor, constant: 10), 183 | messageLabel.centerXAnchor.constraint(equalTo: overlay.leadingAnchor, constant: cutoutCenterX).priority(.defaultLow), 184 | messageLabel.leadingAnchor.constraint(greaterThanOrEqualTo: window.safeAreaLayoutGuide.leadingAnchor, constant: 5), 185 | messageLabel.trailingAnchor.constraint(lessThanOrEqualTo: window.safeAreaLayoutGuide.trailingAnchor, constant: -5), 186 | messageLabel.widthAnchor.constraint(lessThanOrEqualToConstant: window.frame.width - window.safeAreaInsets.right - window.safeAreaInsets.left - 30), 187 | ]) 188 | 189 | // Deciding arrow direction 190 | let arrowConstraints: [NSLayoutConstraint] = { 191 | 192 | messageLabel.layoutIfNeeded() 193 | 194 | if window.frame.height - sourceViewFrameInWindow.maxY < messageLabel.frame.height + rectHeight { 195 | return [ 196 | arrow.bottomAnchor.constraint(equalTo: overlay.topAnchor, constant: cutoutY - 10), 197 | messageLabel.bottomAnchor.constraint(equalTo: overlay.topAnchor, constant: cutoutY - 2 - arrowHeight), 198 | ] 199 | } else { 200 | return [ 201 | arrow.topAnchor.constraint(equalTo: overlay.topAnchor, constant: cutoutY + cutoutHeight + 10), 202 | messageLabel.topAnchor.constraint(equalTo: overlay.topAnchor, constant: cutoutY + cutoutHeight + 2 + arrowHeight), 203 | ] 204 | } 205 | }() 206 | 207 | NSLayoutConstraint.activate(arrowConstraints) 208 | } 209 | 210 | @objc private func onTapOverlay(_ gesture: UITapGestureRecognizer) { 211 | guard let instruction else { return } 212 | let isTapInsideCutoutPath = overlay?.cutoutPath?.contains(gesture.location(in: overlay)) == true 213 | if (instruction.blocksTapInsideCutoutPath || instruction.ignoresTapInsideCutoutPath), isTapInsideCutoutPath { 214 | return 215 | } 216 | 217 | if instruction.blocksTapOutsideCutoutPath, !isTapInsideCutoutPath { 218 | return 219 | } 220 | 221 | close() 222 | } 223 | 224 | private var isClosing = false 225 | 226 | @objc public func close(unexpectedly: Bool = false) { 227 | guard let overlay, !isClosing else { return } 228 | 229 | isClosing = true 230 | overlay.onHitInsideCutoutPath = nil 231 | overlay.onWidthChanged = nil 232 | 233 | // NOTE: 234 | // If close is triggered by hitTest, then `onFinish` call would conflict with client's tap event handling. 235 | // Let's delay this by default. 236 | DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(300)) { [weak self] in 237 | guard let me = self else { return } 238 | 239 | me.isClosing = false 240 | me.onFinish?(!unexpectedly) 241 | } 242 | 243 | window = nil 244 | instruction = nil 245 | 246 | dismissOverlayAnimated() 247 | } 248 | 249 | private func dismissOverlayAnimated() { 250 | UIView.animate(withDuration: 0.3) { [weak overlay] in 251 | overlay?.alpha = 0 252 | } 253 | } 254 | } 255 | -------------------------------------------------------------------------------- /Sources/Yokoso_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Yokoso.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXAggregateTarget section */ 10 | "spotlight::SpotlightPackageTests::ProductTarget" /* SpotlightPackageTests */ = { 11 | isa = PBXAggregateTarget; 12 | buildConfigurationList = OBJ_30 /* Build configuration list for PBXAggregateTarget "SpotlightPackageTests" */; 13 | buildPhases = ( 14 | ); 15 | dependencies = ( 16 | ); 17 | name = SpotlightPackageTests; 18 | productName = SpotlightPackageTests; 19 | }; 20 | /* End PBXAggregateTarget section */ 21 | 22 | /* Begin PBXBuildFile section */ 23 | 8A51EA4B28F6DD0C00970A37 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A51EA4A28F6DD0C00970A37 /* AppDelegate.swift */; }; 24 | 8A51EA4D28F6DD0C00970A37 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A51EA4C28F6DD0C00970A37 /* SceneDelegate.swift */; }; 25 | 8A51EA4F28F6DD0C00970A37 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A51EA4E28F6DD0C00970A37 /* ViewController.swift */; }; 26 | 8A51EA5228F6DD0C00970A37 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8A51EA5028F6DD0C00970A37 /* Main.storyboard */; }; 27 | 8A51EA5428F6DD0D00970A37 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8A51EA5328F6DD0D00970A37 /* Assets.xcassets */; }; 28 | 8A51EA5728F6DD0D00970A37 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8A51EA5528F6DD0D00970A37 /* LaunchScreen.storyboard */; }; 29 | 8A51EA5D28F6DE9100970A37 /* OverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A51EA5C28F6DE9100970A37 /* OverlayView.swift */; }; 30 | 8A51EA6128F82B2200970A37 /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A51EA6028F82B2200970A37 /* Error.swift */; }; 31 | 8A51EA6328F8378900970A37 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A51EA6228F8378900970A37 /* Utilities.swift */; }; 32 | 8A51EA6528F839CB00970A37 /* MessageLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A51EA6428F839CB00970A37 /* MessageLabel.swift */; }; 33 | 8A51EA6728F839FE00970A37 /* Instruction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A51EA6628F839FE00970A37 /* Instruction.swift */; }; 34 | 8AF8B03E28FC2C0B00ED4897 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AF8B03D28FC2C0B00ED4897 /* Utilities.swift */; }; 35 | OBJ_21 /* Yokoso.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_9 /* Yokoso.swift */; }; 36 | OBJ_28 /* Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_6 /* Package.swift */; }; 37 | /* End PBXBuildFile section */ 38 | 39 | /* Begin PBXContainerItemProxy section */ 40 | 8A51EA5E28F6DF1300970A37 /* PBXContainerItemProxy */ = { 41 | isa = PBXContainerItemProxy; 42 | containerPortal = OBJ_1 /* Project object */; 43 | proxyType = 1; 44 | remoteGlobalIDString = "spotlight::Spotlight"; 45 | remoteInfo = Spotlight; 46 | }; 47 | /* End PBXContainerItemProxy section */ 48 | 49 | /* Begin PBXFileReference section */ 50 | 8A51EA4828F6DD0C00970A37 /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | 8A51EA4A28F6DD0C00970A37 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 52 | 8A51EA4C28F6DD0C00970A37 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 53 | 8A51EA4E28F6DD0C00970A37 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 54 | 8A51EA5128F6DD0C00970A37 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 55 | 8A51EA5328F6DD0D00970A37 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 56 | 8A51EA5628F6DD0D00970A37 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 57 | 8A51EA5828F6DD0D00970A37 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 58 | 8A51EA5C28F6DE9100970A37 /* OverlayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverlayView.swift; sourceTree = ""; }; 59 | 8A51EA6028F82B2200970A37 /* Error.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Error.swift; sourceTree = ""; }; 60 | 8A51EA6228F8378900970A37 /* Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utilities.swift; sourceTree = ""; }; 61 | 8A51EA6428F839CB00970A37 /* MessageLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageLabel.swift; sourceTree = ""; }; 62 | 8A51EA6628F839FE00970A37 /* Instruction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Instruction.swift; sourceTree = ""; }; 63 | 8AF8B03C28FC249D00ED4897 /* Yokoso_Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Yokoso_Info.plist; path = Sources/Yokoso_Info.plist; sourceTree = ""; }; 64 | 8AF8B03D28FC2C0B00ED4897 /* Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utilities.swift; sourceTree = ""; }; 65 | OBJ_6 /* Package.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; 66 | OBJ_9 /* Yokoso.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Yokoso.swift; sourceTree = ""; }; 67 | "spotlight::Spotlight::Product" /* Yokoso.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Yokoso.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 68 | /* End PBXFileReference section */ 69 | 70 | /* Begin PBXFrameworksBuildPhase section */ 71 | 8A51EA4528F6DD0C00970A37 /* Frameworks */ = { 72 | isa = PBXFrameworksBuildPhase; 73 | buildActionMask = 2147483647; 74 | files = ( 75 | ); 76 | runOnlyForDeploymentPostprocessing = 0; 77 | }; 78 | OBJ_22 /* Frameworks */ = { 79 | isa = PBXFrameworksBuildPhase; 80 | buildActionMask = 0; 81 | files = ( 82 | ); 83 | runOnlyForDeploymentPostprocessing = 0; 84 | }; 85 | /* End PBXFrameworksBuildPhase section */ 86 | 87 | /* Begin PBXGroup section */ 88 | 8A51EA4928F6DD0C00970A37 /* Example */ = { 89 | isa = PBXGroup; 90 | children = ( 91 | 8A51EA4A28F6DD0C00970A37 /* AppDelegate.swift */, 92 | 8A51EA4C28F6DD0C00970A37 /* SceneDelegate.swift */, 93 | 8A51EA4E28F6DD0C00970A37 /* ViewController.swift */, 94 | 8A51EA5028F6DD0C00970A37 /* Main.storyboard */, 95 | 8A51EA5328F6DD0D00970A37 /* Assets.xcassets */, 96 | 8A51EA5528F6DD0D00970A37 /* LaunchScreen.storyboard */, 97 | 8A51EA5828F6DD0D00970A37 /* Info.plist */, 98 | 8AF8B03D28FC2C0B00ED4897 /* Utilities.swift */, 99 | ); 100 | path = Example; 101 | sourceTree = ""; 102 | }; 103 | OBJ_13 /* Products */ = { 104 | isa = PBXGroup; 105 | children = ( 106 | "spotlight::Spotlight::Product" /* Yokoso.framework */, 107 | 8A51EA4828F6DD0C00970A37 /* Example.app */, 108 | ); 109 | name = Products; 110 | sourceTree = BUILT_PRODUCTS_DIR; 111 | }; 112 | OBJ_5 = { 113 | isa = PBXGroup; 114 | children = ( 115 | OBJ_6 /* Package.swift */, 116 | OBJ_7 /* Sources */, 117 | 8A51EA4928F6DD0C00970A37 /* Example */, 118 | OBJ_13 /* Products */, 119 | ); 120 | sourceTree = ""; 121 | }; 122 | OBJ_7 /* Sources */ = { 123 | isa = PBXGroup; 124 | children = ( 125 | 8AF8B03C28FC249D00ED4897 /* Yokoso_Info.plist */, 126 | OBJ_8 /* Yokoso */, 127 | ); 128 | name = Sources; 129 | sourceTree = SOURCE_ROOT; 130 | }; 131 | OBJ_8 /* Yokoso */ = { 132 | isa = PBXGroup; 133 | children = ( 134 | OBJ_9 /* Yokoso.swift */, 135 | 8A51EA6628F839FE00970A37 /* Instruction.swift */, 136 | 8A51EA6428F839CB00970A37 /* MessageLabel.swift */, 137 | 8A51EA6228F8378900970A37 /* Utilities.swift */, 138 | 8A51EA6028F82B2200970A37 /* Error.swift */, 139 | 8A51EA5C28F6DE9100970A37 /* OverlayView.swift */, 140 | ); 141 | name = Yokoso; 142 | path = Sources/Yokoso; 143 | sourceTree = SOURCE_ROOT; 144 | }; 145 | /* End PBXGroup section */ 146 | 147 | /* Begin PBXNativeTarget section */ 148 | 8A51EA4728F6DD0C00970A37 /* Example */ = { 149 | isa = PBXNativeTarget; 150 | buildConfigurationList = 8A51EA5928F6DD0D00970A37 /* Build configuration list for PBXNativeTarget "Example" */; 151 | buildPhases = ( 152 | 8A51EA4428F6DD0C00970A37 /* Sources */, 153 | 8A51EA4528F6DD0C00970A37 /* Frameworks */, 154 | 8A51EA4628F6DD0C00970A37 /* Resources */, 155 | ); 156 | buildRules = ( 157 | ); 158 | dependencies = ( 159 | 8A51EA5F28F6DF1300970A37 /* PBXTargetDependency */, 160 | ); 161 | name = Example; 162 | productName = Example; 163 | productReference = 8A51EA4828F6DD0C00970A37 /* Example.app */; 164 | productType = "com.apple.product-type.application"; 165 | }; 166 | "spotlight::Spotlight" /* Yokoso */ = { 167 | isa = PBXNativeTarget; 168 | buildConfigurationList = OBJ_17 /* Build configuration list for PBXNativeTarget "Yokoso" */; 169 | buildPhases = ( 170 | OBJ_20 /* Sources */, 171 | OBJ_22 /* Frameworks */, 172 | ); 173 | buildRules = ( 174 | ); 175 | dependencies = ( 176 | ); 177 | name = Yokoso; 178 | productName = Spotlight; 179 | productReference = "spotlight::Spotlight::Product" /* Yokoso.framework */; 180 | productType = "com.apple.product-type.framework"; 181 | }; 182 | "spotlight::SwiftPMPackageDescription" /* SpotlightPackageDescription */ = { 183 | isa = PBXNativeTarget; 184 | buildConfigurationList = OBJ_24 /* Build configuration list for PBXNativeTarget "SpotlightPackageDescription" */; 185 | buildPhases = ( 186 | OBJ_27 /* Sources */, 187 | ); 188 | buildRules = ( 189 | ); 190 | dependencies = ( 191 | ); 192 | name = SpotlightPackageDescription; 193 | productName = SpotlightPackageDescription; 194 | productType = "com.apple.product-type.framework"; 195 | }; 196 | /* End PBXNativeTarget section */ 197 | 198 | /* Begin PBXProject section */ 199 | OBJ_1 /* Project object */ = { 200 | isa = PBXProject; 201 | attributes = { 202 | LastSwiftMigration = 9999; 203 | LastSwiftUpdateCheck = 1400; 204 | LastUpgradeCheck = 9999; 205 | TargetAttributes = { 206 | 8A51EA4728F6DD0C00970A37 = { 207 | CreatedOnToolsVersion = 14.0.1; 208 | ProvisioningStyle = Automatic; 209 | }; 210 | }; 211 | }; 212 | buildConfigurationList = OBJ_2 /* Build configuration list for PBXProject "Yokoso" */; 213 | compatibilityVersion = "Xcode 3.2"; 214 | developmentRegion = en; 215 | hasScannedForEncodings = 0; 216 | knownRegions = ( 217 | en, 218 | Base, 219 | ); 220 | mainGroup = OBJ_5; 221 | productRefGroup = OBJ_13 /* Products */; 222 | projectDirPath = ""; 223 | projectRoot = ""; 224 | targets = ( 225 | "spotlight::Spotlight" /* Yokoso */, 226 | "spotlight::SwiftPMPackageDescription" /* SpotlightPackageDescription */, 227 | "spotlight::SpotlightPackageTests::ProductTarget" /* SpotlightPackageTests */, 228 | 8A51EA4728F6DD0C00970A37 /* Example */, 229 | ); 230 | }; 231 | /* End PBXProject section */ 232 | 233 | /* Begin PBXResourcesBuildPhase section */ 234 | 8A51EA4628F6DD0C00970A37 /* Resources */ = { 235 | isa = PBXResourcesBuildPhase; 236 | buildActionMask = 2147483647; 237 | files = ( 238 | 8A51EA5728F6DD0D00970A37 /* LaunchScreen.storyboard in Resources */, 239 | 8A51EA5428F6DD0D00970A37 /* Assets.xcassets in Resources */, 240 | 8A51EA5228F6DD0C00970A37 /* Main.storyboard in Resources */, 241 | ); 242 | runOnlyForDeploymentPostprocessing = 0; 243 | }; 244 | /* End PBXResourcesBuildPhase section */ 245 | 246 | /* Begin PBXSourcesBuildPhase section */ 247 | 8A51EA4428F6DD0C00970A37 /* Sources */ = { 248 | isa = PBXSourcesBuildPhase; 249 | buildActionMask = 2147483647; 250 | files = ( 251 | 8A51EA4F28F6DD0C00970A37 /* ViewController.swift in Sources */, 252 | 8A51EA4B28F6DD0C00970A37 /* AppDelegate.swift in Sources */, 253 | 8AF8B03E28FC2C0B00ED4897 /* Utilities.swift in Sources */, 254 | 8A51EA4D28F6DD0C00970A37 /* SceneDelegate.swift in Sources */, 255 | ); 256 | runOnlyForDeploymentPostprocessing = 0; 257 | }; 258 | OBJ_20 /* Sources */ = { 259 | isa = PBXSourcesBuildPhase; 260 | buildActionMask = 0; 261 | files = ( 262 | 8A51EA6728F839FE00970A37 /* Instruction.swift in Sources */, 263 | OBJ_21 /* Yokoso.swift in Sources */, 264 | 8A51EA6128F82B2200970A37 /* Error.swift in Sources */, 265 | 8A51EA6528F839CB00970A37 /* MessageLabel.swift in Sources */, 266 | 8A51EA6328F8378900970A37 /* Utilities.swift in Sources */, 267 | 8A51EA5D28F6DE9100970A37 /* OverlayView.swift in Sources */, 268 | ); 269 | runOnlyForDeploymentPostprocessing = 0; 270 | }; 271 | OBJ_27 /* Sources */ = { 272 | isa = PBXSourcesBuildPhase; 273 | buildActionMask = 0; 274 | files = ( 275 | OBJ_28 /* Package.swift in Sources */, 276 | ); 277 | runOnlyForDeploymentPostprocessing = 0; 278 | }; 279 | /* End PBXSourcesBuildPhase section */ 280 | 281 | /* Begin PBXTargetDependency section */ 282 | 8A51EA5F28F6DF1300970A37 /* PBXTargetDependency */ = { 283 | isa = PBXTargetDependency; 284 | target = "spotlight::Spotlight" /* Yokoso */; 285 | targetProxy = 8A51EA5E28F6DF1300970A37 /* PBXContainerItemProxy */; 286 | }; 287 | /* End PBXTargetDependency section */ 288 | 289 | /* Begin PBXVariantGroup section */ 290 | 8A51EA5028F6DD0C00970A37 /* Main.storyboard */ = { 291 | isa = PBXVariantGroup; 292 | children = ( 293 | 8A51EA5128F6DD0C00970A37 /* Base */, 294 | ); 295 | name = Main.storyboard; 296 | sourceTree = ""; 297 | }; 298 | 8A51EA5528F6DD0D00970A37 /* LaunchScreen.storyboard */ = { 299 | isa = PBXVariantGroup; 300 | children = ( 301 | 8A51EA5628F6DD0D00970A37 /* Base */, 302 | ); 303 | name = LaunchScreen.storyboard; 304 | sourceTree = ""; 305 | }; 306 | /* End PBXVariantGroup section */ 307 | 308 | /* Begin XCBuildConfiguration section */ 309 | 8A51EA5A28F6DD0D00970A37 /* Debug */ = { 310 | isa = XCBuildConfiguration; 311 | buildSettings = { 312 | ALWAYS_SEARCH_USER_PATHS = NO; 313 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 314 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 315 | CLANG_ANALYZER_NONNULL = YES; 316 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 317 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 318 | CLANG_ENABLE_MODULES = YES; 319 | CLANG_ENABLE_OBJC_WEAK = YES; 320 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 321 | CLANG_WARN_BOOL_CONVERSION = YES; 322 | CLANG_WARN_COMMA = YES; 323 | CLANG_WARN_CONSTANT_CONVERSION = YES; 324 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 325 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 326 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 327 | CLANG_WARN_EMPTY_BODY = YES; 328 | CLANG_WARN_ENUM_CONVERSION = YES; 329 | CLANG_WARN_INFINITE_RECURSION = YES; 330 | CLANG_WARN_INT_CONVERSION = YES; 331 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 332 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 333 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 334 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 335 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 336 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 337 | CLANG_WARN_STRICT_PROTOTYPES = YES; 338 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 339 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 340 | CLANG_WARN_UNREACHABLE_CODE = YES; 341 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 342 | CODE_SIGN_STYLE = Automatic; 343 | CURRENT_PROJECT_VERSION = 1; 344 | ENABLE_STRICT_OBJC_MSGSEND = YES; 345 | ENABLE_TESTABILITY = YES; 346 | GCC_C_LANGUAGE_STANDARD = gnu11; 347 | GCC_DYNAMIC_NO_PIC = NO; 348 | GCC_NO_COMMON_BLOCKS = YES; 349 | GCC_PREPROCESSOR_DEFINITIONS = ( 350 | "DEBUG=1", 351 | "$(inherited)", 352 | ); 353 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 354 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 355 | GCC_WARN_UNDECLARED_SELECTOR = YES; 356 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 357 | GCC_WARN_UNUSED_FUNCTION = YES; 358 | GCC_WARN_UNUSED_VARIABLE = YES; 359 | GENERATE_INFOPLIST_FILE = YES; 360 | INFOPLIST_FILE = Example/Info.plist; 361 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 362 | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; 363 | INFOPLIST_KEY_UIMainStoryboardFile = Main; 364 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 365 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 366 | IPHONEOS_DEPLOYMENT_TARGET = 16.0; 367 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 368 | MARKETING_VERSION = 1.0; 369 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 370 | MTL_FAST_MATH = YES; 371 | PRODUCT_BUNDLE_IDENTIFIER = jp.toshi0383.Example; 372 | PRODUCT_NAME = "$(TARGET_NAME)"; 373 | SDKROOT = iphoneos; 374 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 375 | SWIFT_EMIT_LOC_STRINGS = YES; 376 | SWIFT_VERSION = 5.0; 377 | TARGETED_DEVICE_FAMILY = "1,2"; 378 | }; 379 | name = Debug; 380 | }; 381 | 8A51EA5B28F6DD0D00970A37 /* Release */ = { 382 | isa = XCBuildConfiguration; 383 | buildSettings = { 384 | ALWAYS_SEARCH_USER_PATHS = NO; 385 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 386 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 387 | CLANG_ANALYZER_NONNULL = YES; 388 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 389 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 390 | CLANG_ENABLE_MODULES = YES; 391 | CLANG_ENABLE_OBJC_WEAK = YES; 392 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 393 | CLANG_WARN_BOOL_CONVERSION = YES; 394 | CLANG_WARN_COMMA = YES; 395 | CLANG_WARN_CONSTANT_CONVERSION = YES; 396 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 397 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 398 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 399 | CLANG_WARN_EMPTY_BODY = YES; 400 | CLANG_WARN_ENUM_CONVERSION = YES; 401 | CLANG_WARN_INFINITE_RECURSION = YES; 402 | CLANG_WARN_INT_CONVERSION = YES; 403 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 404 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 405 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 406 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 407 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 408 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 409 | CLANG_WARN_STRICT_PROTOTYPES = YES; 410 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 411 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 412 | CLANG_WARN_UNREACHABLE_CODE = YES; 413 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 414 | CODE_SIGN_STYLE = Automatic; 415 | COPY_PHASE_STRIP = NO; 416 | CURRENT_PROJECT_VERSION = 1; 417 | ENABLE_NS_ASSERTIONS = NO; 418 | ENABLE_STRICT_OBJC_MSGSEND = YES; 419 | GCC_C_LANGUAGE_STANDARD = gnu11; 420 | GCC_NO_COMMON_BLOCKS = YES; 421 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 422 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 423 | GCC_WARN_UNDECLARED_SELECTOR = YES; 424 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 425 | GCC_WARN_UNUSED_FUNCTION = YES; 426 | GCC_WARN_UNUSED_VARIABLE = YES; 427 | GENERATE_INFOPLIST_FILE = YES; 428 | INFOPLIST_FILE = Example/Info.plist; 429 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 430 | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; 431 | INFOPLIST_KEY_UIMainStoryboardFile = Main; 432 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 433 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 434 | IPHONEOS_DEPLOYMENT_TARGET = 16.0; 435 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 436 | MARKETING_VERSION = 1.0; 437 | MTL_ENABLE_DEBUG_INFO = NO; 438 | MTL_FAST_MATH = YES; 439 | PRODUCT_BUNDLE_IDENTIFIER = jp.toshi0383.Example; 440 | PRODUCT_NAME = "$(TARGET_NAME)"; 441 | SDKROOT = iphoneos; 442 | SWIFT_EMIT_LOC_STRINGS = YES; 443 | SWIFT_VERSION = 5.0; 444 | TARGETED_DEVICE_FAMILY = "1,2"; 445 | VALIDATE_PRODUCT = YES; 446 | }; 447 | name = Release; 448 | }; 449 | OBJ_18 /* Debug */ = { 450 | isa = XCBuildConfiguration; 451 | buildSettings = { 452 | CURRENT_PROJECT_VERSION = 1; 453 | DRIVERKIT_DEPLOYMENT_TARGET = 19.0; 454 | ENABLE_TESTABILITY = YES; 455 | FRAMEWORK_SEARCH_PATHS = ( 456 | "$(inherited)", 457 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 458 | ); 459 | HEADER_SEARCH_PATHS = "$(inherited)"; 460 | INFOPLIST_FILE = Sources/Yokoso_Info.plist; 461 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 462 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift/macosx"; 463 | MACOSX_DEPLOYMENT_TARGET = 10.13; 464 | OTHER_CFLAGS = "$(inherited)"; 465 | OTHER_LDFLAGS = "$(inherited)"; 466 | OTHER_SWIFT_FLAGS = "$(inherited)"; 467 | PRODUCT_BUNDLE_IDENTIFIER = Yokoso; 468 | PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; 469 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 470 | SKIP_INSTALL = YES; 471 | SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; 472 | SUPPORTS_MACCATALYST = NO; 473 | SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; 474 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; 475 | SWIFT_VERSION = 5.0; 476 | TARGETED_DEVICE_FAMILY = "1,2"; 477 | TARGET_NAME = Yokoso; 478 | TVOS_DEPLOYMENT_TARGET = 11.0; 479 | WATCHOS_DEPLOYMENT_TARGET = 4.0; 480 | }; 481 | name = Debug; 482 | }; 483 | OBJ_19 /* Release */ = { 484 | isa = XCBuildConfiguration; 485 | buildSettings = { 486 | CURRENT_PROJECT_VERSION = 1; 487 | DRIVERKIT_DEPLOYMENT_TARGET = 19.0; 488 | ENABLE_TESTABILITY = YES; 489 | FRAMEWORK_SEARCH_PATHS = ( 490 | "$(inherited)", 491 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 492 | ); 493 | HEADER_SEARCH_PATHS = "$(inherited)"; 494 | INFOPLIST_FILE = Sources/Yokoso_Info.plist; 495 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 496 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift/macosx"; 497 | MACOSX_DEPLOYMENT_TARGET = 10.13; 498 | OTHER_CFLAGS = "$(inherited)"; 499 | OTHER_LDFLAGS = "$(inherited)"; 500 | OTHER_SWIFT_FLAGS = "$(inherited)"; 501 | PRODUCT_BUNDLE_IDENTIFIER = Yokoso; 502 | PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; 503 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 504 | SKIP_INSTALL = YES; 505 | SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; 506 | SUPPORTS_MACCATALYST = NO; 507 | SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; 508 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; 509 | SWIFT_VERSION = 5.0; 510 | TARGETED_DEVICE_FAMILY = "1,2"; 511 | TARGET_NAME = Yokoso; 512 | TVOS_DEPLOYMENT_TARGET = 11.0; 513 | WATCHOS_DEPLOYMENT_TARGET = 4.0; 514 | }; 515 | name = Release; 516 | }; 517 | OBJ_25 /* Debug */ = { 518 | isa = XCBuildConfiguration; 519 | buildSettings = { 520 | LD = /usr/bin/true; 521 | OTHER_SWIFT_FLAGS = "-swift-version 5 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/ManifestAPI -sdk /Applications/Xcode14.0.1RC.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk -package-description-version 5.7.0"; 522 | SWIFT_VERSION = 5.0; 523 | }; 524 | name = Debug; 525 | }; 526 | OBJ_26 /* Release */ = { 527 | isa = XCBuildConfiguration; 528 | buildSettings = { 529 | LD = /usr/bin/true; 530 | OTHER_SWIFT_FLAGS = "-swift-version 5 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/ManifestAPI -sdk /Applications/Xcode14.0.1RC.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk -package-description-version 5.7.0"; 531 | SWIFT_VERSION = 5.0; 532 | }; 533 | name = Release; 534 | }; 535 | OBJ_3 /* Debug */ = { 536 | isa = XCBuildConfiguration; 537 | buildSettings = { 538 | CLANG_ENABLE_OBJC_ARC = YES; 539 | COMBINE_HIDPI_IMAGES = YES; 540 | COPY_PHASE_STRIP = NO; 541 | DEBUG_INFORMATION_FORMAT = dwarf; 542 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 543 | ENABLE_NS_ASSERTIONS = YES; 544 | GCC_OPTIMIZATION_LEVEL = 0; 545 | GCC_PREPROCESSOR_DEFINITIONS = ( 546 | "$(inherited)", 547 | "SWIFT_PACKAGE=1", 548 | "DEBUG=1", 549 | ); 550 | MACOSX_DEPLOYMENT_TARGET = 10.10; 551 | ONLY_ACTIVE_ARCH = YES; 552 | OTHER_SWIFT_FLAGS = "$(inherited) -DXcode"; 553 | PRODUCT_NAME = "$(TARGET_NAME)"; 554 | SDKROOT = macosx; 555 | SUPPORTED_PLATFORMS = "$(AVAILABLE_PLATFORMS)"; 556 | SUPPORTS_MACCATALYST = YES; 557 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) SWIFT_PACKAGE DEBUG"; 558 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 559 | USE_HEADERMAP = NO; 560 | }; 561 | name = Debug; 562 | }; 563 | OBJ_31 /* Debug */ = { 564 | isa = XCBuildConfiguration; 565 | buildSettings = { 566 | }; 567 | name = Debug; 568 | }; 569 | OBJ_32 /* Release */ = { 570 | isa = XCBuildConfiguration; 571 | buildSettings = { 572 | }; 573 | name = Release; 574 | }; 575 | OBJ_4 /* Release */ = { 576 | isa = XCBuildConfiguration; 577 | buildSettings = { 578 | CLANG_ENABLE_OBJC_ARC = YES; 579 | COMBINE_HIDPI_IMAGES = YES; 580 | COPY_PHASE_STRIP = YES; 581 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 582 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 583 | GCC_OPTIMIZATION_LEVEL = s; 584 | GCC_PREPROCESSOR_DEFINITIONS = ( 585 | "$(inherited)", 586 | "SWIFT_PACKAGE=1", 587 | ); 588 | MACOSX_DEPLOYMENT_TARGET = 10.10; 589 | OTHER_SWIFT_FLAGS = "$(inherited) -DXcode"; 590 | PRODUCT_NAME = "$(TARGET_NAME)"; 591 | SDKROOT = macosx; 592 | SUPPORTED_PLATFORMS = "$(AVAILABLE_PLATFORMS)"; 593 | SUPPORTS_MACCATALYST = YES; 594 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) SWIFT_PACKAGE"; 595 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 596 | USE_HEADERMAP = NO; 597 | }; 598 | name = Release; 599 | }; 600 | /* End XCBuildConfiguration section */ 601 | 602 | /* Begin XCConfigurationList section */ 603 | 8A51EA5928F6DD0D00970A37 /* Build configuration list for PBXNativeTarget "Example" */ = { 604 | isa = XCConfigurationList; 605 | buildConfigurations = ( 606 | 8A51EA5A28F6DD0D00970A37 /* Debug */, 607 | 8A51EA5B28F6DD0D00970A37 /* Release */, 608 | ); 609 | defaultConfigurationIsVisible = 0; 610 | defaultConfigurationName = Release; 611 | }; 612 | OBJ_17 /* Build configuration list for PBXNativeTarget "Yokoso" */ = { 613 | isa = XCConfigurationList; 614 | buildConfigurations = ( 615 | OBJ_18 /* Debug */, 616 | OBJ_19 /* Release */, 617 | ); 618 | defaultConfigurationIsVisible = 0; 619 | defaultConfigurationName = Release; 620 | }; 621 | OBJ_2 /* Build configuration list for PBXProject "Yokoso" */ = { 622 | isa = XCConfigurationList; 623 | buildConfigurations = ( 624 | OBJ_3 /* Debug */, 625 | OBJ_4 /* Release */, 626 | ); 627 | defaultConfigurationIsVisible = 0; 628 | defaultConfigurationName = Release; 629 | }; 630 | OBJ_24 /* Build configuration list for PBXNativeTarget "SpotlightPackageDescription" */ = { 631 | isa = XCConfigurationList; 632 | buildConfigurations = ( 633 | OBJ_25 /* Debug */, 634 | OBJ_26 /* Release */, 635 | ); 636 | defaultConfigurationIsVisible = 0; 637 | defaultConfigurationName = Release; 638 | }; 639 | OBJ_30 /* Build configuration list for PBXAggregateTarget "SpotlightPackageTests" */ = { 640 | isa = XCConfigurationList; 641 | buildConfigurations = ( 642 | OBJ_31 /* Debug */, 643 | OBJ_32 /* Release */, 644 | ); 645 | defaultConfigurationIsVisible = 0; 646 | defaultConfigurationName = Release; 647 | }; 648 | /* End XCConfigurationList section */ 649 | }; 650 | rootObject = OBJ_1 /* Project object */; 651 | } 652 | -------------------------------------------------------------------------------- /Yokoso.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /Yokoso.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Yokoso.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded 6 | 7 | 8 | -------------------------------------------------------------------------------- /Yokoso.xcodeproj/xcshareddata/xcschemes/Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 60 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /Yokoso.xcodeproj/xcshareddata/xcschemes/Yokoso.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 57 | 58 | 59 | 60 | 62 | 63 | 66 | 67 | 68 | --------------------------------------------------------------------------------