├── .gitignore ├── LICENSE ├── README.md ├── SandboxKit.podspec ├── SandboxKit.xcodeproj ├── project.pbxproj └── xcshareddata │ └── xcschemes │ ├── SandboxKitDemo.xcscheme │ └── SandboxKitDemoMiniApp.xcscheme ├── SandboxKit ├── HTTPMethod.swift ├── Info.plist ├── Mock.swift ├── ResponseStubbable.swift ├── SandboxKit.h ├── SandboxSceneSelectTableViewController.swift └── Scene.swift └── SandboxKitDemo ├── AppDelegate.swift ├── Assets.xcassets ├── AccentColor.colorset │ └── Contents.json ├── AppIcon.appiconset │ └── Contents.json └── Contents.json ├── Base.lproj └── LaunchScreen.storyboard ├── Info.plist ├── Main ├── API │ ├── APIRequest.swift │ ├── MainServiceClient.swift │ ├── Request │ │ ├── PokemonListRequest.swift │ │ └── PokemonRequest.swift │ ├── ServiceClient.swift │ └── ServiceClientProvider.swift ├── Model │ ├── Pokemon.swift │ └── PokemonType.swift ├── PokemonDetailViewController.swift └── PokemonListViewController.swift └── MiniApp ├── MiniAppMainViewController.swift ├── ServiceClientStub.swift ├── StubbableServiceClient.swift └── fixtures ├── pokemon.json └── pokemons.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | 3 | ## User settings 4 | xcuserdata/ 5 | 6 | ## Playgrounds 7 | timeline.xctimeline 8 | playground.xcworkspace 9 | 10 | .build/ 11 | 12 | # CocoaPods 13 | Pods/ 14 | *.xcworkspace 15 | 16 | # Carthage 17 | Carthage/Checkouts 18 | Carthage/Build/ 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Aoi Okawa 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SandboxKit 2 | 3 | This framework makes debugging more efficient in your application. 4 | 5 | Sandbox is the name of a structure that improves the efficiency of debugging your application. 6 | 7 | Branch the startup Scene at the entry point of your application with using debug flag and the Scene that you want to debug is launched from the beginning. 8 | 9 | This framework is introduced in iOSDC 2021. 10 | ## Installation 11 | 12 | ### Cocoapods 13 | 14 | ``` 15 | use_frameworks! 16 | 17 | pod 'SandboxKit' 18 | ``` 19 | 20 | ## Usage 21 | 22 | 1. Install `SandboxKit` in your application. 23 | 24 | 2. Add a new schema from `Manage Schemes` in Xcode, and specify the environment variable `RUNNING_SANDBOX` from the Argument item in the Run configuration. 25 | 26 | 3. In AppDelegate, check the existence of the environment variable from Prosess, and if it is started with the schema for the Sandbox, specify the rootViewController for Sandbox. 27 | 28 | ```swift 29 | class AppDelegate: UIResponder, UIApplicationDelegate { 30 | var window: UIWindow? 31 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 32 | if ProcessInfo.processInfo.environment["RUNNING_MINIAPP"] == "1" { 33 | window = UIWindow(frame: UIScreen.main.bounds) 34 | let viewController = ViewController { 35 | Scene( 36 | name: "PokemonDetail", 37 | PokemonDetailViewController(id: 133) 38 | ) { 39 | Mock(path: "/pokemons/133", statusCode: 200, method: .get) { 40 | JSONData(from: "pokemon") 41 | } 42 | } 43 | 44 | /// .... 45 | } 46 | window?.rootViewController = UINavigationController(rootViewController: viewController) 47 | window?.makeKeyAndVisible() 48 | return true 49 | } 50 | 51 | /// .... 52 | return true 53 | } 54 | } 55 | ``` 56 | 57 | 4. Inherit `SandboxKit.SandboxSceneSelectTableViewController` and implement your stub and your initialization. 58 | 59 | ```swift 60 | final class ViewController: SandboxSceneSelectTableViewController { 61 | override init(@SandboxScenes _ makeScenes: () -> [Scene]) { 62 | super.init(makeScenes) 63 | } 64 | 65 | override func initializeScene(_ scene: Scene) { 66 | /// .... 67 | } 68 | } 69 | ``` 70 | 71 | 5. Select Sandbox schema and launch your application. 72 | 73 | 74 | See `SandboxKitDemo` for detailed implementation. 75 | ## License 76 | 77 | SandboxKit is released under the MIT License. 78 | -------------------------------------------------------------------------------- /SandboxKit.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |spec| 2 | spec.name = "SandboxKit" 3 | spec.version = "0.1.0" 4 | spec.summary = "Framework that makes it easy to launch a single Scene of your application" 5 | spec.homepage = "https://github.com/aomathwift/SandboxKit" 6 | spec.license = { :type => "MIT", :file => "LICENSE" } 7 | spec.author = { "aomathwift" => "a.okw1011@gmail.com" } 8 | spec.social_media_url = "https://twitter.com/aomathwift" 9 | spec.platform = :ios 10 | spec.source = { :git => "https://github.com/aomathwift/SandboxKit.git", :tag => "#{spec.version}" } 11 | spec.source_files = "SandboxKit/*.{h,m}" 12 | spec.swift_version = "5.4" 13 | end 14 | -------------------------------------------------------------------------------- /SandboxKit.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2A20537826DD24E000C66AA1 /* SandboxKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 2A20536A26DD24DF00C66AA1 /* SandboxKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11 | 2A20538A26DEB6AF00C66AA1 /* SandboxSceneSelectTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A20538926DEB6AF00C66AA1 /* SandboxSceneSelectTableViewController.swift */; }; 12 | 2A20538E26DED7FD00C66AA1 /* HTTPMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A20538D26DED7FD00C66AA1 /* HTTPMethod.swift */; }; 13 | 2A20539026DED90000C66AA1 /* Scene.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A20538F26DED90000C66AA1 /* Scene.swift */; }; 14 | 2A20539226DED92900C66AA1 /* Mock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A20539126DED92900C66AA1 /* Mock.swift */; }; 15 | 2A20539A26DEE19B00C66AA1 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A20539926DEE19B00C66AA1 /* AppDelegate.swift */; }; 16 | 2A2053A326DEE19D00C66AA1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2A2053A226DEE19D00C66AA1 /* Assets.xcassets */; }; 17 | 2A2053A626DEE19D00C66AA1 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2A2053A426DEE19D00C66AA1 /* LaunchScreen.storyboard */; }; 18 | 2A2053AF26DEE70E00C66AA1 /* PokemonListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A2053AE26DEE70E00C66AA1 /* PokemonListViewController.swift */; }; 19 | 2A2053B226DEE7B600C66AA1 /* Pokemon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A2053B126DEE7B600C66AA1 /* Pokemon.swift */; }; 20 | 2A2053B426DEE7F000C66AA1 /* PokemonType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A2053B326DEE7F000C66AA1 /* PokemonType.swift */; }; 21 | 2A2053BA26DEE9C200C66AA1 /* PokemonDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A2053B926DEE9C200C66AA1 /* PokemonDetailViewController.swift */; }; 22 | 2A2053BE26DEEB2600C66AA1 /* ServiceClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A2053BD26DEEB2600C66AA1 /* ServiceClient.swift */; }; 23 | 2A2053C026DEEB6200C66AA1 /* APIRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A2053BF26DEEB6200C66AA1 /* APIRequest.swift */; }; 24 | 2A2053C226DEEBB400C66AA1 /* SandboxKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2A20536726DD24DF00C66AA1 /* SandboxKit.framework */; }; 25 | 2A2053C426DEEBEB00C66AA1 /* ServiceClientProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A2053C326DEEBEB00C66AA1 /* ServiceClientProvider.swift */; }; 26 | 2A2053C626DEEC2300C66AA1 /* MainServiceClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A2053C526DEEC2300C66AA1 /* MainServiceClient.swift */; }; 27 | 2A2053C826DEEC5B00C66AA1 /* StubbableServiceClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A2053C726DEEC5B00C66AA1 /* StubbableServiceClient.swift */; }; 28 | 2A2053CA26DEEC8B00C66AA1 /* MiniAppMainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A2053C926DEEC8B00C66AA1 /* MiniAppMainViewController.swift */; }; 29 | 2A2053CD26DEEDC100C66AA1 /* PokemonListRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A2053CC26DEEDC100C66AA1 /* PokemonListRequest.swift */; }; 30 | 2A2053CF26DEEE6900C66AA1 /* PokemonRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A2053CE26DEEE6900C66AA1 /* PokemonRequest.swift */; }; 31 | 2A2053D126DEF1FD00C66AA1 /* pokemon.json in Resources */ = {isa = PBXBuildFile; fileRef = 2A2053D026DEF1FD00C66AA1 /* pokemon.json */; }; 32 | 2A3471EF26F23CE000B8226B /* ServiceClientStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A3471EE26F23CE000B8226B /* ServiceClientStub.swift */; }; 33 | 2A3471F226F2418100B8226B /* ResponseStubbable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A3471F126F2418100B8226B /* ResponseStubbable.swift */; }; 34 | 2A3471F426F2507C00B8226B /* pokemons.json in Resources */ = {isa = PBXBuildFile; fileRef = 2A3471F326F2507C00B8226B /* pokemons.json */; }; 35 | /* End PBXBuildFile section */ 36 | 37 | /* Begin PBXFileReference section */ 38 | 2A20536726DD24DF00C66AA1 /* SandboxKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SandboxKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 39 | 2A20536A26DD24DF00C66AA1 /* SandboxKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SandboxKit.h; sourceTree = ""; }; 40 | 2A20536B26DD24DF00C66AA1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 41 | 2A20538926DEB6AF00C66AA1 /* SandboxSceneSelectTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SandboxSceneSelectTableViewController.swift; sourceTree = ""; }; 42 | 2A20538D26DED7FD00C66AA1 /* HTTPMethod.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPMethod.swift; sourceTree = ""; }; 43 | 2A20538F26DED90000C66AA1 /* Scene.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Scene.swift; sourceTree = ""; }; 44 | 2A20539126DED92900C66AA1 /* Mock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Mock.swift; sourceTree = ""; }; 45 | 2A20539726DEE19B00C66AA1 /* SandboxKitDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SandboxKitDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 46 | 2A20539926DEE19B00C66AA1 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 47 | 2A2053A226DEE19D00C66AA1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 48 | 2A2053A526DEE19D00C66AA1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 49 | 2A2053A726DEE19D00C66AA1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50 | 2A2053AE26DEE70E00C66AA1 /* PokemonListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PokemonListViewController.swift; sourceTree = ""; }; 51 | 2A2053B126DEE7B600C66AA1 /* Pokemon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Pokemon.swift; sourceTree = ""; }; 52 | 2A2053B326DEE7F000C66AA1 /* PokemonType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PokemonType.swift; sourceTree = ""; }; 53 | 2A2053B926DEE9C200C66AA1 /* PokemonDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PokemonDetailViewController.swift; sourceTree = ""; }; 54 | 2A2053BD26DEEB2600C66AA1 /* ServiceClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServiceClient.swift; sourceTree = ""; }; 55 | 2A2053BF26DEEB6200C66AA1 /* APIRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIRequest.swift; sourceTree = ""; }; 56 | 2A2053C326DEEBEB00C66AA1 /* ServiceClientProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServiceClientProvider.swift; sourceTree = ""; }; 57 | 2A2053C526DEEC2300C66AA1 /* MainServiceClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainServiceClient.swift; sourceTree = ""; }; 58 | 2A2053C726DEEC5B00C66AA1 /* StubbableServiceClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StubbableServiceClient.swift; sourceTree = ""; }; 59 | 2A2053C926DEEC8B00C66AA1 /* MiniAppMainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MiniAppMainViewController.swift; sourceTree = ""; }; 60 | 2A2053CC26DEEDC100C66AA1 /* PokemonListRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PokemonListRequest.swift; sourceTree = ""; }; 61 | 2A2053CE26DEEE6900C66AA1 /* PokemonRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PokemonRequest.swift; sourceTree = ""; }; 62 | 2A2053D026DEF1FD00C66AA1 /* pokemon.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = pokemon.json; sourceTree = ""; }; 63 | 2A3471EE26F23CE000B8226B /* ServiceClientStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServiceClientStub.swift; sourceTree = ""; }; 64 | 2A3471F126F2418100B8226B /* ResponseStubbable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResponseStubbable.swift; sourceTree = ""; }; 65 | 2A3471F326F2507C00B8226B /* pokemons.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = pokemons.json; sourceTree = ""; }; 66 | /* End PBXFileReference section */ 67 | 68 | /* Begin PBXFrameworksBuildPhase section */ 69 | 2A20536426DD24DF00C66AA1 /* Frameworks */ = { 70 | isa = PBXFrameworksBuildPhase; 71 | buildActionMask = 2147483647; 72 | files = ( 73 | ); 74 | runOnlyForDeploymentPostprocessing = 0; 75 | }; 76 | 2A20539426DEE19B00C66AA1 /* Frameworks */ = { 77 | isa = PBXFrameworksBuildPhase; 78 | buildActionMask = 2147483647; 79 | files = ( 80 | 2A2053C226DEEBB400C66AA1 /* SandboxKit.framework in Frameworks */, 81 | ); 82 | runOnlyForDeploymentPostprocessing = 0; 83 | }; 84 | /* End PBXFrameworksBuildPhase section */ 85 | 86 | /* Begin PBXGroup section */ 87 | 2A20535D26DD24DF00C66AA1 = { 88 | isa = PBXGroup; 89 | children = ( 90 | 2A20536926DD24DF00C66AA1 /* SandboxKit */, 91 | 2A20539826DEE19B00C66AA1 /* SandboxKitDemo */, 92 | 2A20536826DD24DF00C66AA1 /* Products */, 93 | 2A2053C126DEEBB400C66AA1 /* Frameworks */, 94 | ); 95 | sourceTree = ""; 96 | }; 97 | 2A20536826DD24DF00C66AA1 /* Products */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | 2A20536726DD24DF00C66AA1 /* SandboxKit.framework */, 101 | 2A20539726DEE19B00C66AA1 /* SandboxKitDemo.app */, 102 | ); 103 | name = Products; 104 | sourceTree = ""; 105 | }; 106 | 2A20536926DD24DF00C66AA1 /* SandboxKit */ = { 107 | isa = PBXGroup; 108 | children = ( 109 | 2A20536A26DD24DF00C66AA1 /* SandboxKit.h */, 110 | 2A20536B26DD24DF00C66AA1 /* Info.plist */, 111 | 2A20538926DEB6AF00C66AA1 /* SandboxSceneSelectTableViewController.swift */, 112 | 2A20538D26DED7FD00C66AA1 /* HTTPMethod.swift */, 113 | 2A20538F26DED90000C66AA1 /* Scene.swift */, 114 | 2A20539126DED92900C66AA1 /* Mock.swift */, 115 | 2A3471F126F2418100B8226B /* ResponseStubbable.swift */, 116 | ); 117 | path = SandboxKit; 118 | sourceTree = ""; 119 | }; 120 | 2A20539826DEE19B00C66AA1 /* SandboxKitDemo */ = { 121 | isa = PBXGroup; 122 | children = ( 123 | 2A2053BB26DEEAE000C66AA1 /* MiniApp */, 124 | 2A2053AB26DEE53E00C66AA1 /* Main */, 125 | 2A20539926DEE19B00C66AA1 /* AppDelegate.swift */, 126 | 2A2053A226DEE19D00C66AA1 /* Assets.xcassets */, 127 | 2A2053A426DEE19D00C66AA1 /* LaunchScreen.storyboard */, 128 | 2A2053A726DEE19D00C66AA1 /* Info.plist */, 129 | ); 130 | path = SandboxKitDemo; 131 | sourceTree = ""; 132 | }; 133 | 2A2053AB26DEE53E00C66AA1 /* Main */ = { 134 | isa = PBXGroup; 135 | children = ( 136 | 2A2053BC26DEEAF900C66AA1 /* API */, 137 | 2A2053B026DEE78A00C66AA1 /* Model */, 138 | 2A2053AE26DEE70E00C66AA1 /* PokemonListViewController.swift */, 139 | 2A2053B926DEE9C200C66AA1 /* PokemonDetailViewController.swift */, 140 | ); 141 | path = Main; 142 | sourceTree = ""; 143 | }; 144 | 2A2053B026DEE78A00C66AA1 /* Model */ = { 145 | isa = PBXGroup; 146 | children = ( 147 | 2A2053B126DEE7B600C66AA1 /* Pokemon.swift */, 148 | 2A2053B326DEE7F000C66AA1 /* PokemonType.swift */, 149 | ); 150 | path = Model; 151 | sourceTree = ""; 152 | }; 153 | 2A2053BB26DEEAE000C66AA1 /* MiniApp */ = { 154 | isa = PBXGroup; 155 | children = ( 156 | 2A3471F026F23DCD00B8226B /* fixtures */, 157 | 2A2053C726DEEC5B00C66AA1 /* StubbableServiceClient.swift */, 158 | 2A2053C926DEEC8B00C66AA1 /* MiniAppMainViewController.swift */, 159 | 2A3471EE26F23CE000B8226B /* ServiceClientStub.swift */, 160 | ); 161 | path = MiniApp; 162 | sourceTree = ""; 163 | }; 164 | 2A2053BC26DEEAF900C66AA1 /* API */ = { 165 | isa = PBXGroup; 166 | children = ( 167 | 2A2053CB26DEEDAF00C66AA1 /* Request */, 168 | 2A2053BD26DEEB2600C66AA1 /* ServiceClient.swift */, 169 | 2A2053BF26DEEB6200C66AA1 /* APIRequest.swift */, 170 | 2A2053C326DEEBEB00C66AA1 /* ServiceClientProvider.swift */, 171 | 2A2053C526DEEC2300C66AA1 /* MainServiceClient.swift */, 172 | ); 173 | path = API; 174 | sourceTree = ""; 175 | }; 176 | 2A2053C126DEEBB400C66AA1 /* Frameworks */ = { 177 | isa = PBXGroup; 178 | children = ( 179 | ); 180 | name = Frameworks; 181 | sourceTree = ""; 182 | }; 183 | 2A2053CB26DEEDAF00C66AA1 /* Request */ = { 184 | isa = PBXGroup; 185 | children = ( 186 | 2A2053CC26DEEDC100C66AA1 /* PokemonListRequest.swift */, 187 | 2A2053CE26DEEE6900C66AA1 /* PokemonRequest.swift */, 188 | ); 189 | path = Request; 190 | sourceTree = ""; 191 | }; 192 | 2A3471F026F23DCD00B8226B /* fixtures */ = { 193 | isa = PBXGroup; 194 | children = ( 195 | 2A2053D026DEF1FD00C66AA1 /* pokemon.json */, 196 | 2A3471F326F2507C00B8226B /* pokemons.json */, 197 | ); 198 | path = fixtures; 199 | sourceTree = ""; 200 | }; 201 | /* End PBXGroup section */ 202 | 203 | /* Begin PBXHeadersBuildPhase section */ 204 | 2A20536226DD24DF00C66AA1 /* Headers */ = { 205 | isa = PBXHeadersBuildPhase; 206 | buildActionMask = 2147483647; 207 | files = ( 208 | 2A20537826DD24E000C66AA1 /* SandboxKit.h in Headers */, 209 | ); 210 | runOnlyForDeploymentPostprocessing = 0; 211 | }; 212 | /* End PBXHeadersBuildPhase section */ 213 | 214 | /* Begin PBXNativeTarget section */ 215 | 2A20536626DD24DF00C66AA1 /* SandboxKit */ = { 216 | isa = PBXNativeTarget; 217 | buildConfigurationList = 2A20537B26DD24E000C66AA1 /* Build configuration list for PBXNativeTarget "SandboxKit" */; 218 | buildPhases = ( 219 | 2A20536226DD24DF00C66AA1 /* Headers */, 220 | 2A20536326DD24DF00C66AA1 /* Sources */, 221 | 2A20536426DD24DF00C66AA1 /* Frameworks */, 222 | 2A20536526DD24DF00C66AA1 /* Resources */, 223 | ); 224 | buildRules = ( 225 | ); 226 | dependencies = ( 227 | ); 228 | name = SandboxKit; 229 | productName = SandboxKit; 230 | productReference = 2A20536726DD24DF00C66AA1 /* SandboxKit.framework */; 231 | productType = "com.apple.product-type.framework"; 232 | }; 233 | 2A20539626DEE19B00C66AA1 /* SandboxKitDemo */ = { 234 | isa = PBXNativeTarget; 235 | buildConfigurationList = 2A2053A826DEE19D00C66AA1 /* Build configuration list for PBXNativeTarget "SandboxKitDemo" */; 236 | buildPhases = ( 237 | 2A20539326DEE19B00C66AA1 /* Sources */, 238 | 2A20539426DEE19B00C66AA1 /* Frameworks */, 239 | 2A20539526DEE19B00C66AA1 /* Resources */, 240 | ); 241 | buildRules = ( 242 | ); 243 | dependencies = ( 244 | ); 245 | name = SandboxKitDemo; 246 | productName = SandboxKitDemo; 247 | productReference = 2A20539726DEE19B00C66AA1 /* SandboxKitDemo.app */; 248 | productType = "com.apple.product-type.application"; 249 | }; 250 | /* End PBXNativeTarget section */ 251 | 252 | /* Begin PBXProject section */ 253 | 2A20535E26DD24DF00C66AA1 /* Project object */ = { 254 | isa = PBXProject; 255 | attributes = { 256 | LastSwiftUpdateCheck = 1250; 257 | LastUpgradeCheck = 1250; 258 | TargetAttributes = { 259 | 2A20536626DD24DF00C66AA1 = { 260 | CreatedOnToolsVersion = 12.5; 261 | LastSwiftMigration = 1250; 262 | }; 263 | 2A20539626DEE19B00C66AA1 = { 264 | CreatedOnToolsVersion = 12.5; 265 | }; 266 | }; 267 | }; 268 | buildConfigurationList = 2A20536126DD24DF00C66AA1 /* Build configuration list for PBXProject "SandboxKit" */; 269 | compatibilityVersion = "Xcode 9.3"; 270 | developmentRegion = en; 271 | hasScannedForEncodings = 0; 272 | knownRegions = ( 273 | en, 274 | Base, 275 | ); 276 | mainGroup = 2A20535D26DD24DF00C66AA1; 277 | productRefGroup = 2A20536826DD24DF00C66AA1 /* Products */; 278 | projectDirPath = ""; 279 | projectRoot = ""; 280 | targets = ( 281 | 2A20536626DD24DF00C66AA1 /* SandboxKit */, 282 | 2A20539626DEE19B00C66AA1 /* SandboxKitDemo */, 283 | ); 284 | }; 285 | /* End PBXProject section */ 286 | 287 | /* Begin PBXResourcesBuildPhase section */ 288 | 2A20536526DD24DF00C66AA1 /* Resources */ = { 289 | isa = PBXResourcesBuildPhase; 290 | buildActionMask = 2147483647; 291 | files = ( 292 | ); 293 | runOnlyForDeploymentPostprocessing = 0; 294 | }; 295 | 2A20539526DEE19B00C66AA1 /* Resources */ = { 296 | isa = PBXResourcesBuildPhase; 297 | buildActionMask = 2147483647; 298 | files = ( 299 | 2A2053A626DEE19D00C66AA1 /* LaunchScreen.storyboard in Resources */, 300 | 2A3471F426F2507C00B8226B /* pokemons.json in Resources */, 301 | 2A2053D126DEF1FD00C66AA1 /* pokemon.json in Resources */, 302 | 2A2053A326DEE19D00C66AA1 /* Assets.xcassets in Resources */, 303 | ); 304 | runOnlyForDeploymentPostprocessing = 0; 305 | }; 306 | /* End PBXResourcesBuildPhase section */ 307 | 308 | /* Begin PBXSourcesBuildPhase section */ 309 | 2A20536326DD24DF00C66AA1 /* Sources */ = { 310 | isa = PBXSourcesBuildPhase; 311 | buildActionMask = 2147483647; 312 | files = ( 313 | 2A20538E26DED7FD00C66AA1 /* HTTPMethod.swift in Sources */, 314 | 2A20538A26DEB6AF00C66AA1 /* SandboxSceneSelectTableViewController.swift in Sources */, 315 | 2A20539026DED90000C66AA1 /* Scene.swift in Sources */, 316 | 2A3471F226F2418100B8226B /* ResponseStubbable.swift in Sources */, 317 | 2A20539226DED92900C66AA1 /* Mock.swift in Sources */, 318 | ); 319 | runOnlyForDeploymentPostprocessing = 0; 320 | }; 321 | 2A20539326DEE19B00C66AA1 /* Sources */ = { 322 | isa = PBXSourcesBuildPhase; 323 | buildActionMask = 2147483647; 324 | files = ( 325 | 2A2053AF26DEE70E00C66AA1 /* PokemonListViewController.swift in Sources */, 326 | 2A2053C426DEEBEB00C66AA1 /* ServiceClientProvider.swift in Sources */, 327 | 2A2053C026DEEB6200C66AA1 /* APIRequest.swift in Sources */, 328 | 2A2053CA26DEEC8B00C66AA1 /* MiniAppMainViewController.swift in Sources */, 329 | 2A2053CF26DEEE6900C66AA1 /* PokemonRequest.swift in Sources */, 330 | 2A20539A26DEE19B00C66AA1 /* AppDelegate.swift in Sources */, 331 | 2A2053CD26DEEDC100C66AA1 /* PokemonListRequest.swift in Sources */, 332 | 2A2053BA26DEE9C200C66AA1 /* PokemonDetailViewController.swift in Sources */, 333 | 2A2053B226DEE7B600C66AA1 /* Pokemon.swift in Sources */, 334 | 2A3471EF26F23CE000B8226B /* ServiceClientStub.swift in Sources */, 335 | 2A2053BE26DEEB2600C66AA1 /* ServiceClient.swift in Sources */, 336 | 2A2053C626DEEC2300C66AA1 /* MainServiceClient.swift in Sources */, 337 | 2A2053B426DEE7F000C66AA1 /* PokemonType.swift in Sources */, 338 | 2A2053C826DEEC5B00C66AA1 /* StubbableServiceClient.swift in Sources */, 339 | ); 340 | runOnlyForDeploymentPostprocessing = 0; 341 | }; 342 | /* End PBXSourcesBuildPhase section */ 343 | 344 | /* Begin PBXVariantGroup section */ 345 | 2A2053A426DEE19D00C66AA1 /* LaunchScreen.storyboard */ = { 346 | isa = PBXVariantGroup; 347 | children = ( 348 | 2A2053A526DEE19D00C66AA1 /* Base */, 349 | ); 350 | name = LaunchScreen.storyboard; 351 | sourceTree = ""; 352 | }; 353 | /* End PBXVariantGroup section */ 354 | 355 | /* Begin XCBuildConfiguration section */ 356 | 2A20537926DD24E000C66AA1 /* Debug */ = { 357 | isa = XCBuildConfiguration; 358 | buildSettings = { 359 | ALWAYS_SEARCH_USER_PATHS = NO; 360 | CLANG_ANALYZER_NONNULL = YES; 361 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 362 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 363 | CLANG_CXX_LIBRARY = "libc++"; 364 | CLANG_ENABLE_MODULES = YES; 365 | CLANG_ENABLE_OBJC_ARC = YES; 366 | CLANG_ENABLE_OBJC_WEAK = YES; 367 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 368 | CLANG_WARN_BOOL_CONVERSION = YES; 369 | CLANG_WARN_COMMA = YES; 370 | CLANG_WARN_CONSTANT_CONVERSION = YES; 371 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 372 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 373 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 374 | CLANG_WARN_EMPTY_BODY = YES; 375 | CLANG_WARN_ENUM_CONVERSION = YES; 376 | CLANG_WARN_INFINITE_RECURSION = YES; 377 | CLANG_WARN_INT_CONVERSION = YES; 378 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 379 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 380 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 381 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 382 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 383 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 384 | CLANG_WARN_STRICT_PROTOTYPES = YES; 385 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 386 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 387 | CLANG_WARN_UNREACHABLE_CODE = YES; 388 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 389 | COPY_PHASE_STRIP = NO; 390 | CURRENT_PROJECT_VERSION = 1; 391 | DEBUG_INFORMATION_FORMAT = dwarf; 392 | ENABLE_STRICT_OBJC_MSGSEND = YES; 393 | ENABLE_TESTABILITY = YES; 394 | GCC_C_LANGUAGE_STANDARD = gnu11; 395 | GCC_DYNAMIC_NO_PIC = NO; 396 | GCC_NO_COMMON_BLOCKS = YES; 397 | GCC_OPTIMIZATION_LEVEL = 0; 398 | GCC_PREPROCESSOR_DEFINITIONS = ( 399 | "DEBUG=1", 400 | "$(inherited)", 401 | ); 402 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 403 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 404 | GCC_WARN_UNDECLARED_SELECTOR = YES; 405 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 406 | GCC_WARN_UNUSED_FUNCTION = YES; 407 | GCC_WARN_UNUSED_VARIABLE = YES; 408 | IPHONEOS_DEPLOYMENT_TARGET = 14.5; 409 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 410 | MTL_FAST_MATH = YES; 411 | ONLY_ACTIVE_ARCH = YES; 412 | SDKROOT = iphoneos; 413 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 414 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 415 | VERSIONING_SYSTEM = "apple-generic"; 416 | VERSION_INFO_PREFIX = ""; 417 | }; 418 | name = Debug; 419 | }; 420 | 2A20537A26DD24E000C66AA1 /* Release */ = { 421 | isa = XCBuildConfiguration; 422 | buildSettings = { 423 | ALWAYS_SEARCH_USER_PATHS = NO; 424 | CLANG_ANALYZER_NONNULL = YES; 425 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 426 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 427 | CLANG_CXX_LIBRARY = "libc++"; 428 | CLANG_ENABLE_MODULES = YES; 429 | CLANG_ENABLE_OBJC_ARC = YES; 430 | CLANG_ENABLE_OBJC_WEAK = YES; 431 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 432 | CLANG_WARN_BOOL_CONVERSION = YES; 433 | CLANG_WARN_COMMA = YES; 434 | CLANG_WARN_CONSTANT_CONVERSION = YES; 435 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 436 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 437 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 438 | CLANG_WARN_EMPTY_BODY = YES; 439 | CLANG_WARN_ENUM_CONVERSION = YES; 440 | CLANG_WARN_INFINITE_RECURSION = YES; 441 | CLANG_WARN_INT_CONVERSION = YES; 442 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 443 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 444 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 445 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 446 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 447 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 448 | CLANG_WARN_STRICT_PROTOTYPES = YES; 449 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 450 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 451 | CLANG_WARN_UNREACHABLE_CODE = YES; 452 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 453 | COPY_PHASE_STRIP = NO; 454 | CURRENT_PROJECT_VERSION = 1; 455 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 456 | ENABLE_NS_ASSERTIONS = NO; 457 | ENABLE_STRICT_OBJC_MSGSEND = YES; 458 | GCC_C_LANGUAGE_STANDARD = gnu11; 459 | GCC_NO_COMMON_BLOCKS = YES; 460 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 461 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 462 | GCC_WARN_UNDECLARED_SELECTOR = YES; 463 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 464 | GCC_WARN_UNUSED_FUNCTION = YES; 465 | GCC_WARN_UNUSED_VARIABLE = YES; 466 | IPHONEOS_DEPLOYMENT_TARGET = 14.5; 467 | MTL_ENABLE_DEBUG_INFO = NO; 468 | MTL_FAST_MATH = YES; 469 | SDKROOT = iphoneos; 470 | SWIFT_COMPILATION_MODE = wholemodule; 471 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 472 | VALIDATE_PRODUCT = YES; 473 | VERSIONING_SYSTEM = "apple-generic"; 474 | VERSION_INFO_PREFIX = ""; 475 | }; 476 | name = Release; 477 | }; 478 | 2A20537C26DD24E000C66AA1 /* Debug */ = { 479 | isa = XCBuildConfiguration; 480 | buildSettings = { 481 | CLANG_ENABLE_MODULES = YES; 482 | CODE_SIGN_STYLE = Automatic; 483 | DEFINES_MODULE = YES; 484 | DEVELOPMENT_TEAM = 7Q6B2W96J9; 485 | DYLIB_COMPATIBILITY_VERSION = 1; 486 | DYLIB_CURRENT_VERSION = 1; 487 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 488 | INFOPLIST_FILE = SandboxKit/Info.plist; 489 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 490 | LD_RUNPATH_SEARCH_PATHS = ( 491 | "$(inherited)", 492 | "@executable_path/Frameworks", 493 | "@loader_path/Frameworks", 494 | ); 495 | PRODUCT_BUNDLE_IDENTIFIER = "com.aoi-okawa.SandboxKit"; 496 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 497 | SKIP_INSTALL = YES; 498 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 499 | SWIFT_VERSION = 5.0; 500 | TARGETED_DEVICE_FAMILY = "1,2"; 501 | }; 502 | name = Debug; 503 | }; 504 | 2A20537D26DD24E000C66AA1 /* Release */ = { 505 | isa = XCBuildConfiguration; 506 | buildSettings = { 507 | CLANG_ENABLE_MODULES = YES; 508 | CODE_SIGN_STYLE = Automatic; 509 | DEFINES_MODULE = YES; 510 | DEVELOPMENT_TEAM = 7Q6B2W96J9; 511 | DYLIB_COMPATIBILITY_VERSION = 1; 512 | DYLIB_CURRENT_VERSION = 1; 513 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 514 | INFOPLIST_FILE = SandboxKit/Info.plist; 515 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 516 | LD_RUNPATH_SEARCH_PATHS = ( 517 | "$(inherited)", 518 | "@executable_path/Frameworks", 519 | "@loader_path/Frameworks", 520 | ); 521 | PRODUCT_BUNDLE_IDENTIFIER = "com.aoi-okawa.SandboxKit"; 522 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 523 | SKIP_INSTALL = YES; 524 | SWIFT_VERSION = 5.0; 525 | TARGETED_DEVICE_FAMILY = "1,2"; 526 | }; 527 | name = Release; 528 | }; 529 | 2A2053A926DEE19D00C66AA1 /* Debug */ = { 530 | isa = XCBuildConfiguration; 531 | buildSettings = { 532 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 533 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 534 | CODE_SIGN_STYLE = Automatic; 535 | DEVELOPMENT_TEAM = 7Q6B2W96J9; 536 | INFOPLIST_FILE = SandboxKitDemo/Info.plist; 537 | LD_RUNPATH_SEARCH_PATHS = ( 538 | "$(inherited)", 539 | "@executable_path/Frameworks", 540 | ); 541 | PRODUCT_BUNDLE_IDENTIFIER = "com.aoi-okawa.SandboxKitDemo"; 542 | PRODUCT_NAME = "$(TARGET_NAME)"; 543 | SWIFT_VERSION = 5.0; 544 | TARGETED_DEVICE_FAMILY = "1,2"; 545 | }; 546 | name = Debug; 547 | }; 548 | 2A2053AA26DEE19D00C66AA1 /* Release */ = { 549 | isa = XCBuildConfiguration; 550 | buildSettings = { 551 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 552 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 553 | CODE_SIGN_STYLE = Automatic; 554 | DEVELOPMENT_TEAM = 7Q6B2W96J9; 555 | INFOPLIST_FILE = SandboxKitDemo/Info.plist; 556 | LD_RUNPATH_SEARCH_PATHS = ( 557 | "$(inherited)", 558 | "@executable_path/Frameworks", 559 | ); 560 | PRODUCT_BUNDLE_IDENTIFIER = "com.aoi-okawa.SandboxKitDemo"; 561 | PRODUCT_NAME = "$(TARGET_NAME)"; 562 | SWIFT_VERSION = 5.0; 563 | TARGETED_DEVICE_FAMILY = "1,2"; 564 | }; 565 | name = Release; 566 | }; 567 | /* End XCBuildConfiguration section */ 568 | 569 | /* Begin XCConfigurationList section */ 570 | 2A20536126DD24DF00C66AA1 /* Build configuration list for PBXProject "SandboxKit" */ = { 571 | isa = XCConfigurationList; 572 | buildConfigurations = ( 573 | 2A20537926DD24E000C66AA1 /* Debug */, 574 | 2A20537A26DD24E000C66AA1 /* Release */, 575 | ); 576 | defaultConfigurationIsVisible = 0; 577 | defaultConfigurationName = Release; 578 | }; 579 | 2A20537B26DD24E000C66AA1 /* Build configuration list for PBXNativeTarget "SandboxKit" */ = { 580 | isa = XCConfigurationList; 581 | buildConfigurations = ( 582 | 2A20537C26DD24E000C66AA1 /* Debug */, 583 | 2A20537D26DD24E000C66AA1 /* Release */, 584 | ); 585 | defaultConfigurationIsVisible = 0; 586 | defaultConfigurationName = Release; 587 | }; 588 | 2A2053A826DEE19D00C66AA1 /* Build configuration list for PBXNativeTarget "SandboxKitDemo" */ = { 589 | isa = XCConfigurationList; 590 | buildConfigurations = ( 591 | 2A2053A926DEE19D00C66AA1 /* Debug */, 592 | 2A2053AA26DEE19D00C66AA1 /* Release */, 593 | ); 594 | defaultConfigurationIsVisible = 0; 595 | defaultConfigurationName = Release; 596 | }; 597 | /* End XCConfigurationList section */ 598 | }; 599 | rootObject = 2A20535E26DD24DF00C66AA1 /* Project object */; 600 | } 601 | -------------------------------------------------------------------------------- /SandboxKit.xcodeproj/xcshareddata/xcschemes/SandboxKitDemo.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 | -------------------------------------------------------------------------------- /SandboxKit.xcodeproj/xcshareddata/xcschemes/SandboxKitDemoMiniApp.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 58 | 59 | 60 | 61 | 67 | 69 | 75 | 76 | 77 | 78 | 80 | 81 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /SandboxKit/HTTPMethod.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HTTPMethod.swift 3 | // SandboxKit 4 | // 5 | // Created by aoi-okawa on 2021/09/01. 6 | // 7 | 8 | import Foundation 9 | 10 | public enum HTTPMethod: String { 11 | case get = "GET" 12 | case post = "POST" 13 | case put = "PUT" 14 | case delete = "DELETE" 15 | case patch = "PATCH" 16 | } 17 | -------------------------------------------------------------------------------- /SandboxKit/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | 22 | 23 | -------------------------------------------------------------------------------- /SandboxKit/Mock.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Mock.swift 3 | // SandboxKit 4 | // 5 | // Created by aoi-okawa on 2021/09/01. 6 | // 7 | 8 | import Foundation 9 | 10 | public struct Mock { 11 | public var path: String 12 | public var statusCode: Int 13 | public var method: HTTPMethod 14 | public var json: JSONData 15 | } 16 | 17 | public struct JSONData { 18 | public var data: Data? 19 | 20 | public init(from basename: String) { 21 | if let path = Bundle.main.path(forResource: basename, ofType: "json"), 22 | let jsonData = try? Data(contentsOf: URL(fileURLWithPath: path)) { 23 | self.data = jsonData 24 | } else { 25 | self.data = nil 26 | } 27 | } 28 | 29 | public init(_ rowData: String) { 30 | self.data = rowData.data(using: .utf8) 31 | } 32 | } 33 | 34 | @resultBuilder 35 | public enum JSONDataBuilder { 36 | public static func buildBlock(_ components: JSONData...) -> [JSONData] { 37 | components 38 | } 39 | } 40 | 41 | public extension Mock { 42 | init(path: String, statusCode: Int, method: HTTPMethod, @JSONDataBuilder _ makeJSONData: () -> [JSONData]) { 43 | self.init(path: path, statusCode: statusCode, method: method, json: makeJSONData().first ?? .init("")) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /SandboxKit/ResponseStubbable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ResponseStubbable.swift 3 | // SandboxKit 4 | // 5 | // Created by aoi-okawa on 2021/09/15. 6 | // 7 | 8 | import Foundation 9 | 10 | public protocol ResponseStubbable { 11 | var jsonData: Data? { get } 12 | } 13 | 14 | extension Data: ResponseStubbable { 15 | public var jsonData: Data? { 16 | return self 17 | } 18 | } 19 | 20 | extension String: ResponseStubbable { 21 | public var jsonData: Data? { 22 | return data(using: .utf8) 23 | } 24 | } 25 | 26 | public extension ResponseStubbable where Self: Encodable { 27 | var jsonData: Data? { 28 | let jsonEncoder = JSONEncoder() 29 | do { 30 | return try jsonEncoder.encode(self) 31 | } catch { 32 | fatalError("Could not encode \(error)") 33 | } 34 | } 35 | } 36 | 37 | extension Array: ResponseStubbable where Element: ResponseStubbable & Encodable { 38 | public var jsonData: Data? { 39 | let jsonEncoder = JSONEncoder() 40 | do { 41 | return try jsonEncoder.encode(self) 42 | } catch { 43 | fatalError("Could not encode \(error)") 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /SandboxKit/SandboxKit.h: -------------------------------------------------------------------------------- 1 | // 2 | // SandboxKit.h 3 | // SandboxKit 4 | // 5 | // Created by aoi-okawa on 2021/08/30. 6 | // 7 | 8 | #import 9 | 10 | //! Project version number for SandboxKit. 11 | FOUNDATION_EXPORT double SandboxKitVersionNumber; 12 | 13 | //! Project version string for SandboxKit. 14 | FOUNDATION_EXPORT const unsigned char SandboxKitVersionString[]; 15 | 16 | // In this header, you should import all the public headers of your framework using statements like #import 17 | 18 | 19 | -------------------------------------------------------------------------------- /SandboxKit/SandboxSceneSelectTableViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SandboxSceneSelectTableViewController.swift 3 | // SandboxKit 4 | // 5 | // Created by aoi-okawa on 2021/09/01. 6 | // 7 | 8 | import Foundation 9 | import UIKit 10 | 11 | open class SandboxSceneSelectTableViewController: UITableViewController { 12 | private let scenes: [Scene] 13 | public init(@SandboxScenes _ makeScenes: () -> [Scene]) { 14 | self.scenes = makeScenes() 15 | super.init(nibName: nil, bundle: nil) 16 | } 17 | 18 | public required init?(coder: NSCoder) { 19 | fatalError("init(coder:) has not been implemented") 20 | } 21 | 22 | public override func viewDidLoad() { 23 | super.viewDidLoad() 24 | view.backgroundColor = .white 25 | tableView.reloadData() 26 | } 27 | 28 | public override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 29 | return scenes.count 30 | } 31 | 32 | public override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 33 | let selectedScene = scenes[indexPath.row] 34 | let cell = UITableViewCell() 35 | cell.textLabel?.text = selectedScene.name 36 | return cell 37 | } 38 | 39 | public override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 40 | let selectedScene = scenes[indexPath.row] 41 | initializeScene(selectedScene) 42 | navigationController?.pushViewController(selectedScene.viewController, animated: true) 43 | } 44 | 45 | open func initializeScene(_ scene: Scene) { 46 | 47 | /// Override this method in your application and write initialization such as registering your own stub request. 48 | } 49 | } 50 | 51 | -------------------------------------------------------------------------------- /SandboxKit/Scene.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Scene.swift 3 | // SandboxKit 4 | // 5 | // Created by aoi-okawa on 2021/09/01. 6 | // 7 | 8 | import Foundation 9 | import UIKit 10 | 11 | public struct Scene { 12 | public var name: String 13 | public var viewController: UIViewController 14 | public var stubData: [Mock] 15 | } 16 | 17 | @resultBuilder 18 | public enum SandboxScenes { 19 | public static func buildBlock(_ components: Scene...) -> [Scene] { 20 | components 21 | } 22 | } 23 | 24 | public extension Scene { 25 | init(name: String, _ viewController: UIViewController, @SceneBuilder _ makeStubJson: () -> [Mock]) { 26 | self.init(name: name, viewController: viewController, stubData: makeStubJson()) 27 | } 28 | } 29 | 30 | @resultBuilder 31 | public enum SceneBuilder { 32 | public static func buildBlock(_ components: Mock...) -> [Mock] { 33 | components 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /SandboxKitDemo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // SandboxKitDemo 4 | // 5 | // Created by aoi-okawa on 2021/09/01. 6 | // 7 | 8 | import UIKit 9 | import SandboxKit 10 | 11 | @main 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | var window: UIWindow? 14 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 15 | #if DEBUG 16 | if ProcessInfo.processInfo.environment["RUNNING_MINIAPP"] == "1" { 17 | window = UIWindow(frame: UIScreen.main.bounds) 18 | let viewController = MiniAppMainViewController { 19 | Scene( 20 | name: "PokemonDetail", 21 | PokemonDetailViewController(id: 133) 22 | ) { 23 | Mock(path: "/pokemons/133", statusCode: 200, method: .get) { 24 | JSONData(from: "pokemon") 25 | } 26 | } 27 | Scene( 28 | name: "PokemonList", 29 | PokemonListViewController() 30 | ) { 31 | Mock(path: "/pokemons", statusCode: 200, method: .get) { 32 | JSONData(from: "pokemons") 33 | } 34 | } 35 | } 36 | window?.rootViewController = UINavigationController(rootViewController: viewController) 37 | window?.makeKeyAndVisible() 38 | return true 39 | } 40 | #endif 41 | ServiceClientProvider.injectionContainer.serviceClient = MainServiceClient() 42 | let rootViewController = UINavigationController(rootViewController: PokemonListViewController()) 43 | window?.rootViewController = rootViewController 44 | window?.makeKeyAndVisible() 45 | return true 46 | } 47 | } 48 | 49 | -------------------------------------------------------------------------------- /SandboxKitDemo/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 | -------------------------------------------------------------------------------- /SandboxKitDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /SandboxKitDemo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /SandboxKitDemo/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 | -------------------------------------------------------------------------------- /SandboxKitDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSupportsIndirectInputEvents 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /SandboxKitDemo/Main/API/APIRequest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // APIRequest.swift 3 | // SandboxKitDemo 4 | // 5 | // Created by aoi-okawa on 2021/09/01. 6 | // 7 | 8 | import Foundation 9 | import SandboxKit 10 | 11 | protocol APIRequest { 12 | associatedtype Response 13 | var method: HTTPMethod { get } 14 | var path: String { get } 15 | var parameters: [String: Any] { get } 16 | func makeResponse(from data: Data, urlResponse: HTTPURLResponse) throws -> Response 17 | } 18 | 19 | extension APIRequest where Response: Decodable { 20 | func makeResponse(from data: Data, urlResponse: HTTPURLResponse) throws -> Response { 21 | let decoder = JSONDecoder() 22 | return try decoder.decode(Response.self, from: data) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /SandboxKitDemo/Main/API/MainServiceClient.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainServiceClient.swift 3 | // SandboxKitDemo 4 | // 5 | // Created by aoi-okawa on 2021/09/01. 6 | // 7 | 8 | import Foundation 9 | 10 | final class MainServiceClient: ServiceClient { 11 | func sendRequest(_ request: RequestType, completion: @escaping (Result) -> Void) where RequestType: APIRequest { 12 | /// Implement your URL Request 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /SandboxKitDemo/Main/API/Request/PokemonListRequest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PokemonListRequest.swift 3 | // SandboxKitDemo 4 | // 5 | // Created by aoi-okawa on 2021/09/01. 6 | // 7 | 8 | import Foundation 9 | import SandboxKit 10 | 11 | struct PokemonListRequest: APIRequest { 12 | typealias Response = [Pokemon] 13 | var method: HTTPMethod = .get 14 | var path: String = "/pokemons" 15 | var parameters: [String : Any] = [:] 16 | 17 | struct Pokemon: Decodable { 18 | var id: Int64 19 | var name: String 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SandboxKitDemo/Main/API/Request/PokemonRequest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PokemonRequest.swift 3 | // SandboxKitDemo 4 | // 5 | // Created by aoi-okawa on 2021/09/01. 6 | // 7 | 8 | import Foundation 9 | import SandboxKit 10 | 11 | struct PokemonRequest: APIRequest { 12 | typealias Response = Pokemon 13 | var id: Int64 14 | var method: HTTPMethod = .get 15 | var path: String { 16 | return "/pokemons/\(id)" 17 | } 18 | var parameters: [String : Any] = [:] 19 | } 20 | 21 | -------------------------------------------------------------------------------- /SandboxKitDemo/Main/API/ServiceClient.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ServiceClient.swift 3 | // SandboxKitDemo 4 | // 5 | // Created by aoi-okawa on 2021/09/01. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol ServiceClient { 11 | func sendRequest(_ request: RequestType, completion: @escaping (Result) -> Void) 12 | } 13 | -------------------------------------------------------------------------------- /SandboxKitDemo/Main/API/ServiceClientProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ServiceClientProvider.swift 3 | // SandboxKitDemo 4 | // 5 | // Created by aoi-okawa on 2021/09/01. 6 | // 7 | 8 | import Foundation 9 | 10 | final class ServiceClientProvider { 11 | public static let injectionContainer: Injections = .init() 12 | public class Injections { 13 | public var serviceClient: ServiceClient! 14 | } 15 | 16 | public static var `default`: ServiceClient { 17 | guard let serviceClient = injectionContainer.serviceClient else { 18 | fatalError("Inject before use") 19 | } 20 | return serviceClient 21 | } 22 | private init() {} 23 | } 24 | -------------------------------------------------------------------------------- /SandboxKitDemo/Main/Model/Pokemon.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Pokemon.swift 3 | // SandboxKitDemo 4 | // 5 | // Created by aoi-okawa on 2021/09/01. 6 | // 7 | 8 | import Foundation 9 | 10 | struct Pokemon: Decodable { 11 | var id: Int64 12 | var name: String 13 | private var type: String 14 | var pokemonType: PokemonType { 15 | return .init(rawValue: type) ?? .normal 16 | } 17 | var height: Double 18 | var weight: Double 19 | } 20 | -------------------------------------------------------------------------------- /SandboxKitDemo/Main/Model/PokemonType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PokemonType.swift 3 | // SandboxKitDemo 4 | // 5 | // Created by aoi-okawa on 2021/09/01. 6 | // 7 | 8 | import Foundation 9 | 10 | enum PokemonType: String { 11 | case normal 12 | case fire 13 | case water 14 | case electric 15 | case psychic 16 | case dark 17 | case grass 18 | case ice 19 | case fairy 20 | } 21 | -------------------------------------------------------------------------------- /SandboxKitDemo/Main/PokemonDetailViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PokemonDetailViewController.swift 3 | // SandboxKitDemo 4 | // 5 | // Created by aoi-okawa on 2021/09/01. 6 | // 7 | 8 | import Foundation 9 | import UIKit 10 | 11 | final class PokemonDetailViewController: UIViewController { 12 | private let id: Int64 13 | init(id: Int64) { 14 | self.id = id 15 | super.init(nibName: nil, bundle: nil) 16 | } 17 | 18 | required init?(coder: NSCoder) { 19 | fatalError("init(coder:) has not been implemented") 20 | } 21 | 22 | override func viewDidLoad() { 23 | super.viewDidLoad() 24 | view.backgroundColor = .white 25 | let nameLabel = UILabel() 26 | nameLabel.font = .boldSystemFont(ofSize: 36) 27 | nameLabel.translatesAutoresizingMaskIntoConstraints = false 28 | let typeLabel = UILabel() 29 | typeLabel.font = .systemFont(ofSize: 24) 30 | typeLabel.translatesAutoresizingMaskIntoConstraints = false 31 | let heightLabel = UILabel() 32 | heightLabel.font = .systemFont(ofSize: 24) 33 | heightLabel.translatesAutoresizingMaskIntoConstraints = false 34 | let weightLabel = UILabel() 35 | weightLabel.font = .systemFont(ofSize: 24) 36 | weightLabel.translatesAutoresizingMaskIntoConstraints = false 37 | let stackView = UIStackView( 38 | arrangedSubviews: [ 39 | nameLabel, 40 | typeLabel, 41 | heightLabel, 42 | weightLabel, 43 | ] 44 | ) 45 | stackView.axis = .vertical 46 | stackView.alignment = .center 47 | stackView.spacing = 8 48 | stackView.translatesAutoresizingMaskIntoConstraints = false 49 | view.addSubview(stackView) 50 | NSLayoutConstraint.activate([ 51 | stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor), 52 | stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor), 53 | ]) 54 | ServiceClientProvider.default.sendRequest(PokemonRequest(id: id)) { result in 55 | if case let .success(pokemon) = result { 56 | nameLabel.text = pokemon.name 57 | typeLabel.text = "Type:\(pokemon.pokemonType.rawValue)" 58 | heightLabel.text = "Height:\(pokemon.height) m" 59 | weightLabel.text = "Weight:\(pokemon.weight) kg" 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /SandboxKitDemo/Main/PokemonListViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PokemonListViewController.swift 3 | // SandboxKitDemo 4 | // 5 | // Created by aoi-okawa on 2021/09/01. 6 | // 7 | 8 | import Foundation 9 | import UIKit 10 | 11 | final class PokemonListViewController: UITableViewController { 12 | private var pokemons: [PokemonListRequest.Pokemon] = [] 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | fetch() 16 | tableView.reloadData() 17 | } 18 | 19 | private func fetch() { 20 | ServiceClientProvider.default.sendRequest(PokemonListRequest()) { result in 21 | if case let .success(pokemons) = result { 22 | self.pokemons = pokemons 23 | self.tableView.reloadData() 24 | } 25 | } 26 | } 27 | 28 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 29 | pokemons.count 30 | } 31 | 32 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 33 | let pokemon = pokemons[indexPath.row] 34 | let cell = UITableViewCell() 35 | cell.textLabel?.text = pokemon.name 36 | return cell 37 | } 38 | 39 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 40 | let pokemon = pokemons[indexPath.row] 41 | navigationController?.pushViewController(PokemonDetailViewController(id: pokemon.id), animated: true) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /SandboxKitDemo/MiniApp/MiniAppMainViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MiniAppMainViewController.swift 3 | // SandboxKitDemo 4 | // 5 | // Created by aoi-okawa on 2021/09/01. 6 | // 7 | 8 | import Foundation 9 | import SandboxKit 10 | 11 | #if DEBUG 12 | final class MiniAppMainViewController: SandboxSceneSelectTableViewController { 13 | override init(@SandboxScenes _ makeScenes: () -> [Scene]) { 14 | super.init(makeScenes) 15 | } 16 | 17 | required init?(coder: NSCoder) { 18 | fatalError("init(coder:) has not been implemented") 19 | } 20 | 21 | override func initializeScene(_ scene: Scene) { 22 | let stubbableServiceClient = StubbableServiceClient() 23 | for mock in scene.stubData { 24 | if let jsonData = mock.json.data { 25 | stubbableServiceClient.registerClientResponse( 26 | jsonData, 27 | for: mock.path, 28 | method: mock.method, 29 | statusCode: mock.statusCode 30 | ) 31 | } 32 | } 33 | ServiceClientProvider.injectionContainer.serviceClient = stubbableServiceClient 34 | } 35 | } 36 | #endif 37 | -------------------------------------------------------------------------------- /SandboxKitDemo/MiniApp/ServiceClientStub.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ServiceClientStub.swift 3 | // SandboxKitDemo 4 | // 5 | // Created by aoi-okawa on 2021/09/15. 6 | // 7 | 8 | import Foundation 9 | import SandboxKit 10 | 11 | #if DEBUG 12 | struct ServiceClientStub { 13 | typealias Response = Swift.Result 14 | var path: String 15 | var method: HTTPMethod 16 | var parameters: [String: Any] 17 | var result: Response 18 | var urlResponse: HTTPURLResponse 19 | 20 | init( 21 | path: String, 22 | method: HTTPMethod, 23 | parameters: [String: Any] = [:], 24 | result: ServiceClientStub.Response, 25 | urlResponse: HTTPURLResponse = .init() 26 | ) { 27 | let characterSet: CharacterSet = .urlPathAllowed 28 | let shouldNotContainsInvalidCharacter = path.unicodeScalars.allSatisfy { characterSet.contains($0) } 29 | assert(shouldNotContainsInvalidCharacter, "path \(path) contains invalid characters.") 30 | self.path = path 31 | self.method = method 32 | self.result = result 33 | self.parameters = parameters 34 | self.urlResponse = urlResponse 35 | } 36 | } 37 | #endif 38 | -------------------------------------------------------------------------------- /SandboxKitDemo/MiniApp/StubbableServiceClient.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StubbableServiceClient.swift 3 | // SandboxKitDemo 4 | // 5 | // Created by aoi-okawa on 2021/09/01. 6 | // 7 | 8 | import Foundation 9 | import SandboxKit 10 | 11 | #if DEBUG 12 | final class StubbableServiceClient: ServiceClient { 13 | private var responseStubs: [ServiceClientStub] = [] 14 | func sendRequest(_ request: RequestType, completion: @escaping (Result) -> Void) where RequestType : APIRequest { 15 | 16 | /// Implement your Dummy API Request 17 | 18 | let matchingStubs = responseStubs.filter { stub in 19 | return stub.path == request.path && stub.method == request.method 20 | } 21 | let matchingStub: ServiceClientStub 22 | switch matchingStubs.count { 23 | case 1: 24 | matchingStub = matchingStubs[0] 25 | default: 26 | return completion(.failure(NSError(domain: "StubbableServiceClient.sendRequest", code: 0, userInfo: [NSLocalizedDescriptionKey: NSLocalizedString("Invalid matching Stub.", comment: "")]))) 27 | } 28 | 29 | switch matchingStub.result { 30 | case let .success(data): 31 | do { 32 | let response = try request.makeResponse(from: data ?? Data(), urlResponse: matchingStub.urlResponse) 33 | completion(.success(response)) 34 | } catch let error { 35 | completion(.failure(error)) 36 | } 37 | case let .failure(error): 38 | completion(.failure(error)) 39 | } 40 | } 41 | 42 | func registerClientResponse( 43 | _ response: Response, 44 | for path: String, 45 | method: HTTPMethod, 46 | parameters: [String: Any] = [:], 47 | statusCode: Int 48 | ) { 49 | /// Register Response 50 | let result: ServiceClientStub.Response 51 | if (200 ..< 300).contains(statusCode) { 52 | result = .success(response.jsonData) 53 | } else { 54 | result = .failure((NSError(domain: "StubbableServiceClient.registerClientResponse", code: 0, userInfo: [NSLocalizedDescriptionKey: NSLocalizedString("Registered Error Response", comment: "")]))) 55 | } 56 | responseStubs.append( 57 | .init( 58 | path: path, 59 | method: method, 60 | parameters: parameters, 61 | result: result, 62 | urlResponse: HTTPURLResponse( 63 | url: URL(string: path)!, 64 | statusCode: statusCode, 65 | httpVersion: "1.1", 66 | headerFields: ["Content-Type": "application/json"] 67 | )! 68 | ) 69 | ) 70 | } 71 | } 72 | #endif 73 | -------------------------------------------------------------------------------- /SandboxKitDemo/MiniApp/fixtures/pokemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 133, 3 | "name": "Eevee", 4 | "type": "normal", 5 | "height": 0.3, 6 | "weight": 6.5 7 | } 8 | -------------------------------------------------------------------------------- /SandboxKitDemo/MiniApp/fixtures/pokemons.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 133, 4 | "name": "Eevee" 5 | }, 6 | { 7 | "id": 134, 8 | "name": "Vaporeon" 9 | }, 10 | { 11 | "id": 135, 12 | "name": "Jolteon" 13 | }, 14 | { 15 | "id": 136, 16 | "name": "Flareon" 17 | }, 18 | { 19 | "id": 196, 20 | "name": "Espeon" 21 | }, 22 | { 23 | "id": 197, 24 | "name": "Umbreon" 25 | }, 26 | { 27 | "id": 470, 28 | "name": "Leafeon" 29 | }, 30 | { 31 | "id": 471, 32 | "name": "Glaceon" 33 | }, 34 | { 35 | "id": 700, 36 | "name": "Sylveon" 37 | } 38 | ] 39 | --------------------------------------------------------------------------------