├── Architecture.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── James.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── james.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── Architecture ├── Architecture.h ├── Info.plist ├── MolueBuilderFactory.swift ├── MolueComponentBuilder.swift ├── MoluePresenterInteractor.swift ├── MolueViewableInteractor.swift └── MolueViewableRouting.swift ├── ArchitectureTests ├── ArchitectureTests.swift └── Info.plist ├── Demo ├── Demo.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcuserdata │ │ │ └── James.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── xcuserdata │ │ └── James.xcuserdatad │ │ └── xcschemes │ │ └── xcschememanagement.plist ├── Demo │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── Contents.json │ ├── Base.lproj │ │ └── LaunchScreen.storyboard │ ├── HomePage │ │ ├── HomePageBuilderRouter.swift │ │ ├── HomePagePageInteractor.swift │ │ └── HomePageViewController.swift │ ├── Info.plist │ ├── LoginPage │ │ ├── LoginPageBuilderRouter.swift │ │ ├── LoginPagePageInteractor.swift │ │ └── LoginPageViewController.swift │ └── LoginPageHeaderView.swift ├── DemoTests │ ├── DemoTests.swift │ └── Info.plist └── DemoUITests │ ├── DemoUITests.swift │ └── Info.plist ├── README.md ├── VIPER.xctemplate ├── TemplateIcon.png ├── TemplateIcon@2x.png ├── TemplateInfo.plist ├── ___FILEBASENAME___BuilderRouter.swift ├── ___FILEBASENAME___PageInteractor.swift ├── ___FILEBASENAME___ViewController.swift └── ___FILEBASENAME___ViewInteractor.swift └── VIPER.xcworkspace ├── contents.xcworkspacedata ├── xcshareddata └── IDEWorkspaceChecks.plist └── xcuserdata └── James.xcuserdatad ├── UserInterfaceState.xcuserstate └── xcdebugger └── Breakpoints_v2.xcbkptlist /Architecture.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1A2115C823B4B208005564B6 /* MolueViewableInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A2115C723B4B208005564B6 /* MolueViewableInteractor.swift */; }; 11 | 1A7263BA217887AB00876861 /* Architecture.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A7263B0217887AB00876861 /* Architecture.framework */; }; 12 | 1A7263BF217887AB00876861 /* ArchitectureTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A7263BE217887AB00876861 /* ArchitectureTests.swift */; }; 13 | 1A7263C1217887AB00876861 /* Architecture.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A7263B3217887AB00876861 /* Architecture.h */; settings = {ATTRIBUTES = (Public, ); }; }; 14 | 1A7263CE217887C700876861 /* MolueViewableRouting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A7263CA217887C700876861 /* MolueViewableRouting.swift */; }; 15 | 1A7263CF217887C700876861 /* MoluePresenterInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A7263CB217887C700876861 /* MoluePresenterInteractor.swift */; }; 16 | 1A7263D0217887C700876861 /* MolueComponentBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A7263CC217887C700876861 /* MolueComponentBuilder.swift */; }; 17 | 1A7263D1217887C700876861 /* MolueBuilderFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A7263CD217887C700876861 /* MolueBuilderFactory.swift */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXContainerItemProxy section */ 21 | 1A7263BB217887AB00876861 /* PBXContainerItemProxy */ = { 22 | isa = PBXContainerItemProxy; 23 | containerPortal = 1A7263A7217887AB00876861 /* Project object */; 24 | proxyType = 1; 25 | remoteGlobalIDString = 1A7263AF217887AB00876861; 26 | remoteInfo = Architecture; 27 | }; 28 | /* End PBXContainerItemProxy section */ 29 | 30 | /* Begin PBXFileReference section */ 31 | 1A2115C723B4B208005564B6 /* MolueViewableInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MolueViewableInteractor.swift; sourceTree = ""; }; 32 | 1A7263B0217887AB00876861 /* Architecture.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Architecture.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 33 | 1A7263B3217887AB00876861 /* Architecture.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Architecture.h; sourceTree = ""; }; 34 | 1A7263B4217887AB00876861 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 35 | 1A7263B9217887AB00876861 /* ArchitectureTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ArchitectureTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 36 | 1A7263BE217887AB00876861 /* ArchitectureTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArchitectureTests.swift; sourceTree = ""; }; 37 | 1A7263C0217887AB00876861 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 38 | 1A7263CA217887C700876861 /* MolueViewableRouting.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MolueViewableRouting.swift; sourceTree = ""; }; 39 | 1A7263CB217887C700876861 /* MoluePresenterInteractor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MoluePresenterInteractor.swift; sourceTree = ""; }; 40 | 1A7263CC217887C700876861 /* MolueComponentBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MolueComponentBuilder.swift; sourceTree = ""; }; 41 | 1A7263CD217887C700876861 /* MolueBuilderFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MolueBuilderFactory.swift; sourceTree = ""; }; 42 | /* End PBXFileReference section */ 43 | 44 | /* Begin PBXFrameworksBuildPhase section */ 45 | 1A7263AC217887AB00876861 /* Frameworks */ = { 46 | isa = PBXFrameworksBuildPhase; 47 | buildActionMask = 2147483647; 48 | files = ( 49 | ); 50 | runOnlyForDeploymentPostprocessing = 0; 51 | }; 52 | 1A7263B6217887AB00876861 /* Frameworks */ = { 53 | isa = PBXFrameworksBuildPhase; 54 | buildActionMask = 2147483647; 55 | files = ( 56 | 1A7263BA217887AB00876861 /* Architecture.framework in Frameworks */, 57 | ); 58 | runOnlyForDeploymentPostprocessing = 0; 59 | }; 60 | /* End PBXFrameworksBuildPhase section */ 61 | 62 | /* Begin PBXGroup section */ 63 | 1A7263A6217887AA00876861 = { 64 | isa = PBXGroup; 65 | children = ( 66 | 1A7263B2217887AB00876861 /* Architecture */, 67 | 1A7263BD217887AB00876861 /* ArchitectureTests */, 68 | 1A7263B1217887AB00876861 /* Products */, 69 | ); 70 | sourceTree = ""; 71 | }; 72 | 1A7263B1217887AB00876861 /* Products */ = { 73 | isa = PBXGroup; 74 | children = ( 75 | 1A7263B0217887AB00876861 /* Architecture.framework */, 76 | 1A7263B9217887AB00876861 /* ArchitectureTests.xctest */, 77 | ); 78 | name = Products; 79 | sourceTree = ""; 80 | }; 81 | 1A7263B2217887AB00876861 /* Architecture */ = { 82 | isa = PBXGroup; 83 | children = ( 84 | 1A7263B3217887AB00876861 /* Architecture.h */, 85 | 1A7263CD217887C700876861 /* MolueBuilderFactory.swift */, 86 | 1A7263CC217887C700876861 /* MolueComponentBuilder.swift */, 87 | 1A7263CB217887C700876861 /* MoluePresenterInteractor.swift */, 88 | 1A2115C723B4B208005564B6 /* MolueViewableInteractor.swift */, 89 | 1A7263CA217887C700876861 /* MolueViewableRouting.swift */, 90 | 1A7263B4217887AB00876861 /* Info.plist */, 91 | ); 92 | path = Architecture; 93 | sourceTree = ""; 94 | }; 95 | 1A7263BD217887AB00876861 /* ArchitectureTests */ = { 96 | isa = PBXGroup; 97 | children = ( 98 | 1A7263BE217887AB00876861 /* ArchitectureTests.swift */, 99 | 1A7263C0217887AB00876861 /* Info.plist */, 100 | ); 101 | path = ArchitectureTests; 102 | sourceTree = ""; 103 | }; 104 | /* End PBXGroup section */ 105 | 106 | /* Begin PBXHeadersBuildPhase section */ 107 | 1A7263AD217887AB00876861 /* Headers */ = { 108 | isa = PBXHeadersBuildPhase; 109 | buildActionMask = 2147483647; 110 | files = ( 111 | 1A7263C1217887AB00876861 /* Architecture.h in Headers */, 112 | ); 113 | runOnlyForDeploymentPostprocessing = 0; 114 | }; 115 | /* End PBXHeadersBuildPhase section */ 116 | 117 | /* Begin PBXNativeTarget section */ 118 | 1A7263AF217887AB00876861 /* Architecture */ = { 119 | isa = PBXNativeTarget; 120 | buildConfigurationList = 1A7263C4217887AB00876861 /* Build configuration list for PBXNativeTarget "Architecture" */; 121 | buildPhases = ( 122 | 1A7263AB217887AB00876861 /* Sources */, 123 | 1A7263AC217887AB00876861 /* Frameworks */, 124 | 1A7263AD217887AB00876861 /* Headers */, 125 | 1A7263AE217887AB00876861 /* Resources */, 126 | ); 127 | buildRules = ( 128 | ); 129 | dependencies = ( 130 | ); 131 | name = Architecture; 132 | productName = Architecture; 133 | productReference = 1A7263B0217887AB00876861 /* Architecture.framework */; 134 | productType = "com.apple.product-type.framework"; 135 | }; 136 | 1A7263B8217887AB00876861 /* ArchitectureTests */ = { 137 | isa = PBXNativeTarget; 138 | buildConfigurationList = 1A7263C7217887AB00876861 /* Build configuration list for PBXNativeTarget "ArchitectureTests" */; 139 | buildPhases = ( 140 | 1A7263B5217887AB00876861 /* Sources */, 141 | 1A7263B6217887AB00876861 /* Frameworks */, 142 | 1A7263B7217887AB00876861 /* Resources */, 143 | ); 144 | buildRules = ( 145 | ); 146 | dependencies = ( 147 | 1A7263BC217887AB00876861 /* PBXTargetDependency */, 148 | ); 149 | name = ArchitectureTests; 150 | productName = ArchitectureTests; 151 | productReference = 1A7263B9217887AB00876861 /* ArchitectureTests.xctest */; 152 | productType = "com.apple.product-type.bundle.unit-test"; 153 | }; 154 | /* End PBXNativeTarget section */ 155 | 156 | /* Begin PBXProject section */ 157 | 1A7263A7217887AB00876861 /* Project object */ = { 158 | isa = PBXProject; 159 | attributes = { 160 | LastSwiftUpdateCheck = 0940; 161 | LastUpgradeCheck = 0940; 162 | ORGANIZATIONNAME = Molue; 163 | TargetAttributes = { 164 | 1A7263AF217887AB00876861 = { 165 | CreatedOnToolsVersion = 9.4.1; 166 | LastSwiftMigration = 0940; 167 | }; 168 | 1A7263B8217887AB00876861 = { 169 | CreatedOnToolsVersion = 9.4.1; 170 | }; 171 | }; 172 | }; 173 | buildConfigurationList = 1A7263AA217887AB00876861 /* Build configuration list for PBXProject "Architecture" */; 174 | compatibilityVersion = "Xcode 9.3"; 175 | developmentRegion = en; 176 | hasScannedForEncodings = 0; 177 | knownRegions = ( 178 | en, 179 | ); 180 | mainGroup = 1A7263A6217887AA00876861; 181 | productRefGroup = 1A7263B1217887AB00876861 /* Products */; 182 | projectDirPath = ""; 183 | projectRoot = ""; 184 | targets = ( 185 | 1A7263AF217887AB00876861 /* Architecture */, 186 | 1A7263B8217887AB00876861 /* ArchitectureTests */, 187 | ); 188 | }; 189 | /* End PBXProject section */ 190 | 191 | /* Begin PBXResourcesBuildPhase section */ 192 | 1A7263AE217887AB00876861 /* Resources */ = { 193 | isa = PBXResourcesBuildPhase; 194 | buildActionMask = 2147483647; 195 | files = ( 196 | ); 197 | runOnlyForDeploymentPostprocessing = 0; 198 | }; 199 | 1A7263B7217887AB00876861 /* Resources */ = { 200 | isa = PBXResourcesBuildPhase; 201 | buildActionMask = 2147483647; 202 | files = ( 203 | ); 204 | runOnlyForDeploymentPostprocessing = 0; 205 | }; 206 | /* End PBXResourcesBuildPhase section */ 207 | 208 | /* Begin PBXSourcesBuildPhase section */ 209 | 1A7263AB217887AB00876861 /* Sources */ = { 210 | isa = PBXSourcesBuildPhase; 211 | buildActionMask = 2147483647; 212 | files = ( 213 | 1A7263CF217887C700876861 /* MoluePresenterInteractor.swift in Sources */, 214 | 1A7263D1217887C700876861 /* MolueBuilderFactory.swift in Sources */, 215 | 1A2115C823B4B208005564B6 /* MolueViewableInteractor.swift in Sources */, 216 | 1A7263CE217887C700876861 /* MolueViewableRouting.swift in Sources */, 217 | 1A7263D0217887C700876861 /* MolueComponentBuilder.swift in Sources */, 218 | ); 219 | runOnlyForDeploymentPostprocessing = 0; 220 | }; 221 | 1A7263B5217887AB00876861 /* Sources */ = { 222 | isa = PBXSourcesBuildPhase; 223 | buildActionMask = 2147483647; 224 | files = ( 225 | 1A7263BF217887AB00876861 /* ArchitectureTests.swift in Sources */, 226 | ); 227 | runOnlyForDeploymentPostprocessing = 0; 228 | }; 229 | /* End PBXSourcesBuildPhase section */ 230 | 231 | /* Begin PBXTargetDependency section */ 232 | 1A7263BC217887AB00876861 /* PBXTargetDependency */ = { 233 | isa = PBXTargetDependency; 234 | target = 1A7263AF217887AB00876861 /* Architecture */; 235 | targetProxy = 1A7263BB217887AB00876861 /* PBXContainerItemProxy */; 236 | }; 237 | /* End PBXTargetDependency section */ 238 | 239 | /* Begin XCBuildConfiguration section */ 240 | 1A7263C2217887AB00876861 /* Debug */ = { 241 | isa = XCBuildConfiguration; 242 | buildSettings = { 243 | ALWAYS_SEARCH_USER_PATHS = NO; 244 | CLANG_ANALYZER_NONNULL = YES; 245 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 246 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 247 | CLANG_CXX_LIBRARY = "libc++"; 248 | CLANG_ENABLE_MODULES = YES; 249 | CLANG_ENABLE_OBJC_ARC = YES; 250 | CLANG_ENABLE_OBJC_WEAK = YES; 251 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 252 | CLANG_WARN_BOOL_CONVERSION = YES; 253 | CLANG_WARN_COMMA = YES; 254 | CLANG_WARN_CONSTANT_CONVERSION = YES; 255 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 256 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 257 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 258 | CLANG_WARN_EMPTY_BODY = YES; 259 | CLANG_WARN_ENUM_CONVERSION = YES; 260 | CLANG_WARN_INFINITE_RECURSION = YES; 261 | CLANG_WARN_INT_CONVERSION = YES; 262 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 263 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 264 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 265 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 266 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 267 | CLANG_WARN_STRICT_PROTOTYPES = YES; 268 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 269 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 270 | CLANG_WARN_UNREACHABLE_CODE = YES; 271 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 272 | CODE_SIGN_IDENTITY = "iPhone Developer"; 273 | COPY_PHASE_STRIP = NO; 274 | CURRENT_PROJECT_VERSION = 1; 275 | DEBUG_INFORMATION_FORMAT = dwarf; 276 | ENABLE_STRICT_OBJC_MSGSEND = YES; 277 | ENABLE_TESTABILITY = YES; 278 | GCC_C_LANGUAGE_STANDARD = gnu11; 279 | GCC_DYNAMIC_NO_PIC = NO; 280 | GCC_NO_COMMON_BLOCKS = YES; 281 | GCC_OPTIMIZATION_LEVEL = 0; 282 | GCC_PREPROCESSOR_DEFINITIONS = ( 283 | "DEBUG=1", 284 | "$(inherited)", 285 | ); 286 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 287 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 288 | GCC_WARN_UNDECLARED_SELECTOR = YES; 289 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 290 | GCC_WARN_UNUSED_FUNCTION = YES; 291 | GCC_WARN_UNUSED_VARIABLE = YES; 292 | IPHONEOS_DEPLOYMENT_TARGET = 11.4; 293 | MTL_ENABLE_DEBUG_INFO = YES; 294 | ONLY_ACTIVE_ARCH = YES; 295 | SDKROOT = iphoneos; 296 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 297 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 298 | VERSIONING_SYSTEM = "apple-generic"; 299 | VERSION_INFO_PREFIX = ""; 300 | }; 301 | name = Debug; 302 | }; 303 | 1A7263C3217887AB00876861 /* Release */ = { 304 | isa = XCBuildConfiguration; 305 | buildSettings = { 306 | ALWAYS_SEARCH_USER_PATHS = NO; 307 | CLANG_ANALYZER_NONNULL = YES; 308 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 309 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 310 | CLANG_CXX_LIBRARY = "libc++"; 311 | CLANG_ENABLE_MODULES = YES; 312 | CLANG_ENABLE_OBJC_ARC = YES; 313 | CLANG_ENABLE_OBJC_WEAK = YES; 314 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 315 | CLANG_WARN_BOOL_CONVERSION = YES; 316 | CLANG_WARN_COMMA = YES; 317 | CLANG_WARN_CONSTANT_CONVERSION = YES; 318 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 319 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 320 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 321 | CLANG_WARN_EMPTY_BODY = YES; 322 | CLANG_WARN_ENUM_CONVERSION = YES; 323 | CLANG_WARN_INFINITE_RECURSION = YES; 324 | CLANG_WARN_INT_CONVERSION = YES; 325 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 326 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 327 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 328 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 329 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 330 | CLANG_WARN_STRICT_PROTOTYPES = YES; 331 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 332 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 333 | CLANG_WARN_UNREACHABLE_CODE = YES; 334 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 335 | CODE_SIGN_IDENTITY = "iPhone Developer"; 336 | COPY_PHASE_STRIP = NO; 337 | CURRENT_PROJECT_VERSION = 1; 338 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 339 | ENABLE_NS_ASSERTIONS = NO; 340 | ENABLE_STRICT_OBJC_MSGSEND = YES; 341 | GCC_C_LANGUAGE_STANDARD = gnu11; 342 | GCC_NO_COMMON_BLOCKS = YES; 343 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 344 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 345 | GCC_WARN_UNDECLARED_SELECTOR = YES; 346 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 347 | GCC_WARN_UNUSED_FUNCTION = YES; 348 | GCC_WARN_UNUSED_VARIABLE = YES; 349 | IPHONEOS_DEPLOYMENT_TARGET = 11.4; 350 | MTL_ENABLE_DEBUG_INFO = NO; 351 | SDKROOT = iphoneos; 352 | SWIFT_COMPILATION_MODE = wholemodule; 353 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 354 | VALIDATE_PRODUCT = YES; 355 | VERSIONING_SYSTEM = "apple-generic"; 356 | VERSION_INFO_PREFIX = ""; 357 | }; 358 | name = Release; 359 | }; 360 | 1A7263C5217887AB00876861 /* Debug */ = { 361 | isa = XCBuildConfiguration; 362 | buildSettings = { 363 | CLANG_ENABLE_MODULES = YES; 364 | CODE_SIGN_IDENTITY = ""; 365 | CODE_SIGN_STYLE = Automatic; 366 | DEFINES_MODULE = YES; 367 | DYLIB_COMPATIBILITY_VERSION = 1; 368 | DYLIB_CURRENT_VERSION = 1; 369 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 370 | INFOPLIST_FILE = Architecture/Info.plist; 371 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 372 | LD_RUNPATH_SEARCH_PATHS = ( 373 | "$(inherited)", 374 | "@executable_path/Frameworks", 375 | "@loader_path/Frameworks", 376 | ); 377 | PRODUCT_BUNDLE_IDENTIFIER = James.Architecture; 378 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 379 | SKIP_INSTALL = YES; 380 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 381 | SWIFT_VERSION = 4.0; 382 | TARGETED_DEVICE_FAMILY = "1,2"; 383 | }; 384 | name = Debug; 385 | }; 386 | 1A7263C6217887AB00876861 /* Release */ = { 387 | isa = XCBuildConfiguration; 388 | buildSettings = { 389 | CLANG_ENABLE_MODULES = YES; 390 | CODE_SIGN_IDENTITY = ""; 391 | CODE_SIGN_STYLE = Automatic; 392 | DEFINES_MODULE = YES; 393 | DYLIB_COMPATIBILITY_VERSION = 1; 394 | DYLIB_CURRENT_VERSION = 1; 395 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 396 | INFOPLIST_FILE = Architecture/Info.plist; 397 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 398 | LD_RUNPATH_SEARCH_PATHS = ( 399 | "$(inherited)", 400 | "@executable_path/Frameworks", 401 | "@loader_path/Frameworks", 402 | ); 403 | PRODUCT_BUNDLE_IDENTIFIER = James.Architecture; 404 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 405 | SKIP_INSTALL = YES; 406 | SWIFT_VERSION = 4.0; 407 | TARGETED_DEVICE_FAMILY = "1,2"; 408 | }; 409 | name = Release; 410 | }; 411 | 1A7263C8217887AB00876861 /* Debug */ = { 412 | isa = XCBuildConfiguration; 413 | buildSettings = { 414 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 415 | CODE_SIGN_STYLE = Automatic; 416 | INFOPLIST_FILE = ArchitectureTests/Info.plist; 417 | LD_RUNPATH_SEARCH_PATHS = ( 418 | "$(inherited)", 419 | "@executable_path/Frameworks", 420 | "@loader_path/Frameworks", 421 | ); 422 | PRODUCT_BUNDLE_IDENTIFIER = James.ArchitectureTests; 423 | PRODUCT_NAME = "$(TARGET_NAME)"; 424 | SWIFT_VERSION = 4.0; 425 | TARGETED_DEVICE_FAMILY = "1,2"; 426 | }; 427 | name = Debug; 428 | }; 429 | 1A7263C9217887AB00876861 /* Release */ = { 430 | isa = XCBuildConfiguration; 431 | buildSettings = { 432 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 433 | CODE_SIGN_STYLE = Automatic; 434 | INFOPLIST_FILE = ArchitectureTests/Info.plist; 435 | LD_RUNPATH_SEARCH_PATHS = ( 436 | "$(inherited)", 437 | "@executable_path/Frameworks", 438 | "@loader_path/Frameworks", 439 | ); 440 | PRODUCT_BUNDLE_IDENTIFIER = James.ArchitectureTests; 441 | PRODUCT_NAME = "$(TARGET_NAME)"; 442 | SWIFT_VERSION = 4.0; 443 | TARGETED_DEVICE_FAMILY = "1,2"; 444 | }; 445 | name = Release; 446 | }; 447 | /* End XCBuildConfiguration section */ 448 | 449 | /* Begin XCConfigurationList section */ 450 | 1A7263AA217887AB00876861 /* Build configuration list for PBXProject "Architecture" */ = { 451 | isa = XCConfigurationList; 452 | buildConfigurations = ( 453 | 1A7263C2217887AB00876861 /* Debug */, 454 | 1A7263C3217887AB00876861 /* Release */, 455 | ); 456 | defaultConfigurationIsVisible = 0; 457 | defaultConfigurationName = Release; 458 | }; 459 | 1A7263C4217887AB00876861 /* Build configuration list for PBXNativeTarget "Architecture" */ = { 460 | isa = XCConfigurationList; 461 | buildConfigurations = ( 462 | 1A7263C5217887AB00876861 /* Debug */, 463 | 1A7263C6217887AB00876861 /* Release */, 464 | ); 465 | defaultConfigurationIsVisible = 0; 466 | defaultConfigurationName = Release; 467 | }; 468 | 1A7263C7217887AB00876861 /* Build configuration list for PBXNativeTarget "ArchitectureTests" */ = { 469 | isa = XCConfigurationList; 470 | buildConfigurations = ( 471 | 1A7263C8217887AB00876861 /* Debug */, 472 | 1A7263C9217887AB00876861 /* Release */, 473 | ); 474 | defaultConfigurationIsVisible = 0; 475 | defaultConfigurationName = Release; 476 | }; 477 | /* End XCConfigurationList section */ 478 | }; 479 | rootObject = 1A7263A7217887AB00876861 /* Project object */; 480 | } 481 | -------------------------------------------------------------------------------- /Architecture.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Architecture.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Architecture.xcodeproj/project.xcworkspace/xcuserdata/James.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MolueJames/VIPER/abbab8c8039e8cc20990342c59228036ac6c6415/Architecture.xcodeproj/project.xcworkspace/xcuserdata/James.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Architecture.xcodeproj/xcuserdata/james.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Architecture.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | Architecture.xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 1 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Architecture/Architecture.h: -------------------------------------------------------------------------------- 1 | // 2 | // Architecture.h 3 | // Architecture 4 | // 5 | // Created by JamesCheng on 2018/10/18. 6 | // Copyright © 2018年 Molue. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for Architecture. 12 | FOUNDATION_EXPORT double ArchitectureVersionNumber; 13 | 14 | //! Project version string for Architecture. 15 | FOUNDATION_EXPORT const unsigned char ArchitectureVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /Architecture/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 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Architecture/MolueBuilderFactory.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MolueBuilderFactory.swift 3 | // MolueMediator 4 | // 5 | // Created by MolueJames on 2018/9/22. 6 | // Copyright © 2018年 MolueJames. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | public protocol MolueBuilderPathProtocol { 11 | func builderPath() -> String 12 | } 13 | open class MolueBuilderFactory { 14 | 15 | private let component: Component 16 | 17 | open func queryBuilder () -> T? { 18 | let className = component.builderPath() 19 | guard let targetCalss: AnyClass = NSClassFromString(className) else {return nil} 20 | guard let targetBuilder = targetCalss as? MolueComponentBuilder.Type else {return nil} 21 | guard let resultBuilder = targetBuilder.init() as? T else {return nil} 22 | return resultBuilder 23 | } 24 | 25 | public init(_ component: Component) { 26 | self.component = component; 27 | } 28 | } 29 | 30 | // 组件间解耦工具。 31 | import Foundation 32 | import UIKit 33 | 34 | public typealias OMethodProtocol = MethodAction & OMethodValidate 35 | 36 | public typealias CMethodProtocol = MethodAction & CMethodValidate 37 | 38 | public class THMethodManager { 39 | 40 | public static let `default` = THMethodManager() 41 | 42 | var methodList: [MethodAction] = [] 43 | 44 | private var lock: NSLock = .init() 45 | 46 | public func method(with name: String) -> MethodAction? { 47 | return self.methodList.first { action in 48 | return action.actionName() == name 49 | } 50 | } 51 | 52 | public func insert(_ action: MethodAction) { 53 | lock.lock(); defer { lock.unlock() } 54 | if self.methodList.contains(where: { 55 | $0.actionName() == action.actionName() 56 | }) { fatalError("已存在相同actionName的") } 57 | self.methodList.append(action) 58 | } 59 | 60 | public var currentMethods: [String] { 61 | return self.methodList.compactMap { 62 | return $0.actionName() 63 | } 64 | } 65 | 66 | } 67 | 68 | public protocol MethodValidate { 69 | 70 | associatedtype I 71 | 72 | associatedtype R 73 | 74 | associatedtype T 75 | 76 | var TClass: T.Type { get } 77 | 78 | var RClass: R.Type { get } 79 | 80 | var IClass: I.Type { get } 81 | 82 | } 83 | 84 | public extension MethodValidate where Self: MethodAction { 85 | 86 | var IClass: I.Type { return I.self } 87 | 88 | var RClass: R.Type { return R.self } 89 | 90 | var TClass: T.Type { return T.self } 91 | 92 | func vparams(for param: Any) throws -> I { 93 | if let result = param as? I { 94 | return result 95 | } else { 96 | let explain = self.description 97 | throw ActionError.params(explain) 98 | } 99 | } 100 | 101 | func vresult(for result: Any) throws -> R { 102 | if let result = result as? R { 103 | return result 104 | } else { 105 | let explain = self.description 106 | throw ActionError.output(explain) 107 | } 108 | } 109 | 110 | var description: String { 111 | return """ 112 | 方法入参类型: \(IClass), 113 | 方法出参类型: \(RClass), 114 | 调用对象类型: \(TClass), 115 | """ 116 | } 117 | } 118 | 119 | public protocol MethodAction { 120 | 121 | func perform(_ target: Any, params: Any) throws -> Any 122 | 123 | func actionName() -> String 124 | 125 | var description: String { get } 126 | 127 | var explanation: String { get } 128 | } 129 | 130 | public protocol OMethodValidate: MethodValidate { 131 | func perform(_ target: T, _ params: I) -> R 132 | } 133 | 134 | public protocol CMethodValidate: MethodValidate { 135 | func perform(with params: I) -> R 136 | } 137 | 138 | public extension MethodAction where Self: OMethodValidate { 139 | 140 | func vtarget(for target: Any) throws -> T { 141 | if let result = target as? T { 142 | return result 143 | } else { 144 | let explain = self.description 145 | throw ActionError.target(explain) 146 | } 147 | } 148 | func perform(_ target: Any, params: Any) throws -> Any { 149 | let target = try vtarget(for: target) 150 | let params = try vparams(for: params) 151 | return self.perform(target, params) 152 | } 153 | } 154 | 155 | public extension MethodAction where Self: CMethodValidate { 156 | 157 | func perform(_ target: Any, params: Any) throws -> Any { 158 | let params = try vparams(for: params) 159 | return self.perform(with: params) 160 | } 161 | } 162 | 163 | public enum ActionError: Error { 164 | 165 | case params(String) 166 | 167 | case output(String) 168 | 169 | case target(String) 170 | 171 | case method(String) 172 | } 173 | 174 | extension ActionError: CustomStringConvertible { 175 | public var description: String { 176 | switch self { 177 | case .method(let input): 178 | return "未找到注册方法: " + input 179 | case .params(let input): 180 | return "方法入参异常\n" + input 181 | case .output(let input): 182 | return "方法出参异常\n" + input 183 | case .target(let input): 184 | return "调用对象异常\n" + input 185 | } 186 | } 187 | } 188 | 189 | public func msg_send(_ name: String, _ target: Any, _ params: Any) throws -> Any { 190 | let method = THMethodManager.default.method(with: name) 191 | guard let method = method else { throw ActionError.method(name) } 192 | return try method.perform(target, params: params) 193 | } 194 | 195 | public func registerMethodAction(_ method: MethodAction) { 196 | THMethodManager.default.insert(method) 197 | } 198 | 199 | public func methodDescription(_ name: String) -> String { 200 | let method = THMethodManager.default.method(with: name) 201 | guard let method = method else { return "未找到对应的对象" } 202 | return method.description + "\nClass: \(type(of: method))" 203 | } 204 | 205 | public func methodExplanation(_ name: String) -> String { 206 | let method = THMethodManager.default.method(with: name) 207 | guard let method = method else { return "未找到对应的对象" } 208 | return method.explanation + "\nClass: \(type(of: method))" 209 | } 210 | 211 | public func registerMethodList() -> [String] { 212 | return THMethodManager.default.methodList.compactMap { 213 | "name: " + $0.actionName() + " class: \(type(of: $0))" 214 | } 215 | } 216 | 217 | public func build(_ name: String, _ target: Any, _ params: Any) throws -> T { 218 | let result = try msg_send(name, target, params) 219 | guard let result = result as? T else { 220 | throw THMethodError.resultNotMatched 221 | } 222 | return result 223 | } 224 | 225 | public enum THMethodError: Error { 226 | case resultNotMatched 227 | } 228 | -------------------------------------------------------------------------------- /Architecture/MolueComponentBuilder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MolueBuilder.swift 3 | // MolueFoundation 4 | // 5 | // Created by MolueJames on 2018/9/22. 6 | // Copyright © 2018年 MolueTech. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol MolueComponentBuildable: class {} 12 | 13 | open class MolueComponentBuilder: MolueComponentBuildable { 14 | public required init() {} 15 | } 16 | -------------------------------------------------------------------------------- /Architecture/MoluePresenterInteractor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MolueInteractor.swift 3 | // MolueFoundation 4 | // 5 | // Created by MolueJames on 2018/9/22. 6 | // Copyright © 2018年 MolueTech. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol MolueInteractorPresentable: class {} 12 | 13 | public protocol MoluePresenterInteractable: class { 14 | associatedtype Presentable 15 | 16 | init(presenter: Presentable) 17 | 18 | var presenter: Presentable { get } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /Architecture/MolueViewableInteractor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MolueViewableInteractor.swift 3 | // Architecture 4 | // 5 | // Created by James on 2019/12/26. 6 | // Copyright © 2019 Molue. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol MolueViewableInteractable: class { 12 | associatedtype Presentable 13 | 14 | init(presenter: Presentable) 15 | 16 | var presenter: Presentable { get } 17 | } 18 | 19 | public protocol MolueViewablePresentable: class { 20 | 21 | } 22 | -------------------------------------------------------------------------------- /Architecture/MolueViewableRouting.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MolueViewableRouting.swift 3 | // MolueMediator 4 | // 5 | // Created by MolueJames on 2018/10/3. 6 | // Copyright © 2018 MolueJames. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | public protocol MolueViewableRouting: class { 11 | associatedtype Interactable 12 | associatedtype Controllerable 13 | 14 | init(interactor: Interactable, controller: Controllerable) 15 | 16 | var interactor: Interactable { get } 17 | var controller: Controllerable { get } 18 | } 19 | -------------------------------------------------------------------------------- /ArchitectureTests/ArchitectureTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ArchitectureTests.swift 3 | // ArchitectureTests 4 | // 5 | // Created by JamesCheng on 2018/10/18. 6 | // Copyright © 2018年 Molue. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import Architecture 11 | 12 | class ArchitectureTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measure { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /ArchitectureTests/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 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Demo/Demo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1A6D142B23B497A20026CED3 /* LoginPageHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A6D142A23B497A20026CED3 /* LoginPageHeaderView.swift */; }; 11 | 1AF1B4F6217B6BFC00BB554C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AF1B4F5217B6BFC00BB554C /* AppDelegate.swift */; }; 12 | 1AF1B4FD217B6BFD00BB554C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1AF1B4FC217B6BFD00BB554C /* Assets.xcassets */; }; 13 | 1AF1B500217B6BFD00BB554C /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1AF1B4FE217B6BFD00BB554C /* LaunchScreen.storyboard */; }; 14 | 1AF1B50B217B6BFE00BB554C /* DemoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AF1B50A217B6BFE00BB554C /* DemoTests.swift */; }; 15 | 1AF1B516217B6BFE00BB554C /* DemoUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AF1B515217B6BFE00BB554C /* DemoUITests.swift */; }; 16 | 1AF1B528217B6DA100BB554C /* LoginPageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AF1B525217B6DA100BB554C /* LoginPageViewController.swift */; }; 17 | 1AF1B529217B6DA100BB554C /* LoginPagePageInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AF1B526217B6DA100BB554C /* LoginPagePageInteractor.swift */; }; 18 | 1AF1B52A217B6DA100BB554C /* LoginPageBuilderRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AF1B527217B6DA100BB554C /* LoginPageBuilderRouter.swift */; }; 19 | 1AF1B52E217B6DB200BB554C /* HomePageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AF1B52B217B6DB200BB554C /* HomePageViewController.swift */; }; 20 | 1AF1B52F217B6DB200BB554C /* HomePagePageInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AF1B52C217B6DB200BB554C /* HomePagePageInteractor.swift */; }; 21 | 1AF1B530217B6DB200BB554C /* HomePageBuilderRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AF1B52D217B6DB200BB554C /* HomePageBuilderRouter.swift */; }; 22 | 1AF1B534217B6E3700BB554C /* Architecture.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1AF1B532217B6E3200BB554C /* Architecture.framework */; }; 23 | 1AF1B535217B6E3700BB554C /* Architecture.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 1AF1B532217B6E3200BB554C /* Architecture.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 24 | /* End PBXBuildFile section */ 25 | 26 | /* Begin PBXContainerItemProxy section */ 27 | 1AF1B507217B6BFE00BB554C /* PBXContainerItemProxy */ = { 28 | isa = PBXContainerItemProxy; 29 | containerPortal = 1AF1B4EA217B6BFC00BB554C /* Project object */; 30 | proxyType = 1; 31 | remoteGlobalIDString = 1AF1B4F1217B6BFC00BB554C; 32 | remoteInfo = Demo; 33 | }; 34 | 1AF1B512217B6BFE00BB554C /* PBXContainerItemProxy */ = { 35 | isa = PBXContainerItemProxy; 36 | containerPortal = 1AF1B4EA217B6BFC00BB554C /* Project object */; 37 | proxyType = 1; 38 | remoteGlobalIDString = 1AF1B4F1217B6BFC00BB554C; 39 | remoteInfo = Demo; 40 | }; 41 | /* End PBXContainerItemProxy section */ 42 | 43 | /* Begin PBXCopyFilesBuildPhase section */ 44 | 1AF1B536217B6E3700BB554C /* Embed Frameworks */ = { 45 | isa = PBXCopyFilesBuildPhase; 46 | buildActionMask = 2147483647; 47 | dstPath = ""; 48 | dstSubfolderSpec = 10; 49 | files = ( 50 | 1AF1B535217B6E3700BB554C /* Architecture.framework in Embed Frameworks */, 51 | ); 52 | name = "Embed Frameworks"; 53 | runOnlyForDeploymentPostprocessing = 0; 54 | }; 55 | /* End PBXCopyFilesBuildPhase section */ 56 | 57 | /* Begin PBXFileReference section */ 58 | 1A6D142A23B497A20026CED3 /* LoginPageHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginPageHeaderView.swift; sourceTree = ""; }; 59 | 1AF1B4F2217B6BFC00BB554C /* Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Demo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 60 | 1AF1B4F5217B6BFC00BB554C /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 61 | 1AF1B4FC217B6BFD00BB554C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 62 | 1AF1B4FF217B6BFD00BB554C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 63 | 1AF1B501217B6BFD00BB554C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 64 | 1AF1B506217B6BFE00BB554C /* DemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 65 | 1AF1B50A217B6BFE00BB554C /* DemoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoTests.swift; sourceTree = ""; }; 66 | 1AF1B50C217B6BFE00BB554C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 67 | 1AF1B511217B6BFE00BB554C /* DemoUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DemoUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 68 | 1AF1B515217B6BFE00BB554C /* DemoUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoUITests.swift; sourceTree = ""; }; 69 | 1AF1B517217B6BFE00BB554C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 70 | 1AF1B525217B6DA100BB554C /* LoginPageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginPageViewController.swift; sourceTree = ""; }; 71 | 1AF1B526217B6DA100BB554C /* LoginPagePageInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginPagePageInteractor.swift; sourceTree = ""; }; 72 | 1AF1B527217B6DA100BB554C /* LoginPageBuilderRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginPageBuilderRouter.swift; sourceTree = ""; }; 73 | 1AF1B52B217B6DB200BB554C /* HomePageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomePageViewController.swift; sourceTree = ""; }; 74 | 1AF1B52C217B6DB200BB554C /* HomePagePageInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomePagePageInteractor.swift; sourceTree = ""; }; 75 | 1AF1B52D217B6DB200BB554C /* HomePageBuilderRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomePageBuilderRouter.swift; sourceTree = ""; }; 76 | 1AF1B532217B6E3200BB554C /* Architecture.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Architecture.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 77 | /* End PBXFileReference section */ 78 | 79 | /* Begin PBXFrameworksBuildPhase section */ 80 | 1AF1B4EF217B6BFC00BB554C /* Frameworks */ = { 81 | isa = PBXFrameworksBuildPhase; 82 | buildActionMask = 2147483647; 83 | files = ( 84 | 1AF1B534217B6E3700BB554C /* Architecture.framework in Frameworks */, 85 | ); 86 | runOnlyForDeploymentPostprocessing = 0; 87 | }; 88 | 1AF1B503217B6BFE00BB554C /* Frameworks */ = { 89 | isa = PBXFrameworksBuildPhase; 90 | buildActionMask = 2147483647; 91 | files = ( 92 | ); 93 | runOnlyForDeploymentPostprocessing = 0; 94 | }; 95 | 1AF1B50E217B6BFE00BB554C /* Frameworks */ = { 96 | isa = PBXFrameworksBuildPhase; 97 | buildActionMask = 2147483647; 98 | files = ( 99 | ); 100 | runOnlyForDeploymentPostprocessing = 0; 101 | }; 102 | /* End PBXFrameworksBuildPhase section */ 103 | 104 | /* Begin PBXGroup section */ 105 | 1AF1B4E9217B6BFC00BB554C = { 106 | isa = PBXGroup; 107 | children = ( 108 | 1AF1B4F4217B6BFC00BB554C /* Demo */, 109 | 1AF1B509217B6BFE00BB554C /* DemoTests */, 110 | 1AF1B514217B6BFE00BB554C /* DemoUITests */, 111 | 1AF1B4F3217B6BFC00BB554C /* Products */, 112 | 1AF1B531217B6E3200BB554C /* Frameworks */, 113 | ); 114 | sourceTree = ""; 115 | }; 116 | 1AF1B4F3217B6BFC00BB554C /* Products */ = { 117 | isa = PBXGroup; 118 | children = ( 119 | 1AF1B4F2217B6BFC00BB554C /* Demo.app */, 120 | 1AF1B506217B6BFE00BB554C /* DemoTests.xctest */, 121 | 1AF1B511217B6BFE00BB554C /* DemoUITests.xctest */, 122 | ); 123 | name = Products; 124 | sourceTree = ""; 125 | }; 126 | 1AF1B4F4217B6BFC00BB554C /* Demo */ = { 127 | isa = PBXGroup; 128 | children = ( 129 | 1AF1B4F5217B6BFC00BB554C /* AppDelegate.swift */, 130 | 1AF1B524217B6CA300BB554C /* LoginPage */, 131 | 1AF1B523217B6C9000BB554C /* HomePage */, 132 | 1AF1B4FC217B6BFD00BB554C /* Assets.xcassets */, 133 | 1AF1B4FE217B6BFD00BB554C /* LaunchScreen.storyboard */, 134 | 1AF1B501217B6BFD00BB554C /* Info.plist */, 135 | 1A6D142A23B497A20026CED3 /* LoginPageHeaderView.swift */, 136 | ); 137 | path = Demo; 138 | sourceTree = ""; 139 | }; 140 | 1AF1B509217B6BFE00BB554C /* DemoTests */ = { 141 | isa = PBXGroup; 142 | children = ( 143 | 1AF1B50A217B6BFE00BB554C /* DemoTests.swift */, 144 | 1AF1B50C217B6BFE00BB554C /* Info.plist */, 145 | ); 146 | path = DemoTests; 147 | sourceTree = ""; 148 | }; 149 | 1AF1B514217B6BFE00BB554C /* DemoUITests */ = { 150 | isa = PBXGroup; 151 | children = ( 152 | 1AF1B515217B6BFE00BB554C /* DemoUITests.swift */, 153 | 1AF1B517217B6BFE00BB554C /* Info.plist */, 154 | ); 155 | path = DemoUITests; 156 | sourceTree = ""; 157 | }; 158 | 1AF1B523217B6C9000BB554C /* HomePage */ = { 159 | isa = PBXGroup; 160 | children = ( 161 | 1AF1B52B217B6DB200BB554C /* HomePageViewController.swift */, 162 | 1AF1B52C217B6DB200BB554C /* HomePagePageInteractor.swift */, 163 | 1AF1B52D217B6DB200BB554C /* HomePageBuilderRouter.swift */, 164 | ); 165 | path = HomePage; 166 | sourceTree = ""; 167 | }; 168 | 1AF1B524217B6CA300BB554C /* LoginPage */ = { 169 | isa = PBXGroup; 170 | children = ( 171 | 1AF1B525217B6DA100BB554C /* LoginPageViewController.swift */, 172 | 1AF1B526217B6DA100BB554C /* LoginPagePageInteractor.swift */, 173 | 1AF1B527217B6DA100BB554C /* LoginPageBuilderRouter.swift */, 174 | ); 175 | path = LoginPage; 176 | sourceTree = ""; 177 | }; 178 | 1AF1B531217B6E3200BB554C /* Frameworks */ = { 179 | isa = PBXGroup; 180 | children = ( 181 | 1AF1B532217B6E3200BB554C /* Architecture.framework */, 182 | ); 183 | name = Frameworks; 184 | sourceTree = ""; 185 | }; 186 | /* End PBXGroup section */ 187 | 188 | /* Begin PBXNativeTarget section */ 189 | 1AF1B4F1217B6BFC00BB554C /* Demo */ = { 190 | isa = PBXNativeTarget; 191 | buildConfigurationList = 1AF1B51A217B6BFE00BB554C /* Build configuration list for PBXNativeTarget "Demo" */; 192 | buildPhases = ( 193 | 1AF1B4EE217B6BFC00BB554C /* Sources */, 194 | 1AF1B4EF217B6BFC00BB554C /* Frameworks */, 195 | 1AF1B4F0217B6BFC00BB554C /* Resources */, 196 | 1AF1B536217B6E3700BB554C /* Embed Frameworks */, 197 | ); 198 | buildRules = ( 199 | ); 200 | dependencies = ( 201 | ); 202 | name = Demo; 203 | productName = Demo; 204 | productReference = 1AF1B4F2217B6BFC00BB554C /* Demo.app */; 205 | productType = "com.apple.product-type.application"; 206 | }; 207 | 1AF1B505217B6BFE00BB554C /* DemoTests */ = { 208 | isa = PBXNativeTarget; 209 | buildConfigurationList = 1AF1B51D217B6BFE00BB554C /* Build configuration list for PBXNativeTarget "DemoTests" */; 210 | buildPhases = ( 211 | 1AF1B502217B6BFE00BB554C /* Sources */, 212 | 1AF1B503217B6BFE00BB554C /* Frameworks */, 213 | 1AF1B504217B6BFE00BB554C /* Resources */, 214 | ); 215 | buildRules = ( 216 | ); 217 | dependencies = ( 218 | 1AF1B508217B6BFE00BB554C /* PBXTargetDependency */, 219 | ); 220 | name = DemoTests; 221 | productName = DemoTests; 222 | productReference = 1AF1B506217B6BFE00BB554C /* DemoTests.xctest */; 223 | productType = "com.apple.product-type.bundle.unit-test"; 224 | }; 225 | 1AF1B510217B6BFE00BB554C /* DemoUITests */ = { 226 | isa = PBXNativeTarget; 227 | buildConfigurationList = 1AF1B520217B6BFE00BB554C /* Build configuration list for PBXNativeTarget "DemoUITests" */; 228 | buildPhases = ( 229 | 1AF1B50D217B6BFE00BB554C /* Sources */, 230 | 1AF1B50E217B6BFE00BB554C /* Frameworks */, 231 | 1AF1B50F217B6BFE00BB554C /* Resources */, 232 | ); 233 | buildRules = ( 234 | ); 235 | dependencies = ( 236 | 1AF1B513217B6BFE00BB554C /* PBXTargetDependency */, 237 | ); 238 | name = DemoUITests; 239 | productName = DemoUITests; 240 | productReference = 1AF1B511217B6BFE00BB554C /* DemoUITests.xctest */; 241 | productType = "com.apple.product-type.bundle.ui-testing"; 242 | }; 243 | /* End PBXNativeTarget section */ 244 | 245 | /* Begin PBXProject section */ 246 | 1AF1B4EA217B6BFC00BB554C /* Project object */ = { 247 | isa = PBXProject; 248 | attributes = { 249 | LastSwiftUpdateCheck = 1000; 250 | LastUpgradeCheck = 1000; 251 | ORGANIZATIONNAME = MolueJames; 252 | TargetAttributes = { 253 | 1AF1B4F1217B6BFC00BB554C = { 254 | CreatedOnToolsVersion = 10.0; 255 | }; 256 | 1AF1B505217B6BFE00BB554C = { 257 | CreatedOnToolsVersion = 10.0; 258 | TestTargetID = 1AF1B4F1217B6BFC00BB554C; 259 | }; 260 | 1AF1B510217B6BFE00BB554C = { 261 | CreatedOnToolsVersion = 10.0; 262 | TestTargetID = 1AF1B4F1217B6BFC00BB554C; 263 | }; 264 | }; 265 | }; 266 | buildConfigurationList = 1AF1B4ED217B6BFC00BB554C /* Build configuration list for PBXProject "Demo" */; 267 | compatibilityVersion = "Xcode 9.3"; 268 | developmentRegion = en; 269 | hasScannedForEncodings = 0; 270 | knownRegions = ( 271 | en, 272 | Base, 273 | ); 274 | mainGroup = 1AF1B4E9217B6BFC00BB554C; 275 | productRefGroup = 1AF1B4F3217B6BFC00BB554C /* Products */; 276 | projectDirPath = ""; 277 | projectRoot = ""; 278 | targets = ( 279 | 1AF1B4F1217B6BFC00BB554C /* Demo */, 280 | 1AF1B505217B6BFE00BB554C /* DemoTests */, 281 | 1AF1B510217B6BFE00BB554C /* DemoUITests */, 282 | ); 283 | }; 284 | /* End PBXProject section */ 285 | 286 | /* Begin PBXResourcesBuildPhase section */ 287 | 1AF1B4F0217B6BFC00BB554C /* Resources */ = { 288 | isa = PBXResourcesBuildPhase; 289 | buildActionMask = 2147483647; 290 | files = ( 291 | 1AF1B500217B6BFD00BB554C /* LaunchScreen.storyboard in Resources */, 292 | 1AF1B4FD217B6BFD00BB554C /* Assets.xcassets in Resources */, 293 | ); 294 | runOnlyForDeploymentPostprocessing = 0; 295 | }; 296 | 1AF1B504217B6BFE00BB554C /* Resources */ = { 297 | isa = PBXResourcesBuildPhase; 298 | buildActionMask = 2147483647; 299 | files = ( 300 | ); 301 | runOnlyForDeploymentPostprocessing = 0; 302 | }; 303 | 1AF1B50F217B6BFE00BB554C /* Resources */ = { 304 | isa = PBXResourcesBuildPhase; 305 | buildActionMask = 2147483647; 306 | files = ( 307 | ); 308 | runOnlyForDeploymentPostprocessing = 0; 309 | }; 310 | /* End PBXResourcesBuildPhase section */ 311 | 312 | /* Begin PBXSourcesBuildPhase section */ 313 | 1AF1B4EE217B6BFC00BB554C /* Sources */ = { 314 | isa = PBXSourcesBuildPhase; 315 | buildActionMask = 2147483647; 316 | files = ( 317 | 1A6D142B23B497A20026CED3 /* LoginPageHeaderView.swift in Sources */, 318 | 1AF1B529217B6DA100BB554C /* LoginPagePageInteractor.swift in Sources */, 319 | 1AF1B52A217B6DA100BB554C /* LoginPageBuilderRouter.swift in Sources */, 320 | 1AF1B4F6217B6BFC00BB554C /* AppDelegate.swift in Sources */, 321 | 1AF1B52E217B6DB200BB554C /* HomePageViewController.swift in Sources */, 322 | 1AF1B530217B6DB200BB554C /* HomePageBuilderRouter.swift in Sources */, 323 | 1AF1B528217B6DA100BB554C /* LoginPageViewController.swift in Sources */, 324 | 1AF1B52F217B6DB200BB554C /* HomePagePageInteractor.swift in Sources */, 325 | ); 326 | runOnlyForDeploymentPostprocessing = 0; 327 | }; 328 | 1AF1B502217B6BFE00BB554C /* Sources */ = { 329 | isa = PBXSourcesBuildPhase; 330 | buildActionMask = 2147483647; 331 | files = ( 332 | 1AF1B50B217B6BFE00BB554C /* DemoTests.swift in Sources */, 333 | ); 334 | runOnlyForDeploymentPostprocessing = 0; 335 | }; 336 | 1AF1B50D217B6BFE00BB554C /* Sources */ = { 337 | isa = PBXSourcesBuildPhase; 338 | buildActionMask = 2147483647; 339 | files = ( 340 | 1AF1B516217B6BFE00BB554C /* DemoUITests.swift in Sources */, 341 | ); 342 | runOnlyForDeploymentPostprocessing = 0; 343 | }; 344 | /* End PBXSourcesBuildPhase section */ 345 | 346 | /* Begin PBXTargetDependency section */ 347 | 1AF1B508217B6BFE00BB554C /* PBXTargetDependency */ = { 348 | isa = PBXTargetDependency; 349 | target = 1AF1B4F1217B6BFC00BB554C /* Demo */; 350 | targetProxy = 1AF1B507217B6BFE00BB554C /* PBXContainerItemProxy */; 351 | }; 352 | 1AF1B513217B6BFE00BB554C /* PBXTargetDependency */ = { 353 | isa = PBXTargetDependency; 354 | target = 1AF1B4F1217B6BFC00BB554C /* Demo */; 355 | targetProxy = 1AF1B512217B6BFE00BB554C /* PBXContainerItemProxy */; 356 | }; 357 | /* End PBXTargetDependency section */ 358 | 359 | /* Begin PBXVariantGroup section */ 360 | 1AF1B4FE217B6BFD00BB554C /* LaunchScreen.storyboard */ = { 361 | isa = PBXVariantGroup; 362 | children = ( 363 | 1AF1B4FF217B6BFD00BB554C /* Base */, 364 | ); 365 | name = LaunchScreen.storyboard; 366 | sourceTree = ""; 367 | }; 368 | /* End PBXVariantGroup section */ 369 | 370 | /* Begin XCBuildConfiguration section */ 371 | 1AF1B518217B6BFE00BB554C /* Debug */ = { 372 | isa = XCBuildConfiguration; 373 | buildSettings = { 374 | ALWAYS_SEARCH_USER_PATHS = NO; 375 | CLANG_ANALYZER_NONNULL = YES; 376 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 377 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 378 | CLANG_CXX_LIBRARY = "libc++"; 379 | CLANG_ENABLE_MODULES = YES; 380 | CLANG_ENABLE_OBJC_ARC = YES; 381 | CLANG_ENABLE_OBJC_WEAK = YES; 382 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 383 | CLANG_WARN_BOOL_CONVERSION = YES; 384 | CLANG_WARN_COMMA = YES; 385 | CLANG_WARN_CONSTANT_CONVERSION = YES; 386 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 387 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 388 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 389 | CLANG_WARN_EMPTY_BODY = YES; 390 | CLANG_WARN_ENUM_CONVERSION = YES; 391 | CLANG_WARN_INFINITE_RECURSION = YES; 392 | CLANG_WARN_INT_CONVERSION = YES; 393 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 394 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 395 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 396 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 397 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 398 | CLANG_WARN_STRICT_PROTOTYPES = YES; 399 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 400 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 401 | CLANG_WARN_UNREACHABLE_CODE = YES; 402 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 403 | CODE_SIGN_IDENTITY = "iPhone Developer"; 404 | COPY_PHASE_STRIP = NO; 405 | DEBUG_INFORMATION_FORMAT = dwarf; 406 | ENABLE_STRICT_OBJC_MSGSEND = YES; 407 | ENABLE_TESTABILITY = YES; 408 | GCC_C_LANGUAGE_STANDARD = gnu11; 409 | GCC_DYNAMIC_NO_PIC = NO; 410 | GCC_NO_COMMON_BLOCKS = YES; 411 | GCC_OPTIMIZATION_LEVEL = 0; 412 | GCC_PREPROCESSOR_DEFINITIONS = ( 413 | "DEBUG=1", 414 | "$(inherited)", 415 | ); 416 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 417 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 418 | GCC_WARN_UNDECLARED_SELECTOR = YES; 419 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 420 | GCC_WARN_UNUSED_FUNCTION = YES; 421 | GCC_WARN_UNUSED_VARIABLE = YES; 422 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 423 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 424 | MTL_FAST_MATH = YES; 425 | ONLY_ACTIVE_ARCH = YES; 426 | SDKROOT = iphoneos; 427 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 428 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 429 | }; 430 | name = Debug; 431 | }; 432 | 1AF1B519217B6BFE00BB554C /* Release */ = { 433 | isa = XCBuildConfiguration; 434 | buildSettings = { 435 | ALWAYS_SEARCH_USER_PATHS = NO; 436 | CLANG_ANALYZER_NONNULL = YES; 437 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 438 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 439 | CLANG_CXX_LIBRARY = "libc++"; 440 | CLANG_ENABLE_MODULES = YES; 441 | CLANG_ENABLE_OBJC_ARC = YES; 442 | CLANG_ENABLE_OBJC_WEAK = YES; 443 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 444 | CLANG_WARN_BOOL_CONVERSION = YES; 445 | CLANG_WARN_COMMA = YES; 446 | CLANG_WARN_CONSTANT_CONVERSION = YES; 447 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 448 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 449 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 450 | CLANG_WARN_EMPTY_BODY = YES; 451 | CLANG_WARN_ENUM_CONVERSION = YES; 452 | CLANG_WARN_INFINITE_RECURSION = YES; 453 | CLANG_WARN_INT_CONVERSION = YES; 454 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 455 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 456 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 457 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 458 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 459 | CLANG_WARN_STRICT_PROTOTYPES = YES; 460 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 461 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 462 | CLANG_WARN_UNREACHABLE_CODE = YES; 463 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 464 | CODE_SIGN_IDENTITY = "iPhone Developer"; 465 | COPY_PHASE_STRIP = NO; 466 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 467 | ENABLE_NS_ASSERTIONS = NO; 468 | ENABLE_STRICT_OBJC_MSGSEND = YES; 469 | GCC_C_LANGUAGE_STANDARD = gnu11; 470 | GCC_NO_COMMON_BLOCKS = YES; 471 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 472 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 473 | GCC_WARN_UNDECLARED_SELECTOR = YES; 474 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 475 | GCC_WARN_UNUSED_FUNCTION = YES; 476 | GCC_WARN_UNUSED_VARIABLE = YES; 477 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 478 | MTL_ENABLE_DEBUG_INFO = NO; 479 | MTL_FAST_MATH = YES; 480 | SDKROOT = iphoneos; 481 | SWIFT_COMPILATION_MODE = wholemodule; 482 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 483 | VALIDATE_PRODUCT = YES; 484 | }; 485 | name = Release; 486 | }; 487 | 1AF1B51B217B6BFE00BB554C /* Debug */ = { 488 | isa = XCBuildConfiguration; 489 | buildSettings = { 490 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 491 | CODE_SIGN_STYLE = Automatic; 492 | INFOPLIST_FILE = Demo/Info.plist; 493 | LD_RUNPATH_SEARCH_PATHS = ( 494 | "$(inherited)", 495 | "@executable_path/Frameworks", 496 | ); 497 | PRODUCT_BUNDLE_IDENTIFIER = Molue.Demo; 498 | PRODUCT_NAME = "$(TARGET_NAME)"; 499 | SWIFT_VERSION = 4.2; 500 | TARGETED_DEVICE_FAMILY = 1; 501 | }; 502 | name = Debug; 503 | }; 504 | 1AF1B51C217B6BFE00BB554C /* Release */ = { 505 | isa = XCBuildConfiguration; 506 | buildSettings = { 507 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 508 | CODE_SIGN_STYLE = Automatic; 509 | INFOPLIST_FILE = Demo/Info.plist; 510 | LD_RUNPATH_SEARCH_PATHS = ( 511 | "$(inherited)", 512 | "@executable_path/Frameworks", 513 | ); 514 | PRODUCT_BUNDLE_IDENTIFIER = Molue.Demo; 515 | PRODUCT_NAME = "$(TARGET_NAME)"; 516 | SWIFT_VERSION = 4.2; 517 | TARGETED_DEVICE_FAMILY = 1; 518 | }; 519 | name = Release; 520 | }; 521 | 1AF1B51E217B6BFE00BB554C /* Debug */ = { 522 | isa = XCBuildConfiguration; 523 | buildSettings = { 524 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 525 | BUNDLE_LOADER = "$(TEST_HOST)"; 526 | CODE_SIGN_STYLE = Automatic; 527 | INFOPLIST_FILE = DemoTests/Info.plist; 528 | LD_RUNPATH_SEARCH_PATHS = ( 529 | "$(inherited)", 530 | "@executable_path/Frameworks", 531 | "@loader_path/Frameworks", 532 | ); 533 | PRODUCT_BUNDLE_IDENTIFIER = Molue.DemoTests; 534 | PRODUCT_NAME = "$(TARGET_NAME)"; 535 | SWIFT_VERSION = 4.2; 536 | TARGETED_DEVICE_FAMILY = "1,2"; 537 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Demo.app/Demo"; 538 | }; 539 | name = Debug; 540 | }; 541 | 1AF1B51F217B6BFE00BB554C /* Release */ = { 542 | isa = XCBuildConfiguration; 543 | buildSettings = { 544 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 545 | BUNDLE_LOADER = "$(TEST_HOST)"; 546 | CODE_SIGN_STYLE = Automatic; 547 | INFOPLIST_FILE = DemoTests/Info.plist; 548 | LD_RUNPATH_SEARCH_PATHS = ( 549 | "$(inherited)", 550 | "@executable_path/Frameworks", 551 | "@loader_path/Frameworks", 552 | ); 553 | PRODUCT_BUNDLE_IDENTIFIER = Molue.DemoTests; 554 | PRODUCT_NAME = "$(TARGET_NAME)"; 555 | SWIFT_VERSION = 4.2; 556 | TARGETED_DEVICE_FAMILY = "1,2"; 557 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Demo.app/Demo"; 558 | }; 559 | name = Release; 560 | }; 561 | 1AF1B521217B6BFE00BB554C /* Debug */ = { 562 | isa = XCBuildConfiguration; 563 | buildSettings = { 564 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 565 | CODE_SIGN_STYLE = Automatic; 566 | INFOPLIST_FILE = DemoUITests/Info.plist; 567 | LD_RUNPATH_SEARCH_PATHS = ( 568 | "$(inherited)", 569 | "@executable_path/Frameworks", 570 | "@loader_path/Frameworks", 571 | ); 572 | PRODUCT_BUNDLE_IDENTIFIER = Molue.DemoUITests; 573 | PRODUCT_NAME = "$(TARGET_NAME)"; 574 | SWIFT_VERSION = 4.2; 575 | TARGETED_DEVICE_FAMILY = "1,2"; 576 | TEST_TARGET_NAME = Demo; 577 | }; 578 | name = Debug; 579 | }; 580 | 1AF1B522217B6BFE00BB554C /* Release */ = { 581 | isa = XCBuildConfiguration; 582 | buildSettings = { 583 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 584 | CODE_SIGN_STYLE = Automatic; 585 | INFOPLIST_FILE = DemoUITests/Info.plist; 586 | LD_RUNPATH_SEARCH_PATHS = ( 587 | "$(inherited)", 588 | "@executable_path/Frameworks", 589 | "@loader_path/Frameworks", 590 | ); 591 | PRODUCT_BUNDLE_IDENTIFIER = Molue.DemoUITests; 592 | PRODUCT_NAME = "$(TARGET_NAME)"; 593 | SWIFT_VERSION = 4.2; 594 | TARGETED_DEVICE_FAMILY = "1,2"; 595 | TEST_TARGET_NAME = Demo; 596 | }; 597 | name = Release; 598 | }; 599 | /* End XCBuildConfiguration section */ 600 | 601 | /* Begin XCConfigurationList section */ 602 | 1AF1B4ED217B6BFC00BB554C /* Build configuration list for PBXProject "Demo" */ = { 603 | isa = XCConfigurationList; 604 | buildConfigurations = ( 605 | 1AF1B518217B6BFE00BB554C /* Debug */, 606 | 1AF1B519217B6BFE00BB554C /* Release */, 607 | ); 608 | defaultConfigurationIsVisible = 0; 609 | defaultConfigurationName = Release; 610 | }; 611 | 1AF1B51A217B6BFE00BB554C /* Build configuration list for PBXNativeTarget "Demo" */ = { 612 | isa = XCConfigurationList; 613 | buildConfigurations = ( 614 | 1AF1B51B217B6BFE00BB554C /* Debug */, 615 | 1AF1B51C217B6BFE00BB554C /* Release */, 616 | ); 617 | defaultConfigurationIsVisible = 0; 618 | defaultConfigurationName = Release; 619 | }; 620 | 1AF1B51D217B6BFE00BB554C /* Build configuration list for PBXNativeTarget "DemoTests" */ = { 621 | isa = XCConfigurationList; 622 | buildConfigurations = ( 623 | 1AF1B51E217B6BFE00BB554C /* Debug */, 624 | 1AF1B51F217B6BFE00BB554C /* Release */, 625 | ); 626 | defaultConfigurationIsVisible = 0; 627 | defaultConfigurationName = Release; 628 | }; 629 | 1AF1B520217B6BFE00BB554C /* Build configuration list for PBXNativeTarget "DemoUITests" */ = { 630 | isa = XCConfigurationList; 631 | buildConfigurations = ( 632 | 1AF1B521217B6BFE00BB554C /* Debug */, 633 | 1AF1B522217B6BFE00BB554C /* Release */, 634 | ); 635 | defaultConfigurationIsVisible = 0; 636 | defaultConfigurationName = Release; 637 | }; 638 | /* End XCConfigurationList section */ 639 | }; 640 | rootObject = 1AF1B4EA217B6BFC00BB554C /* Project object */; 641 | } 642 | -------------------------------------------------------------------------------- /Demo/Demo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Demo/Demo.xcodeproj/project.xcworkspace/xcuserdata/James.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MolueJames/VIPER/abbab8c8039e8cc20990342c59228036ac6c6415/Demo/Demo.xcodeproj/project.xcworkspace/xcuserdata/James.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Demo/Demo.xcodeproj/xcuserdata/James.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Demo.xcscheme 8 | 9 | orderHint 10 | 1 11 | 12 | Demo.xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 0 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Demo/Demo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Demo 4 | // 5 | // Created by MolueJames on 2018/10/20. 6 | // Copyright © 2018 MolueJames. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | let controller = HomePageComponentBuilder().build() 20 | self.window = UIWindow(frame: UIScreen.main.bounds) 21 | self.window?.isHidden = false 22 | let navController = UINavigationController(rootViewController: controller) 23 | self.window?.rootViewController = navController 24 | return true 25 | } 26 | 27 | func applicationWillResignActive(_ application: UIApplication) { 28 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 29 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 30 | } 31 | 32 | func applicationDidEnterBackground(_ application: UIApplication) { 33 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 34 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 35 | } 36 | 37 | func applicationWillEnterForeground(_ application: UIApplication) { 38 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 39 | } 40 | 41 | func applicationDidBecomeActive(_ application: UIApplication) { 42 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 43 | } 44 | 45 | func applicationWillTerminate(_ application: UIApplication) { 46 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 47 | } 48 | 49 | 50 | } 51 | 52 | -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Demo/Demo/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 | -------------------------------------------------------------------------------- /Demo/Demo/HomePage/HomePageBuilderRouter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomePageBuilderRouter.swift 3 | // Demo 4 | // 5 | // Created by MolueJames on 2018/10/20. 6 | // Copyright © 2018 MolueJames. All rights reserved. 7 | // 8 | 9 | import Architecture 10 | 11 | protocol HomePageRouterInteractable: LoginPageInteractListener { 12 | var viewRouter: HomePageViewableRouting? { get set } 13 | var listener: HomePageInteractListener? { get set } 14 | } 15 | 16 | protocol HomePageViewControllable: class { 17 | // 定义一些该页面需要的其他commponent的组件, 比如该页面的childViewController等. 18 | func pushToTargetViewController(_ controller: UIViewController) 19 | } 20 | 21 | final class HomePageViewableRouter: MolueViewableRouting { 22 | 23 | unowned var interactor: HomePageRouterInteractable 24 | 25 | unowned var controller: HomePageViewControllable 26 | 27 | @discardableResult 28 | required init(interactor: HomePageRouterInteractable, controller: HomePageViewControllable) { 29 | self.controller = controller 30 | self.interactor = interactor 31 | interactor.viewRouter = self 32 | } 33 | } 34 | 35 | extension HomePageViewableRouter: HomePageViewableRouting { 36 | func pushToLoginPage() { 37 | let target = LoginPageComponentBuilder().build(listener: self.interactor) 38 | self.controller.pushToTargetViewController(target) 39 | } 40 | } 41 | 42 | protocol HomePageInteractListener: class { 43 | //用于定义其他的Component需要定义的协议方法 44 | } 45 | 46 | protocol HomePageComponentBuildable: MolueComponentBuildable { 47 | //定义当前的Component的构造方法. 48 | func build(listener: HomePageInteractListener) -> UIViewController 49 | 50 | func build() -> UIViewController 51 | } 52 | 53 | class HomePageComponentBuilder: MolueComponentBuilder, HomePageComponentBuildable { 54 | func build(listener: HomePageInteractListener) -> UIViewController { 55 | let controller = HomePageViewController() 56 | let interactor = HomePagePageInteractor(presenter: controller) 57 | HomePageViewableRouter(interactor: interactor, controller: controller) 58 | interactor.listener = listener 59 | return controller 60 | } 61 | 62 | func build() -> UIViewController { 63 | let controller = HomePageViewController() 64 | let interactor = HomePagePageInteractor(presenter: controller) 65 | HomePageViewableRouter(interactor: interactor, controller: controller) 66 | return controller 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Demo/Demo/HomePage/HomePagePageInteractor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomePagePageInteractor.swift 3 | // Demo 4 | // 5 | // Created by MolueJames on 2018/10/20. 6 | // Copyright © 2018 MolueJames. All rights reserved. 7 | // 8 | 9 | import Architecture 10 | 11 | protocol HomePageViewableRouting: class { 12 | // 定义一些页面跳转的方法, 比如Push, Presenter等. 13 | func pushToLoginPage() 14 | } 15 | 16 | protocol HomePagePagePresentable: MolueInteractorPresentable { 17 | var listener: HomePagePresentableListener? { get set } 18 | // 定义一些页面需要的方法, 比如刷新页面的显示内容等. 19 | func showLoginPageTestNumber(_ number: String) 20 | } 21 | 22 | final class HomePagePageInteractor: MoluePresenterInteractable { 23 | 24 | unowned var presenter: HomePagePagePresentable 25 | 26 | var viewRouter: HomePageViewableRouting? 27 | 28 | weak var listener: HomePageInteractListener? 29 | 30 | required init(presenter: HomePagePagePresentable) { 31 | self.presenter = presenter 32 | presenter.listener = self 33 | } 34 | } 35 | 36 | extension HomePagePageInteractor: HomePageRouterInteractable { 37 | var list: [String] { 38 | return ["1", "2", "3", "4", "5"] 39 | } 40 | 41 | func loginPageDidSelectedTestNumber(_ number: String) { 42 | self.presenter.showLoginPageTestNumber(number) 43 | } 44 | } 45 | 46 | extension HomePagePageInteractor: HomePagePresentableListener { 47 | func jumpToLoginPage() { 48 | guard let router = self.viewRouter else {return} 49 | router.pushToLoginPage() 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Demo/Demo/HomePage/HomePageViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomePageViewController.swift 3 | // Demo 4 | // 5 | // Created by MolueJames on 2018/10/20. 6 | // Copyright © 2018 MolueJames. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol HomePagePresentableListener: class { 12 | // 定义一些当前页面需要的业务逻辑, 比如网络请求. 13 | func jumpToLoginPage() 14 | } 15 | 16 | protocol HomePageViewElementsLayout where Self: HomePageViewController { 17 | var button: UIButton! {get} 18 | var label: UILabel! {get} 19 | func doViewControllerLayout() 20 | } 21 | 22 | extension HomePageViewElementsLayout { 23 | func doViewControllerLayout() { 24 | self.button.frame = CGRect(x: 0, y: 0, width: 100, height: 45) 25 | self.button.center = self.view.center 26 | 27 | self.label.frame = CGRect(x: 30, y: 100, width: self.view.frame.width - 60, height: 45) 28 | } 29 | } 30 | 31 | final class HomePageViewController: UIViewController, HomePageViewElementsLayout { 32 | //MARK: View Controller Properties 33 | var listener: HomePagePresentableListener? 34 | lazy var label: UILabel! = { 35 | let label = UILabel() 36 | label.textColor = .red 37 | label.textAlignment = .center 38 | self.view.addSubview(label) 39 | return label 40 | }() 41 | 42 | lazy var button: UIButton! = { 43 | let button = UIButton() 44 | button.backgroundColor = UIColor.red 45 | button.addTarget(self, action: #selector(buttonClicked), for: .touchUpInside) 46 | self.view.addSubview(button) 47 | return button 48 | }() 49 | 50 | @IBAction func buttonClicked(button: UIButton) { 51 | guard let listener = self.listener else {return} 52 | listener.jumpToLoginPage() 53 | } 54 | 55 | //MARK: View Controller Life Cycle 56 | override func viewDidLoad() { 57 | super.viewDidLoad() 58 | // Do any additional setup after loading the view. 59 | self.view.backgroundColor = .white 60 | self.doViewControllerLayout() 61 | } 62 | } 63 | 64 | extension HomePageViewController: HomePagePagePresentable { 65 | func showLoginPageTestNumber(_ number: String) { 66 | self.label.text = number 67 | } 68 | } 69 | 70 | extension HomePageViewController: HomePageViewControllable { 71 | func pushToTargetViewController(_ controller: UIViewController) { 72 | self.navigationController?.pushViewController(controller, animated: true) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Demo/Demo/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 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIRequiredDeviceCapabilities 26 | 27 | armv7 28 | 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Demo/Demo/LoginPage/LoginPageBuilderRouter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoginPageBuilderRouter.swift 3 | // Demo 4 | // 5 | // Created by MolueJames on 2018/10/20. 6 | // Copyright © 2018 MolueJames. All rights reserved. 7 | // 8 | 9 | import Architecture 10 | 11 | protocol LoginPageRouterInteractable: class { 12 | var viewRouter: LoginPageViewableRouting? { get set } 13 | var listener: LoginPageInteractListener? { get set } 14 | } 15 | 16 | protocol LoginPageViewControllable: class { 17 | // 定义一些该页面需要的其他commponent的组件, 比如该页面的childViewController等. 18 | func doPopBackFromLoginPage() 19 | } 20 | 21 | final class LoginPageViewableRouter: MolueViewableRouting { 22 | 23 | unowned var interactor: LoginPageRouterInteractable 24 | 25 | unowned var controller: LoginPageViewControllable 26 | 27 | @discardableResult 28 | required init(interactor: LoginPageRouterInteractable, controller: LoginPageViewControllable) { 29 | self.controller = controller 30 | self.interactor = interactor 31 | interactor.viewRouter = self 32 | } 33 | } 34 | 35 | extension LoginPageViewableRouter: LoginPageViewableRouting { 36 | func popBackFromLoginPage() { 37 | self.controller.doPopBackFromLoginPage() 38 | } 39 | } 40 | 41 | protocol LoginPageInteractListener: class { 42 | //用于定义其他的Component需要定义的协议方法 43 | func loginPageDidSelectedTestNumber(_ number: String) 44 | var list: [String] {get} 45 | } 46 | 47 | protocol LoginPageComponentBuildable: MolueComponentBuildable { 48 | //定义当前的Component的构造方法. 49 | func build(listener: LoginPageInteractListener) -> UIViewController 50 | } 51 | 52 | class LoginPageComponentBuilder: MolueComponentBuilder, LoginPageComponentBuildable { 53 | func build(listener: LoginPageInteractListener) -> UIViewController { 54 | let controller = LoginPageViewController() 55 | let interactor = LoginPagePageInteractor(presenter: controller) 56 | LoginPageViewableRouter(interactor: interactor, controller: controller) 57 | interactor.listener = listener 58 | return controller 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Demo/Demo/LoginPage/LoginPagePageInteractor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoginPagePageInteractor.swift 3 | // Demo 4 | // 5 | // Created by MolueJames on 2018/10/20. 6 | // Copyright © 2018 MolueJames. All rights reserved. 7 | // 8 | 9 | import Architecture 10 | 11 | protocol LoginPageViewableRouting: class { 12 | // 定义一些页面跳转的方法, 比如Push, Presenter等. 13 | func popBackFromLoginPage() 14 | } 15 | 16 | protocol LoginPagePagePresentable: MolueInteractorPresentable { 17 | var listener: LoginPagePresentableListener? { get set } 18 | // 定义一些页面需要的方法, 比如刷新页面的显示内容等. 19 | } 20 | 21 | final class LoginPagePageInteractor: MoluePresenterInteractable { 22 | 23 | unowned var presenter: LoginPagePagePresentable 24 | 25 | var viewRouter: LoginPageViewableRouting? 26 | 27 | weak var listener: LoginPageInteractListener? 28 | 29 | var list: [String]? 30 | 31 | required init(presenter: LoginPagePagePresentable) { 32 | self.presenter = presenter 33 | presenter.listener = self 34 | } 35 | 36 | func viewControllerDidLoad() { 37 | guard let listener = self.listener else {return} 38 | self.list = listener.list 39 | } 40 | } 41 | 42 | extension LoginPagePageInteractor: LoginPageRouterInteractable { 43 | 44 | } 45 | 46 | extension LoginPagePageInteractor: LoginPagePresentableListener { 47 | func postNetworkRequest() { 48 | 49 | } 50 | 51 | func didSelectedTestNumber(_ number: String) { 52 | guard let listener = self.listener, let router = self.viewRouter else {return} 53 | listener.loginPageDidSelectedTestNumber(number) 54 | router.popBackFromLoginPage() 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Demo/Demo/LoginPage/LoginPageViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoginPageViewController.swift 3 | // Demo 4 | // 5 | // Created by MolueJames on 2018/10/20. 6 | // Copyright © 2018 MolueJames. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol LoginPagePresentableListener: LoginPageViewableInteractable { 12 | // 定义一些当前页面需要的业务逻辑, 比如网络请求. 13 | func didSelectedTestNumber(_ number: String) 14 | var list: [String]? {get} 15 | func viewControllerDidLoad() 16 | } 17 | 18 | protocol LoginPageViewElementsLayout where Self: LoginPageViewController{ 19 | var tableView: UITableView! {get} 20 | func doViewControllerLayout() 21 | } 22 | 23 | extension LoginPageViewElementsLayout { 24 | func doViewControllerLayout() { 25 | self.tableView.frame = self.view.bounds 26 | } 27 | } 28 | 29 | final class LoginPageViewController: UIViewController, LoginPageViewElementsLayout { 30 | //MARK: View Controller Properties 31 | var listener: LoginPagePresentableListener? 32 | 33 | lazy var tableView: UITableView! = { 34 | let tableView = UITableView() 35 | self.view.addSubview(tableView) 36 | tableView.delegate = self 37 | tableView.dataSource = self 38 | tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Identifier") 39 | return tableView 40 | }() 41 | 42 | lazy var headerView: LoginPageHeaderView! = { 43 | let builder = LoginPageViewableBuilder() 44 | return builder.build(listener: self.listener) 45 | }() 46 | 47 | //MARK: View Controller Life Cycle 48 | override func viewDidLoad() { 49 | super.viewDidLoad() 50 | // Do any additional setup after loading the view. 51 | self.view.backgroundColor = .white 52 | self.doViewControllerLayout() 53 | self.listener?.viewControllerDidLoad() 54 | } 55 | } 56 | 57 | extension LoginPageViewController: LoginPagePagePresentable { 58 | 59 | } 60 | 61 | extension LoginPageViewController: LoginPageViewControllable { 62 | func doPopBackFromLoginPage() { 63 | self.navigationController?.popViewController(animated: true) 64 | } 65 | } 66 | 67 | extension LoginPageViewController: UITableViewDelegate, UITableViewDataSource { 68 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 69 | return self.listener?.list?.count ?? 0 70 | } 71 | 72 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 73 | let cell = tableView.dequeueReusableCell(withIdentifier: "Identifier")! 74 | cell.textLabel?.text = self.listener?.list?[indexPath.row] 75 | return cell 76 | } 77 | 78 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 79 | guard let listener = self.listener else {return} 80 | listener.didSelectedTestNumber(self.listener?.list?[indexPath.row] ?? "0") 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /Demo/Demo/LoginPageHeaderView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoginPageTableViewCell.swift 3 | // Demo 4 | // 5 | // Created by James on 2019/12/26. 6 | // Copyright © 2019 MolueJames. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Architecture 11 | import UIKit 12 | 13 | public class LoginPageHeaderView: UIView, LoginPageViewablePresentable { 14 | public var listener: LoginPageViewableListenerable? 15 | 16 | lazy var submitButton: UIButton = { 17 | let button = UIButton() 18 | let selector: Selector = #selector(submitButtonClicked) 19 | button.addTarget(self, action: selector, for: .touchUpInside) 20 | return button 21 | }() 22 | 23 | @IBAction func submitButtonClicked(_ sender: Any) { 24 | self.listener?.submitButtonClicked() 25 | } 26 | } 27 | 28 | public protocol LoginPageViewableListenerable: class { 29 | //MARK: 定义一些当前页面需要的业务逻辑, 比如网络请求. 30 | func submitButtonClicked() 31 | } 32 | 33 | public protocol LoginPageViewableInteractable: class { 34 | //MARK: 用于定义其他的Component需要定义的协议方法 35 | func postNetworkRequest() 36 | } 37 | 38 | public protocol LoginPageViewablePresentable: MolueViewablePresentable { 39 | //MARK: 定义一些页面需要的方法, 比如刷新页面的显示内容等. 40 | var listener: LoginPageViewableListenerable? { get set } 41 | } 42 | 43 | class LoginPageViewableInteractor: MolueViewableInteractable { 44 | 45 | unowned var presenter: LoginPageViewablePresentable 46 | 47 | weak var listener: LoginPageViewableInteractable? 48 | 49 | required init(presenter: LoginPageViewablePresentable) { 50 | self.presenter = presenter 51 | presenter.listener = self 52 | } 53 | } 54 | 55 | extension LoginPageViewableInteractor: LoginPageViewableListenerable { 56 | 57 | func submitButtonClicked() { 58 | self.listener?.postNetworkRequest() 59 | } 60 | } 61 | 62 | public class LoginPageViewableBuilder { 63 | //MARK: 定义当前的Component的构造方法. 64 | func build(listener: LoginPageViewableInteractable?) -> LoginPageHeaderView { 65 | let presenter: LoginPageHeaderView = LoginPageHeaderView() 66 | let interactor = LoginPageViewableInteractor(presenter: presenter) 67 | interactor.listener = listener 68 | return presenter 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Demo/DemoTests/DemoTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DemoTests.swift 3 | // DemoTests 4 | // 5 | // Created by MolueJames on 2018/10/20. 6 | // Copyright © 2018 MolueJames. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import Demo 11 | 12 | class DemoTests: XCTestCase { 13 | 14 | override func setUp() { 15 | // Put setup code here. This method is called before the invocation of each test method in the class. 16 | } 17 | 18 | override func tearDown() { 19 | // Put teardown code here. This method is called after the invocation of each test method in the class. 20 | } 21 | 22 | func testExample() { 23 | // This is an example of a functional test case. 24 | // Use XCTAssert and related functions to verify your tests produce the correct results. 25 | } 26 | 27 | func testPerformanceExample() { 28 | // This is an example of a performance test case. 29 | self.measure { 30 | // Put the code you want to measure the time of here. 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /Demo/DemoTests/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 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Demo/DemoUITests/DemoUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DemoUITests.swift 3 | // DemoUITests 4 | // 5 | // Created by MolueJames on 2018/10/20. 6 | // Copyright © 2018 MolueJames. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class DemoUITests: XCTestCase { 12 | 13 | override func setUp() { 14 | // Put setup code here. This method is called before the invocation of each test method in the class. 15 | 16 | // In UI tests it is usually best to stop immediately when a failure occurs. 17 | continueAfterFailure = false 18 | 19 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 20 | XCUIApplication().launch() 21 | 22 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 23 | } 24 | 25 | override func tearDown() { 26 | // Put teardown code here. This method is called after the invocation of each test method in the class. 27 | } 28 | 29 | func testExample() { 30 | // Use recording to get started writing UI tests. 31 | // Use XCTAssert and related functions to verify your tests produce the correct results. 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /Demo/DemoUITests/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 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | VIPER-Swift 2 | 3 | 一个基于 VIPER 架构 的轻量级实现,通过模块化设计提升代码可维护性。支持动态模块组合和清晰的分层通信机制。 4 | 5 | 项目概述 6 | 7 | 本项目提供了一个 VIPER 架构 的 Swift 实现方案,通过 Builder 模式 和 Listener 机制 简化模块化开发。每个模块被拆分为独立的组件(View、Interactor、Presenter、Router、Entity),并通过 Builder 动态组合,实现高内聚、低耦合的设计目标。 8 | 9 | 核心架构 10 | 11 | VIPER 分层说明 12 | 13 | 层级 说明 14 | 15 | V (View) 负责 UI 层(如 UIViewController),通过 Presenter 更新界面。 16 | 17 | I (Interactor) 处理业务逻辑和数据操作,是模块的核心逻辑层。 18 | 19 | P (Presenter) 桥接 View 和 Interactor,处理数据转换和 UI 逻辑。 20 | 21 | E (Entity) 定义模块内部的数据模型(如 Data 类)。 22 | 23 | R (Router) 管理导航和依赖注入,负责模块间的跳转和生命周期管理。 24 | 25 | L (Listener) 完成两个VIPER之间的数据交互。 26 | 27 | 提供了Xctemplate模版,支持快速使用。 28 | -------------------------------------------------------------------------------- /VIPER.xctemplate/TemplateIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MolueJames/VIPER/abbab8c8039e8cc20990342c59228036ac6c6415/VIPER.xctemplate/TemplateIcon.png -------------------------------------------------------------------------------- /VIPER.xctemplate/TemplateIcon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MolueJames/VIPER/abbab8c8039e8cc20990342c59228036ac6c6415/VIPER.xctemplate/TemplateIcon@2x.png -------------------------------------------------------------------------------- /VIPER.xctemplate/TemplateInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Kind 6 | Xcode.IDEFoundation.TextSubstitutionFileTemplateKind 7 | Summary 8 | VIPER Architecture 9 | Description 10 | VIPER classes. 11 | SortOrder 12 | 1 13 | MainTemplateFiles 14 | ___FILEBASENAME___.swift 15 | AllowedTypes 16 | 17 | Item 0 18 | public.swift-source 19 | 20 | Platforms 21 | 22 | com.apple.platform.iphoneos 23 | 24 | Options 25 | 26 | 27 | Identifier 28 | productName 29 | Required 30 | true 31 | Name 32 | VIPER Name: 33 | Description 34 | The name of the VIPER to create 35 | Type 36 | text 37 | NotPersisted 38 | true 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /VIPER.xctemplate/___FILEBASENAME___BuilderRouter.swift: -------------------------------------------------------------------------------- 1 | //___FILEHEADER___ 2 | 3 | import Architecture 4 | 5 | protocol ___VARIABLE_productName___RouterInteractable: class { 6 | var viewRouter: ___VARIABLE_productName___ViewableRouting? { get set } 7 | var listener: ___VARIABLE_productName___InteractListener? { get set } 8 | } 9 | 10 | protocol ___VARIABLE_productName___ViewControllable: class { 11 | // 定义一些该页面需要的其他commponent的组件, 比如该页面的childViewController等. 12 | } 13 | 14 | final class ___VARIABLE_productName___ViewableRouter: MolueViewableRouting { 15 | 16 | unowned var interactor: ___VARIABLE_productName___RouterInteractable 17 | 18 | unowned var controller: ___VARIABLE_productName___ViewControllable 19 | 20 | @discardableResult 21 | required init(interactor: ___VARIABLE_productName___RouterInteractable, controller: ___VARIABLE_productName___ViewControllable) { 22 | self.controller = controller 23 | self.interactor = interactor 24 | interactor.viewRouter = self 25 | } 26 | } 27 | 28 | extension ___VARIABLE_productName___ViewableRouter: ___VARIABLE_productName___ViewableRouting { 29 | 30 | } 31 | 32 | protocol ___VARIABLE_productName___InteractListener: class { 33 | //用于定义其他的Component需要定义的协议方法 34 | } 35 | 36 | protocol ___VARIABLE_productName___ComponentBuildable: MolueComponentBuildable { 37 | //定义当前的Component的构造方法. 38 | func build(listener: ___VARIABLE_productName___InteractListener) -> UIViewController 39 | } 40 | 41 | class ___VARIABLE_productName___ComponentBuilder: MolueComponentBuilder, ___VARIABLE_productName___ComponentBuildable { 42 | func build(listener: ___VARIABLE_productName___InteractListener) -> UIViewController { 43 | let controller = ___VARIABLE_productName___ViewController() 44 | let interactor = ___VARIABLE_productName___PageInteractor(presenter: controller) 45 | ___VARIABLE_productName___ViewableRouter(interactor: interactor, controller: controller) 46 | interactor.listener = listener 47 | return controller 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /VIPER.xctemplate/___FILEBASENAME___PageInteractor.swift: -------------------------------------------------------------------------------- 1 | //___FILEHEADER___ 2 | 3 | import Architecture 4 | 5 | protocol ___VARIABLE_productName___ViewableRouting: class { 6 | // 定义一些页面跳转的方法, 比如Push, Presenter等. 7 | } 8 | 9 | protocol ___VARIABLE_productName___PagePresentable: MolueInteractorPresentable { 10 | var listener: ___VARIABLE_productName___PresentableListener? { get set } 11 | // 定义一些页面需要的方法, 比如刷新页面的显示内容等. 12 | } 13 | 14 | final class ___VARIABLE_productName___PageInteractor: MoluePresenterInteractable { 15 | 16 | unowned var presenter: ___VARIABLE_productName___PagePresentable 17 | 18 | var viewRouter: ___VARIABLE_productName___ViewableRouting? 19 | 20 | weak var listener: ___VARIABLE_productName___InteractListener? 21 | 22 | required init(presenter: ___VARIABLE_productName___PagePresentable) { 23 | self.presenter = presenter 24 | presenter.listener = self 25 | } 26 | } 27 | 28 | extension ___VARIABLE_productName___PageInteractor: ___VARIABLE_productName___RouterInteractable { 29 | 30 | } 31 | 32 | extension ___VARIABLE_productName___PageInteractor: ___VARIABLE_productName___PresentableListener { 33 | 34 | } 35 | -------------------------------------------------------------------------------- /VIPER.xctemplate/___FILEBASENAME___ViewController.swift: -------------------------------------------------------------------------------- 1 | //___FILEHEADER___ 2 | 3 | import UIKit 4 | 5 | protocol ___VARIABLE_productName___PresentableListener: class { 6 | // 定义一些当前页面需要的业务逻辑, 比如网络请求. 7 | } 8 | 9 | protocol ___VARIABLE_productName___ViewElementsLayout where Self: ___VARIABLE_productName___ViewController { 10 | // 页面初始化的布局方法, 如果当前页面需要其他的布局方法,需要在当前协议中添加方法即可. 11 | func doViewControllerLayout() 12 | // 可以在该协议下定义当前页面所需要的页面元素, 并且在使用该协议的类中使用懒加载模式去更新元素其他属性. 13 | } 14 | 15 | extension ___VARIABLE_productName___ViewElementsLayout { 16 | func doViewControllerLayout() { 17 | } 18 | } 19 | 20 | final class ___VARIABLE_productName___ViewController: UIViewController, ___VARIABLE_productName___ViewElementsLayout { 21 | //MARK: View Controller Properties 22 | var listener: ___VARIABLE_productName___PresentableListener? 23 | 24 | //MARK: View Controller Life Cycle 25 | override func viewDidLoad() { 26 | super.viewDidLoad() 27 | // Do any additional setup after loading the view. 28 | } 29 | } 30 | 31 | extension ___VARIABLE_productName___ViewController: ___VARIABLE_productName___PagePresentable { 32 | 33 | } 34 | 35 | extension ___VARIABLE_productName___ViewController: ___VARIABLE_productName___ViewControllable { 36 | 37 | } 38 | -------------------------------------------------------------------------------- /VIPER.xctemplate/___FILEBASENAME___ViewInteractor.swift: -------------------------------------------------------------------------------- 1 | //___FILEHEADER___ 2 | 3 | import Architecture 4 | 5 | protocol ___VARIABLE_productName___ViewableListenerable: class { 6 | //MARK: 定义一些当前页面需要的业务逻辑, 比如网络请求. 7 | } 8 | 9 | protocol ___VARIABLE_productName___ViewableInteractable: class { 10 | //MARK: 用于定义其他的Component需要定义的协议方法 11 | } 12 | 13 | protocol ___VARIABLE_productName___ViewablePresentable: MolueViewablePresentable { 14 | //MARK: 定义一些页面需要的方法, 比如刷新页面的显示内容等. 15 | var listener: ___VARIABLE_productName___ViewableListenerable? { get set } 16 | } 17 | 18 | final class ___VARIABLE_productName___ViewableInteractor: MolueViewableInteractable { 19 | 20 | unowned var presenter: ___VARIABLE_productName___ViewablePresentable 21 | 22 | weak var listener: ___VARIABLE_productName___ViewableInteractable? 23 | 24 | required init(presenter: ___VARIABLE_productName___ViewablePresentable) { 25 | self.presenter = presenter 26 | presenter.listener = self 27 | } 28 | } 29 | 30 | extension ___VARIABLE_productName___ViewableInteractor: ___VARIABLE_productName___ViewableListenerable { 31 | 32 | } 33 | 34 | final class ___VARIABLE_productName___ViewableBuilder { 35 | func build(listener: ___VARIABLE_productName___ViewableInteractable?) -> Void { 36 | 37 | let interactor = ___VARIABLE_productName___ViewableInteractor(presenter: presenter) 38 | interactor.listener = listener 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /VIPER.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /VIPER.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /VIPER.xcworkspace/xcuserdata/James.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MolueJames/VIPER/abbab8c8039e8cc20990342c59228036ac6c6415/VIPER.xcworkspace/xcuserdata/James.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /VIPER.xcworkspace/xcuserdata/James.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | --------------------------------------------------------------------------------