├── .DS_Store ├── MVVM-SwiftUI.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── dabasra.xcuserdatad │ │ └── UserInterfaceState.xcuserstate ├── xcshareddata │ └── xcschemes │ │ └── MVVM-SwiftUI.xcscheme └── xcuserdata │ └── dabasra.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ └── xcschememanagement.plist ├── MVVM-SwiftUI ├── AppDelegate.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ ├── tips.imageset │ │ ├── Contents.json │ │ └── TIPS@3x.png │ └── tipsters.imageset │ │ ├── Contents.json │ │ └── TIPSTERS@3x.png ├── Base.lproj │ └── LaunchScreen.storyboard ├── CustomViews │ └── LoadingView.swift ├── Info.plist ├── Model │ ├── AuthService.swift │ └── DataService.swift ├── Preview Content │ └── Preview Assets.xcassets │ │ └── Contents.json ├── SceneDelegate.swift ├── View │ ├── DetailView.swift │ ├── LoginView.swift │ ├── TabBarView.swift │ ├── TipsView.swift │ └── Tipsters.swift └── ViewModel │ ├── TipsViewModel.swift │ └── TipstersViewModel.swift ├── MVVM-SwiftUITests ├── Info.plist └── MVVM_SwiftUITests.swift └── README.md /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielbastidasr/swiftui-mvvm/c0c5e194a06cb3efe42733e057618cef5b7e5257/.DS_Store -------------------------------------------------------------------------------- /MVVM-SwiftUI.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 040CEAD1234774A400CA7CAB /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 040CEAD0234774A400CA7CAB /* AppDelegate.swift */; }; 11 | 040CEAD3234774A400CA7CAB /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 040CEAD2234774A400CA7CAB /* SceneDelegate.swift */; }; 12 | 040CEAD5234774A400CA7CAB /* LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 040CEAD4234774A400CA7CAB /* LoginView.swift */; }; 13 | 040CEAD7234774A600CA7CAB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 040CEAD6234774A600CA7CAB /* Assets.xcassets */; }; 14 | 040CEADA234774A600CA7CAB /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 040CEAD9234774A600CA7CAB /* Preview Assets.xcassets */; }; 15 | 040CEADD234774A600CA7CAB /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 040CEADB234774A600CA7CAB /* LaunchScreen.storyboard */; }; 16 | 040CEAE8234774A600CA7CAB /* MVVM_SwiftUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 040CEAE7234774A600CA7CAB /* MVVM_SwiftUITests.swift */; }; 17 | 04E88D762347AC2B00BBA37E /* Tipsters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04E88D752347AC2B00BBA37E /* Tipsters.swift */; }; 18 | 04E88D782347AC7B00BBA37E /* TipsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04E88D772347AC7B00BBA37E /* TipsView.swift */; }; 19 | 04E88D7A2347B97700BBA37E /* AuthService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04E88D792347B97700BBA37E /* AuthService.swift */; }; 20 | 04E88D7D2347C0D300BBA37E /* TabBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04E88D7C2347C0D300BBA37E /* TabBarView.swift */; }; 21 | 04E88D7F2348B62300BBA37E /* DataService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04E88D7E2348B62300BBA37E /* DataService.swift */; }; 22 | 04E88D832349FC9A00BBA37E /* LoadingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04E88D822349FC9A00BBA37E /* LoadingView.swift */; }; 23 | 04E88D85234A01F300BBA37E /* TipstersViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04E88D84234A01F300BBA37E /* TipstersViewModel.swift */; }; 24 | 04E88D87234A020200BBA37E /* TipsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04E88D86234A020200BBA37E /* TipsViewModel.swift */; }; 25 | 04E88D89234A3FCD00BBA37E /* DetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04E88D88234A3FCD00BBA37E /* DetailView.swift */; }; 26 | /* End PBXBuildFile section */ 27 | 28 | /* Begin PBXContainerItemProxy section */ 29 | 040CEAE4234774A600CA7CAB /* PBXContainerItemProxy */ = { 30 | isa = PBXContainerItemProxy; 31 | containerPortal = 040CEAC5234774A400CA7CAB /* Project object */; 32 | proxyType = 1; 33 | remoteGlobalIDString = 040CEACC234774A400CA7CAB; 34 | remoteInfo = "MVVM-SwiftUI"; 35 | }; 36 | /* End PBXContainerItemProxy section */ 37 | 38 | /* Begin PBXFileReference section */ 39 | 040CEACD234774A400CA7CAB /* MVVM-SwiftUI.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "MVVM-SwiftUI.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 40 | 040CEAD0234774A400CA7CAB /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 41 | 040CEAD2234774A400CA7CAB /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 42 | 040CEAD4234774A400CA7CAB /* LoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginView.swift; sourceTree = ""; }; 43 | 040CEAD6234774A600CA7CAB /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 44 | 040CEAD9234774A600CA7CAB /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 45 | 040CEADC234774A600CA7CAB /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 46 | 040CEADE234774A600CA7CAB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 47 | 040CEAE3234774A600CA7CAB /* MVVM-SwiftUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "MVVM-SwiftUITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 48 | 040CEAE7234774A600CA7CAB /* MVVM_SwiftUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVVM_SwiftUITests.swift; sourceTree = ""; }; 49 | 040CEAE9234774A600CA7CAB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50 | 04E88D752347AC2B00BBA37E /* Tipsters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tipsters.swift; sourceTree = ""; }; 51 | 04E88D772347AC7B00BBA37E /* TipsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TipsView.swift; sourceTree = ""; }; 52 | 04E88D792347B97700BBA37E /* AuthService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthService.swift; sourceTree = ""; }; 53 | 04E88D7C2347C0D300BBA37E /* TabBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarView.swift; sourceTree = ""; }; 54 | 04E88D7E2348B62300BBA37E /* DataService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataService.swift; sourceTree = ""; }; 55 | 04E88D822349FC9A00BBA37E /* LoadingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingView.swift; sourceTree = ""; }; 56 | 04E88D84234A01F300BBA37E /* TipstersViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TipstersViewModel.swift; sourceTree = ""; }; 57 | 04E88D86234A020200BBA37E /* TipsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TipsViewModel.swift; sourceTree = ""; }; 58 | 04E88D88234A3FCD00BBA37E /* DetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailView.swift; sourceTree = ""; }; 59 | /* End PBXFileReference section */ 60 | 61 | /* Begin PBXFrameworksBuildPhase section */ 62 | 040CEACA234774A400CA7CAB /* Frameworks */ = { 63 | isa = PBXFrameworksBuildPhase; 64 | buildActionMask = 2147483647; 65 | files = ( 66 | ); 67 | runOnlyForDeploymentPostprocessing = 0; 68 | }; 69 | 040CEAE0234774A600CA7CAB /* Frameworks */ = { 70 | isa = PBXFrameworksBuildPhase; 71 | buildActionMask = 2147483647; 72 | files = ( 73 | ); 74 | runOnlyForDeploymentPostprocessing = 0; 75 | }; 76 | /* End PBXFrameworksBuildPhase section */ 77 | 78 | /* Begin PBXGroup section */ 79 | 040CEAC4234774A400CA7CAB = { 80 | isa = PBXGroup; 81 | children = ( 82 | 040CEACF234774A400CA7CAB /* MVVM-SwiftUI */, 83 | 040CEAE6234774A600CA7CAB /* MVVM-SwiftUITests */, 84 | 040CEACE234774A400CA7CAB /* Products */, 85 | ); 86 | sourceTree = ""; 87 | }; 88 | 040CEACE234774A400CA7CAB /* Products */ = { 89 | isa = PBXGroup; 90 | children = ( 91 | 040CEACD234774A400CA7CAB /* MVVM-SwiftUI.app */, 92 | 040CEAE3234774A600CA7CAB /* MVVM-SwiftUITests.xctest */, 93 | ); 94 | name = Products; 95 | sourceTree = ""; 96 | }; 97 | 040CEACF234774A400CA7CAB /* MVVM-SwiftUI */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | 04E88D812349FC6D00BBA37E /* CustomViews */, 101 | 04E88D802349F41000BBA37E /* ViewModel */, 102 | 04E88D7B2347BA2B00BBA37E /* Model */, 103 | 04E88D7223479BDB00BBA37E /* View */, 104 | 040CEAD0234774A400CA7CAB /* AppDelegate.swift */, 105 | 040CEAD2234774A400CA7CAB /* SceneDelegate.swift */, 106 | 040CEAD6234774A600CA7CAB /* Assets.xcassets */, 107 | 040CEADB234774A600CA7CAB /* LaunchScreen.storyboard */, 108 | 040CEADE234774A600CA7CAB /* Info.plist */, 109 | 040CEAD8234774A600CA7CAB /* Preview Content */, 110 | ); 111 | path = "MVVM-SwiftUI"; 112 | sourceTree = ""; 113 | }; 114 | 040CEAD8234774A600CA7CAB /* Preview Content */ = { 115 | isa = PBXGroup; 116 | children = ( 117 | 040CEAD9234774A600CA7CAB /* Preview Assets.xcassets */, 118 | ); 119 | path = "Preview Content"; 120 | sourceTree = ""; 121 | }; 122 | 040CEAE6234774A600CA7CAB /* MVVM-SwiftUITests */ = { 123 | isa = PBXGroup; 124 | children = ( 125 | 040CEAE7234774A600CA7CAB /* MVVM_SwiftUITests.swift */, 126 | 040CEAE9234774A600CA7CAB /* Info.plist */, 127 | ); 128 | path = "MVVM-SwiftUITests"; 129 | sourceTree = ""; 130 | }; 131 | 04E88D7223479BDB00BBA37E /* View */ = { 132 | isa = PBXGroup; 133 | children = ( 134 | 040CEAD4234774A400CA7CAB /* LoginView.swift */, 135 | 04E88D7C2347C0D300BBA37E /* TabBarView.swift */, 136 | 04E88D752347AC2B00BBA37E /* Tipsters.swift */, 137 | 04E88D772347AC7B00BBA37E /* TipsView.swift */, 138 | 04E88D88234A3FCD00BBA37E /* DetailView.swift */, 139 | ); 140 | path = View; 141 | sourceTree = ""; 142 | }; 143 | 04E88D7B2347BA2B00BBA37E /* Model */ = { 144 | isa = PBXGroup; 145 | children = ( 146 | 04E88D792347B97700BBA37E /* AuthService.swift */, 147 | 04E88D7E2348B62300BBA37E /* DataService.swift */, 148 | ); 149 | path = Model; 150 | sourceTree = ""; 151 | }; 152 | 04E88D802349F41000BBA37E /* ViewModel */ = { 153 | isa = PBXGroup; 154 | children = ( 155 | 04E88D84234A01F300BBA37E /* TipstersViewModel.swift */, 156 | 04E88D86234A020200BBA37E /* TipsViewModel.swift */, 157 | ); 158 | path = ViewModel; 159 | sourceTree = ""; 160 | }; 161 | 04E88D812349FC6D00BBA37E /* CustomViews */ = { 162 | isa = PBXGroup; 163 | children = ( 164 | 04E88D822349FC9A00BBA37E /* LoadingView.swift */, 165 | ); 166 | path = CustomViews; 167 | sourceTree = ""; 168 | }; 169 | /* End PBXGroup section */ 170 | 171 | /* Begin PBXNativeTarget section */ 172 | 040CEACC234774A400CA7CAB /* MVVM-SwiftUI */ = { 173 | isa = PBXNativeTarget; 174 | buildConfigurationList = 040CEAEC234774A600CA7CAB /* Build configuration list for PBXNativeTarget "MVVM-SwiftUI" */; 175 | buildPhases = ( 176 | 040CEAC9234774A400CA7CAB /* Sources */, 177 | 040CEACA234774A400CA7CAB /* Frameworks */, 178 | 040CEACB234774A400CA7CAB /* Resources */, 179 | ); 180 | buildRules = ( 181 | ); 182 | dependencies = ( 183 | ); 184 | name = "MVVM-SwiftUI"; 185 | productName = "MVVM-SwiftUI"; 186 | productReference = 040CEACD234774A400CA7CAB /* MVVM-SwiftUI.app */; 187 | productType = "com.apple.product-type.application"; 188 | }; 189 | 040CEAE2234774A600CA7CAB /* MVVM-SwiftUITests */ = { 190 | isa = PBXNativeTarget; 191 | buildConfigurationList = 040CEAEF234774A600CA7CAB /* Build configuration list for PBXNativeTarget "MVVM-SwiftUITests" */; 192 | buildPhases = ( 193 | 040CEADF234774A600CA7CAB /* Sources */, 194 | 040CEAE0234774A600CA7CAB /* Frameworks */, 195 | 040CEAE1234774A600CA7CAB /* Resources */, 196 | ); 197 | buildRules = ( 198 | ); 199 | dependencies = ( 200 | 040CEAE5234774A600CA7CAB /* PBXTargetDependency */, 201 | ); 202 | name = "MVVM-SwiftUITests"; 203 | productName = "MVVM-SwiftUITests"; 204 | productReference = 040CEAE3234774A600CA7CAB /* MVVM-SwiftUITests.xctest */; 205 | productType = "com.apple.product-type.bundle.unit-test"; 206 | }; 207 | /* End PBXNativeTarget section */ 208 | 209 | /* Begin PBXProject section */ 210 | 040CEAC5234774A400CA7CAB /* Project object */ = { 211 | isa = PBXProject; 212 | attributes = { 213 | LastSwiftUpdateCheck = 1100; 214 | LastUpgradeCheck = 1100; 215 | ORGANIZATIONNAME = "Daniel Bastidas"; 216 | TargetAttributes = { 217 | 040CEACC234774A400CA7CAB = { 218 | CreatedOnToolsVersion = 11.0; 219 | }; 220 | 040CEAE2234774A600CA7CAB = { 221 | CreatedOnToolsVersion = 11.0; 222 | TestTargetID = 040CEACC234774A400CA7CAB; 223 | }; 224 | }; 225 | }; 226 | buildConfigurationList = 040CEAC8234774A400CA7CAB /* Build configuration list for PBXProject "MVVM-SwiftUI" */; 227 | compatibilityVersion = "Xcode 9.3"; 228 | developmentRegion = en; 229 | hasScannedForEncodings = 0; 230 | knownRegions = ( 231 | en, 232 | Base, 233 | ); 234 | mainGroup = 040CEAC4234774A400CA7CAB; 235 | productRefGroup = 040CEACE234774A400CA7CAB /* Products */; 236 | projectDirPath = ""; 237 | projectRoot = ""; 238 | targets = ( 239 | 040CEACC234774A400CA7CAB /* MVVM-SwiftUI */, 240 | 040CEAE2234774A600CA7CAB /* MVVM-SwiftUITests */, 241 | ); 242 | }; 243 | /* End PBXProject section */ 244 | 245 | /* Begin PBXResourcesBuildPhase section */ 246 | 040CEACB234774A400CA7CAB /* Resources */ = { 247 | isa = PBXResourcesBuildPhase; 248 | buildActionMask = 2147483647; 249 | files = ( 250 | 040CEADD234774A600CA7CAB /* LaunchScreen.storyboard in Resources */, 251 | 040CEADA234774A600CA7CAB /* Preview Assets.xcassets in Resources */, 252 | 040CEAD7234774A600CA7CAB /* Assets.xcassets in Resources */, 253 | ); 254 | runOnlyForDeploymentPostprocessing = 0; 255 | }; 256 | 040CEAE1234774A600CA7CAB /* Resources */ = { 257 | isa = PBXResourcesBuildPhase; 258 | buildActionMask = 2147483647; 259 | files = ( 260 | ); 261 | runOnlyForDeploymentPostprocessing = 0; 262 | }; 263 | /* End PBXResourcesBuildPhase section */ 264 | 265 | /* Begin PBXSourcesBuildPhase section */ 266 | 040CEAC9234774A400CA7CAB /* Sources */ = { 267 | isa = PBXSourcesBuildPhase; 268 | buildActionMask = 2147483647; 269 | files = ( 270 | 04E88D87234A020200BBA37E /* TipsViewModel.swift in Sources */, 271 | 04E88D782347AC7B00BBA37E /* TipsView.swift in Sources */, 272 | 04E88D89234A3FCD00BBA37E /* DetailView.swift in Sources */, 273 | 040CEAD1234774A400CA7CAB /* AppDelegate.swift in Sources */, 274 | 04E88D85234A01F300BBA37E /* TipstersViewModel.swift in Sources */, 275 | 04E88D7D2347C0D300BBA37E /* TabBarView.swift in Sources */, 276 | 04E88D762347AC2B00BBA37E /* Tipsters.swift in Sources */, 277 | 04E88D7A2347B97700BBA37E /* AuthService.swift in Sources */, 278 | 040CEAD3234774A400CA7CAB /* SceneDelegate.swift in Sources */, 279 | 040CEAD5234774A400CA7CAB /* LoginView.swift in Sources */, 280 | 04E88D7F2348B62300BBA37E /* DataService.swift in Sources */, 281 | 04E88D832349FC9A00BBA37E /* LoadingView.swift in Sources */, 282 | ); 283 | runOnlyForDeploymentPostprocessing = 0; 284 | }; 285 | 040CEADF234774A600CA7CAB /* Sources */ = { 286 | isa = PBXSourcesBuildPhase; 287 | buildActionMask = 2147483647; 288 | files = ( 289 | 040CEAE8234774A600CA7CAB /* MVVM_SwiftUITests.swift in Sources */, 290 | ); 291 | runOnlyForDeploymentPostprocessing = 0; 292 | }; 293 | /* End PBXSourcesBuildPhase section */ 294 | 295 | /* Begin PBXTargetDependency section */ 296 | 040CEAE5234774A600CA7CAB /* PBXTargetDependency */ = { 297 | isa = PBXTargetDependency; 298 | target = 040CEACC234774A400CA7CAB /* MVVM-SwiftUI */; 299 | targetProxy = 040CEAE4234774A600CA7CAB /* PBXContainerItemProxy */; 300 | }; 301 | /* End PBXTargetDependency section */ 302 | 303 | /* Begin PBXVariantGroup section */ 304 | 040CEADB234774A600CA7CAB /* LaunchScreen.storyboard */ = { 305 | isa = PBXVariantGroup; 306 | children = ( 307 | 040CEADC234774A600CA7CAB /* Base */, 308 | ); 309 | name = LaunchScreen.storyboard; 310 | sourceTree = ""; 311 | }; 312 | /* End PBXVariantGroup section */ 313 | 314 | /* Begin XCBuildConfiguration section */ 315 | 040CEAEA234774A600CA7CAB /* Debug */ = { 316 | isa = XCBuildConfiguration; 317 | buildSettings = { 318 | ALWAYS_SEARCH_USER_PATHS = NO; 319 | CLANG_ANALYZER_NONNULL = YES; 320 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 321 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 322 | CLANG_CXX_LIBRARY = "libc++"; 323 | CLANG_ENABLE_MODULES = YES; 324 | CLANG_ENABLE_OBJC_ARC = YES; 325 | CLANG_ENABLE_OBJC_WEAK = YES; 326 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 327 | CLANG_WARN_BOOL_CONVERSION = YES; 328 | CLANG_WARN_COMMA = YES; 329 | CLANG_WARN_CONSTANT_CONVERSION = YES; 330 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 331 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 332 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 333 | CLANG_WARN_EMPTY_BODY = YES; 334 | CLANG_WARN_ENUM_CONVERSION = YES; 335 | CLANG_WARN_INFINITE_RECURSION = YES; 336 | CLANG_WARN_INT_CONVERSION = YES; 337 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 338 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 339 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 340 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 341 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 342 | CLANG_WARN_STRICT_PROTOTYPES = YES; 343 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 344 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 345 | CLANG_WARN_UNREACHABLE_CODE = YES; 346 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 347 | COPY_PHASE_STRIP = NO; 348 | DEBUG_INFORMATION_FORMAT = dwarf; 349 | ENABLE_STRICT_OBJC_MSGSEND = YES; 350 | ENABLE_TESTABILITY = YES; 351 | GCC_C_LANGUAGE_STANDARD = gnu11; 352 | GCC_DYNAMIC_NO_PIC = NO; 353 | GCC_NO_COMMON_BLOCKS = YES; 354 | GCC_OPTIMIZATION_LEVEL = 0; 355 | GCC_PREPROCESSOR_DEFINITIONS = ( 356 | "DEBUG=1", 357 | "$(inherited)", 358 | ); 359 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 360 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 361 | GCC_WARN_UNDECLARED_SELECTOR = YES; 362 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 363 | GCC_WARN_UNUSED_FUNCTION = YES; 364 | GCC_WARN_UNUSED_VARIABLE = YES; 365 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 366 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 367 | MTL_FAST_MATH = YES; 368 | ONLY_ACTIVE_ARCH = YES; 369 | SDKROOT = iphoneos; 370 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 371 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 372 | }; 373 | name = Debug; 374 | }; 375 | 040CEAEB234774A600CA7CAB /* Release */ = { 376 | isa = XCBuildConfiguration; 377 | buildSettings = { 378 | ALWAYS_SEARCH_USER_PATHS = NO; 379 | CLANG_ANALYZER_NONNULL = YES; 380 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 381 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 382 | CLANG_CXX_LIBRARY = "libc++"; 383 | CLANG_ENABLE_MODULES = YES; 384 | CLANG_ENABLE_OBJC_ARC = YES; 385 | CLANG_ENABLE_OBJC_WEAK = YES; 386 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 387 | CLANG_WARN_BOOL_CONVERSION = YES; 388 | CLANG_WARN_COMMA = YES; 389 | CLANG_WARN_CONSTANT_CONVERSION = YES; 390 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 391 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 392 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 393 | CLANG_WARN_EMPTY_BODY = YES; 394 | CLANG_WARN_ENUM_CONVERSION = YES; 395 | CLANG_WARN_INFINITE_RECURSION = YES; 396 | CLANG_WARN_INT_CONVERSION = YES; 397 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 398 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 399 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 400 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 401 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 402 | CLANG_WARN_STRICT_PROTOTYPES = YES; 403 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 404 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 405 | CLANG_WARN_UNREACHABLE_CODE = YES; 406 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 407 | COPY_PHASE_STRIP = NO; 408 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 409 | ENABLE_NS_ASSERTIONS = NO; 410 | ENABLE_STRICT_OBJC_MSGSEND = YES; 411 | GCC_C_LANGUAGE_STANDARD = gnu11; 412 | GCC_NO_COMMON_BLOCKS = YES; 413 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 414 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 415 | GCC_WARN_UNDECLARED_SELECTOR = YES; 416 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 417 | GCC_WARN_UNUSED_FUNCTION = YES; 418 | GCC_WARN_UNUSED_VARIABLE = YES; 419 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 420 | MTL_ENABLE_DEBUG_INFO = NO; 421 | MTL_FAST_MATH = YES; 422 | SDKROOT = iphoneos; 423 | SWIFT_COMPILATION_MODE = wholemodule; 424 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 425 | VALIDATE_PRODUCT = YES; 426 | }; 427 | name = Release; 428 | }; 429 | 040CEAED234774A600CA7CAB /* Debug */ = { 430 | isa = XCBuildConfiguration; 431 | buildSettings = { 432 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 433 | CODE_SIGN_STYLE = Automatic; 434 | DEVELOPMENT_ASSET_PATHS = "\"MVVM-SwiftUI/Preview Content\""; 435 | DEVELOPMENT_TEAM = AYT5R2RQFH; 436 | ENABLE_PREVIEWS = YES; 437 | INFOPLIST_FILE = "MVVM-SwiftUI/Info.plist"; 438 | LD_RUNPATH_SEARCH_PATHS = ( 439 | "$(inherited)", 440 | "@executable_path/Frameworks", 441 | ); 442 | PRODUCT_BUNDLE_IDENTIFIER = "dabasra.MVVM-SwiftUI"; 443 | PRODUCT_NAME = "$(TARGET_NAME)"; 444 | SWIFT_VERSION = 5.0; 445 | TARGETED_DEVICE_FAMILY = "1,2"; 446 | }; 447 | name = Debug; 448 | }; 449 | 040CEAEE234774A600CA7CAB /* Release */ = { 450 | isa = XCBuildConfiguration; 451 | buildSettings = { 452 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 453 | CODE_SIGN_STYLE = Automatic; 454 | DEVELOPMENT_ASSET_PATHS = "\"MVVM-SwiftUI/Preview Content\""; 455 | DEVELOPMENT_TEAM = AYT5R2RQFH; 456 | ENABLE_PREVIEWS = YES; 457 | INFOPLIST_FILE = "MVVM-SwiftUI/Info.plist"; 458 | LD_RUNPATH_SEARCH_PATHS = ( 459 | "$(inherited)", 460 | "@executable_path/Frameworks", 461 | ); 462 | PRODUCT_BUNDLE_IDENTIFIER = "dabasra.MVVM-SwiftUI"; 463 | PRODUCT_NAME = "$(TARGET_NAME)"; 464 | SWIFT_VERSION = 5.0; 465 | TARGETED_DEVICE_FAMILY = "1,2"; 466 | }; 467 | name = Release; 468 | }; 469 | 040CEAF0234774A600CA7CAB /* Debug */ = { 470 | isa = XCBuildConfiguration; 471 | buildSettings = { 472 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 473 | BUNDLE_LOADER = "$(TEST_HOST)"; 474 | CODE_SIGN_STYLE = Automatic; 475 | DEVELOPMENT_TEAM = AYT5R2RQFH; 476 | INFOPLIST_FILE = "MVVM-SwiftUITests/Info.plist"; 477 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 478 | LD_RUNPATH_SEARCH_PATHS = ( 479 | "$(inherited)", 480 | "@executable_path/Frameworks", 481 | "@loader_path/Frameworks", 482 | ); 483 | PRODUCT_BUNDLE_IDENTIFIER = "dabasra.MVVM-SwiftUITests"; 484 | PRODUCT_NAME = "$(TARGET_NAME)"; 485 | SWIFT_VERSION = 5.0; 486 | TARGETED_DEVICE_FAMILY = "1,2"; 487 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/MVVM-SwiftUI.app/MVVM-SwiftUI"; 488 | }; 489 | name = Debug; 490 | }; 491 | 040CEAF1234774A600CA7CAB /* Release */ = { 492 | isa = XCBuildConfiguration; 493 | buildSettings = { 494 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 495 | BUNDLE_LOADER = "$(TEST_HOST)"; 496 | CODE_SIGN_STYLE = Automatic; 497 | DEVELOPMENT_TEAM = AYT5R2RQFH; 498 | INFOPLIST_FILE = "MVVM-SwiftUITests/Info.plist"; 499 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 500 | LD_RUNPATH_SEARCH_PATHS = ( 501 | "$(inherited)", 502 | "@executable_path/Frameworks", 503 | "@loader_path/Frameworks", 504 | ); 505 | PRODUCT_BUNDLE_IDENTIFIER = "dabasra.MVVM-SwiftUITests"; 506 | PRODUCT_NAME = "$(TARGET_NAME)"; 507 | SWIFT_VERSION = 5.0; 508 | TARGETED_DEVICE_FAMILY = "1,2"; 509 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/MVVM-SwiftUI.app/MVVM-SwiftUI"; 510 | }; 511 | name = Release; 512 | }; 513 | /* End XCBuildConfiguration section */ 514 | 515 | /* Begin XCConfigurationList section */ 516 | 040CEAC8234774A400CA7CAB /* Build configuration list for PBXProject "MVVM-SwiftUI" */ = { 517 | isa = XCConfigurationList; 518 | buildConfigurations = ( 519 | 040CEAEA234774A600CA7CAB /* Debug */, 520 | 040CEAEB234774A600CA7CAB /* Release */, 521 | ); 522 | defaultConfigurationIsVisible = 0; 523 | defaultConfigurationName = Release; 524 | }; 525 | 040CEAEC234774A600CA7CAB /* Build configuration list for PBXNativeTarget "MVVM-SwiftUI" */ = { 526 | isa = XCConfigurationList; 527 | buildConfigurations = ( 528 | 040CEAED234774A600CA7CAB /* Debug */, 529 | 040CEAEE234774A600CA7CAB /* Release */, 530 | ); 531 | defaultConfigurationIsVisible = 0; 532 | defaultConfigurationName = Release; 533 | }; 534 | 040CEAEF234774A600CA7CAB /* Build configuration list for PBXNativeTarget "MVVM-SwiftUITests" */ = { 535 | isa = XCConfigurationList; 536 | buildConfigurations = ( 537 | 040CEAF0234774A600CA7CAB /* Debug */, 538 | 040CEAF1234774A600CA7CAB /* Release */, 539 | ); 540 | defaultConfigurationIsVisible = 0; 541 | defaultConfigurationName = Release; 542 | }; 543 | /* End XCConfigurationList section */ 544 | }; 545 | rootObject = 040CEAC5234774A400CA7CAB /* Project object */; 546 | } 547 | -------------------------------------------------------------------------------- /MVVM-SwiftUI.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /MVVM-SwiftUI.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /MVVM-SwiftUI.xcodeproj/project.xcworkspace/xcuserdata/dabasra.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielbastidasr/swiftui-mvvm/c0c5e194a06cb3efe42733e057618cef5b7e5257/MVVM-SwiftUI.xcodeproj/project.xcworkspace/xcuserdata/dabasra.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /MVVM-SwiftUI.xcodeproj/xcshareddata/xcschemes/MVVM-SwiftUI.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 53 | 55 | 61 | 62 | 63 | 64 | 70 | 72 | 78 | 79 | 80 | 81 | 83 | 84 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /MVVM-SwiftUI.xcodeproj/xcuserdata/dabasra.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /MVVM-SwiftUI.xcodeproj/xcuserdata/dabasra.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | MVVM-SwiftUI.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 040CEACC234774A400CA7CAB 16 | 17 | primary 18 | 19 | 20 | 040CEAE2234774A600CA7CAB 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /MVVM-SwiftUI/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // MVVM-SwiftUI 4 | // 5 | // Copyright © 2019 Daniel Bastidas. All rights reserved. 6 | // 7 | 8 | import UIKit 9 | 10 | @UIApplicationMain 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 14 | // Override point for customization after application launch. 15 | return true 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /MVVM-SwiftUI/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 | } -------------------------------------------------------------------------------- /MVVM-SwiftUI/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /MVVM-SwiftUI/Assets.xcassets/tips.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "filename" : "TIPS@3x.png", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /MVVM-SwiftUI/Assets.xcassets/tips.imageset/TIPS@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielbastidasr/swiftui-mvvm/c0c5e194a06cb3efe42733e057618cef5b7e5257/MVVM-SwiftUI/Assets.xcassets/tips.imageset/TIPS@3x.png -------------------------------------------------------------------------------- /MVVM-SwiftUI/Assets.xcassets/tipsters.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "filename" : "TIPSTERS@3x.png", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /MVVM-SwiftUI/Assets.xcassets/tipsters.imageset/TIPSTERS@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielbastidasr/swiftui-mvvm/c0c5e194a06cb3efe42733e057618cef5b7e5257/MVVM-SwiftUI/Assets.xcassets/tipsters.imageset/TIPSTERS@3x.png -------------------------------------------------------------------------------- /MVVM-SwiftUI/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 | -------------------------------------------------------------------------------- /MVVM-SwiftUI/CustomViews/LoadingView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoadingView.swift 3 | // MVVM-SwiftUI 4 | // 5 | // Copyright © 2019 Daniel Bastidas. All rights reserved. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct LoadingView: UIViewRepresentable { 11 | 12 | @Binding var isAnimating: Bool 13 | let style: UIActivityIndicatorView.Style 14 | 15 | func makeUIView(context: UIViewRepresentableContext) -> UIActivityIndicatorView { 16 | return UIActivityIndicatorView(style: style) 17 | } 18 | 19 | func updateUIView(_ uiView: UIActivityIndicatorView, context: UIViewRepresentableContext) { 20 | isAnimating ? uiView.startAnimating() : uiView.stopAnimating() 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /MVVM-SwiftUI/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSceneManifest 24 | 25 | UIApplicationSupportsMultipleScenes 26 | 27 | UISceneConfigurations 28 | 29 | UIWindowSceneSessionRoleApplication 30 | 31 | 32 | UISceneConfigurationName 33 | Default Configuration 34 | UISceneDelegateClassName 35 | $(PRODUCT_MODULE_NAME).SceneDelegate 36 | 37 | 38 | 39 | 40 | UILaunchStoryboardName 41 | LaunchScreen 42 | UIRequiredDeviceCapabilities 43 | 44 | armv7 45 | 46 | UISupportedInterfaceOrientations 47 | 48 | UIInterfaceOrientationPortrait 49 | UIInterfaceOrientationLandscapeLeft 50 | UIInterfaceOrientationLandscapeRight 51 | 52 | UISupportedInterfaceOrientations~ipad 53 | 54 | UIInterfaceOrientationPortrait 55 | UIInterfaceOrientationPortraitUpsideDown 56 | UIInterfaceOrientationLandscapeLeft 57 | UIInterfaceOrientationLandscapeRight 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /MVVM-SwiftUI/Model/AuthService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DataService.swift 3 | // MVVM-SwiftUI 4 | // 5 | // Copyright © 2019 Daniel Bastidas. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | 10 | class AuthService : ObservableObject { 11 | 12 | @Published var signedIn:Bool 13 | private var uid:String? = nil 14 | 15 | init(signedIn:Bool) { 16 | self.signedIn = signedIn 17 | } 18 | 19 | // Make sure the API calls once they are finished modify the values on the Main Thread 20 | func signIn() { 21 | self.signedIn = true 22 | self.uid = "USERIDSET" 23 | } 24 | 25 | func getUid() -> String? { 26 | if(self.signedIn){ 27 | return self.uid 28 | } 29 | else{ 30 | return nil 31 | } 32 | } 33 | 34 | func signOut(){ 35 | self.signedIn = false 36 | self.uid = nil 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /MVVM-SwiftUI/Model/DataService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DataService.swift 3 | // MVVM-SwiftUI 4 | // 5 | // Copyright © 2019 Daniel Bastidas. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | class DataService { 12 | 13 | // Get from API/Database 14 | func getTipList(uid:String?) -> [String] { 15 | if(uid != nil){ 16 | return ["Hello World","Code Clean","Test it!"] 17 | } 18 | else{ 19 | return [] 20 | } 21 | } 22 | 23 | // Get from API/Database 24 | func getTipstersList(uid:String?) -> [String]{ 25 | if(uid != nil){ 26 | return ["Matt Vanderman","Daniel Bastidas","Craig Saxby"] 27 | } 28 | else{ 29 | return [] 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /MVVM-SwiftUI/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /MVVM-SwiftUI/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // MVVM-SwiftUI 4 | // 5 | // Copyright © 2019 Daniel Bastidas. All rights reserved. 6 | // 7 | 8 | import UIKit 9 | import SwiftUI 10 | 11 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 12 | 13 | var window: UIWindow? 14 | 15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 16 | 17 | let authService = AuthService(signedIn: false) 18 | let contentView = LoginView().environmentObject(authService) 19 | 20 | // Use a UIHostingController as window root view controller. 21 | if let windowScene = scene as? UIWindowScene { 22 | let window = UIWindow(windowScene: windowScene) 23 | window.rootViewController = UIHostingController(rootView: contentView) 24 | self.window = window 25 | window.makeKeyAndVisible() 26 | } 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /MVVM-SwiftUI/View/DetailView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Detail.swift 3 | // MVVM-SwiftUI 4 | // 5 | // Copyright © 2019 Daniel Bastidas. All rights reserved. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct DetailView: View { 11 | 12 | let tipster:String 13 | 14 | var body: some View { 15 | Text("Tipster Name: \(tipster)") 16 | } 17 | } 18 | 19 | struct Detail_Previews: PreviewProvider { 20 | static var previews: some View { 21 | DetailView(tipster: "Tipster Name") 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /MVVM-SwiftUI/View/LoginView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // MVVM-SwiftUI 4 | // 5 | // Copyright © 2019 Daniel Bastidas. All rights reserved. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct LoginView: View { 11 | 12 | @EnvironmentObject var authService:AuthService 13 | 14 | var body: some View{ 15 | ZStack{ 16 | if(!authService.signedIn){ 17 | Button(action: { 18 | self.authService.signIn() 19 | }) { 20 | Text("Login") 21 | .padding([.leading,.trailing],30) 22 | .padding([.top,.bottom],15) 23 | } 24 | .foregroundColor(.white) 25 | .background(Color(.blue)) 26 | .cornerRadius(3) 27 | } 28 | else{ 29 | TabBarView() 30 | } 31 | } 32 | } 33 | } 34 | 35 | // MARK:- Preview 36 | struct ContentView_Previews: PreviewProvider { 37 | static var previews: some View { 38 | LoginView() 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /MVVM-SwiftUI/View/TabBarView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TabBarView.swift 3 | // MVVM-SwiftUI 4 | // 5 | // Copyright © 2019 Daniel Bastidas. All rights reserved. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct TabBarView: View { 11 | 12 | @EnvironmentObject var authService:AuthService 13 | var dataService:DataService 14 | var tipstersViewModel:TipstersViewModel 15 | var tipsViewModel:TipsViewModel 16 | 17 | init() { 18 | self.dataService = DataService() 19 | self.tipstersViewModel = TipstersViewModel(dataService: dataService) 20 | self.tipsViewModel = TipsViewModel(dataService: dataService) 21 | } 22 | 23 | var body: some View { 24 | 25 | TabView { 26 | NavigationView{ 27 | TipstersView(tipstersViewModel: tipstersViewModel) 28 | } 29 | .tabItem { 30 | VStack{ 31 | Image("tipsters") 32 | Text("Tipsters") 33 | } 34 | } 35 | NavigationView{ 36 | TipsView(tipsViewModel: tipsViewModel) 37 | } 38 | .tabItem { 39 | VStack{ 40 | Image("tips") 41 | Text("Tips") 42 | } 43 | } 44 | } 45 | } 46 | } 47 | 48 | struct LogOutButton: View { 49 | var logOutFunction:() -> Void 50 | 51 | var body: some View { 52 | Button(action: logOutFunction , label: { 53 | Text("Log Out") 54 | }) 55 | } 56 | } 57 | // MARK:- Preview 58 | struct TabBarView_Previews: PreviewProvider { 59 | static var previews: some View { 60 | TabBarView() 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /MVVM-SwiftUI/View/TipsView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TipsView.swift 3 | // MVVM-SwiftUI 4 | // 5 | // Copyright © 2019 Daniel Bastidas. All rights reserved. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct TipsView: View { 11 | 12 | @ObservedObject var tipsViewModel:TipsViewModel 13 | @EnvironmentObject var authService:AuthService 14 | 15 | var body: some View { 16 | ZStack{ 17 | List{ 18 | ForEach(tipsViewModel.tips, id: \.self) { tip in 19 | Text("\(tip)") 20 | .foregroundColor(.black) 21 | .padding(10) 22 | } 23 | } 24 | if tipsViewModel.tips.count == 0 { 25 | LoadingView(isAnimating: .constant(true), style: .large) 26 | } 27 | } 28 | .navigationBarTitle(Text("Tips")) 29 | .onAppear { 30 | self.tipsViewModel.getTipList(uid: self.authService.getUid()) 31 | } 32 | .navigationBarItems(trailing: LogOutButton(logOutFunction: authService.signOut)) 33 | } 34 | } 35 | 36 | 37 | // MARK:- Preview 38 | struct Tips_Previews: PreviewProvider { 39 | static var previews: some View { 40 | TipsView(tipsViewModel: TipsViewModel(dataService: DataService())) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /MVVM-SwiftUI/View/Tipsters.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Tipsters.swift 3 | // MVVM-SwiftUI 4 | // 5 | // Copyright © 2019 Daniel Bastidas. All rights reserved. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct TipstersView: View { 11 | 12 | @ObservedObject var tipstersViewModel:TipstersViewModel 13 | @EnvironmentObject var authService:AuthService 14 | 15 | var body: some View { 16 | ZStack{ 17 | List{ 18 | ForEach(tipstersViewModel.tipsters, id: \.self) { tip in 19 | NavigationLink(destination: DetailView(tipster: tip)) { 20 | Text("\(tip)") 21 | .padding(10) 22 | } 23 | } 24 | } 25 | if tipstersViewModel.tipsters.count == 0 { 26 | LoadingView(isAnimating: .constant(true), style: .large) 27 | } 28 | } 29 | .onAppear { 30 | UITableView.appearance().separatorColor = .clear 31 | self.tipstersViewModel.getTipstersList(uid: self.authService.getUid()) 32 | } 33 | .navigationBarTitle(Text("Tipsters")) 34 | .navigationBarItems(trailing: LogOutButton(logOutFunction: authService.signOut)) 35 | } 36 | } 37 | 38 | // MARK:- Preview 39 | struct Tipsters_Previews: PreviewProvider { 40 | static var previews: some View { 41 | TipstersView(tipstersViewModel: TipstersViewModel(dataService: DataService())) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /MVVM-SwiftUI/ViewModel/TipsViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TipsViewModel.swift 3 | // MVVM-SwiftUI 4 | // 5 | // Copyright © 2019 Daniel Bastidas. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | protocol TipsViewModelProtocol { 12 | func getTipList(uid:String?) 13 | } 14 | 15 | class TipsViewModel: TipsViewModelProtocol, ObservableObject{ 16 | 17 | var dataService:DataService 18 | @Published var tips: [String] = [] 19 | 20 | init(dataService:DataService) { 21 | self.dataService = dataService 22 | } 23 | 24 | func getTipList(uid: String?) { 25 | DispatchQueue.main.asyncAfter(deadline: .now() + 1.0, execute: { 26 | [weak self] in 27 | self?.tips = self?.dataService.getTipList(uid: uid) ?? [] 28 | }) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /MVVM-SwiftUI/ViewModel/TipstersViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TipstersViewModel.swift 3 | // MVVM-SwiftUI 4 | // 5 | // Copyright © 2019 Daniel Bastidas. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | protocol TipstersViewModelProtocol { 12 | func getTipstersList(uid:String?) 13 | } 14 | 15 | class TipstersViewModel: TipstersViewModelProtocol, ObservableObject{ 16 | 17 | var dataService:DataService 18 | @Published var tipsters: [String] = [] 19 | 20 | init(dataService:DataService) { 21 | self.dataService = dataService 22 | } 23 | 24 | func getTipstersList(uid: String?) { 25 | DispatchQueue.main.asyncAfter(deadline: .now() + 1.0, execute: { 26 | [weak self] in 27 | self?.tipsters = self?.dataService.getTipstersList(uid: uid) ?? [] 28 | }) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /MVVM-SwiftUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /MVVM-SwiftUITests/MVVM_SwiftUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MVVM_SwiftUITests.swift 3 | // MVVM-SwiftUITests 4 | // 5 | // Created by Daniel Bastidas Ramirez on 04/10/2019. 6 | // Copyright © 2019 Daniel Bastidas. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import MVVM_SwiftUI 11 | 12 | class MVVM_SwiftUITests: 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SwiftUI Review 2 | 3 | The App use state management for Login. 4 | 5 | - Once logged in, it should take you to a TabBarView. 6 | - The Tabbar should hold 2 views, and one of them will navigate back and forth with the detailed information of what was clicked. 7 | - The information in both is a list of items. 8 | 9 | ## References 10 | 11 | ### Videos 12 | * **[Introducing SwiftUI: Building Your First App](https://developer.apple.com/videos/play/wwdc2019/204/)** 13 | * **[SwiftUI Essentials](https://developer.apple.com/videos/play/wwdc2019/216)** 14 | * **[Data Flow Through SwiftUI](https://developer.apple.com/videos/play/wwdc2019/226)** 15 | * **[Building Custom Views with SwiftUI](https://developer.apple.com/videos/play/wwdc2019/237)** 16 | * **[Integrating SwiftUI](https://developer.apple.com/videos/play/wwdc2019/231)** 17 | * **[Accessibility in SwiftUI](https://developer.apple.com/videos/play/wwdc2019/238)** 18 | * **[SwiftUI On All Devices](https://developer.apple.com/videos/play/wwdc2019/240)** 19 | * **[SwiftUI on watchOS](https://developer.apple.com/videos/play/wwdc2019/219)** 20 | * **[Mastering Xcode Previews](https://developer.apple.com/videos/play/wwdc2019/233)** 21 | 22 | ### Tutorials 23 | * **[Creating and Combining Views](https://developer.apple.com/tutorials/swiftui/creating-and-combining-views)** 24 | * **[Working with UI Controls](https://developer.apple.com/tutorials/swiftui/working-with-ui-controls)** 25 | * **[Handling User Input](https://developer.apple.com/tutorials/swiftui/handling-user-input)** 26 | * **[Building Lists and Navigation](https://developer.apple.com/tutorials/swiftui/building-lists-and-navigation)** 27 | * **[Drawing Paths and Shapes](https://developer.apple.com/tutorials/swiftui/drawing-paths-and-shapes)** 28 | * **[Animating Views and Transitions](https://developer.apple.com/tutorials/swiftui/animating-views-and-transitions)** 29 | * **[Composing Complex Interfaces](https://developer.apple.com/tutorials/swiftui/composing-complex-interfaces)** 30 | * **[Interfacing with UIKit](https://developer.apple.com/tutorials/swiftui/interfacing-with-uikit)** 31 | 32 | --------------------------------------------------------------------------------