├── .gitignore ├── LICENSE ├── Night Mode.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcuserdata │ └── michael.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── Night Mode ├── AppDelegate.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── first.imageset │ │ ├── Contents.json │ │ └── first.pdf │ └── second.imageset │ │ ├── Contents.json │ │ └── second.pdf ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Helpers │ ├── ArrayExtensions.swift │ ├── SubscribableValue.swift │ └── Weak.swift ├── Info.plist ├── Subclasses │ ├── AppNavigationController.swift │ └── AppTabBarController.swift ├── Theming │ ├── AppTheme.swift │ ├── AppThemeProvider.swift │ └── Theming.swift └── View Controllers │ ├── FirstViewController.swift │ └── SecondViewController.swift └── Preview.gif /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | # Package.pins 40 | .build/ 41 | 42 | # CocoaPods 43 | # 44 | # We recommend against adding the Pods directory to your .gitignore. However 45 | # you should judge for yourself, the pros and cons are mentioned at: 46 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 47 | # 48 | # Pods/ 49 | 50 | # Carthage 51 | # 52 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 53 | # Carthage/Checkouts 54 | 55 | Carthage/Build 56 | 57 | # fastlane 58 | # 59 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 60 | # screenshots whenever they are needed. 61 | # For more information about the recommended setup visit: 62 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 63 | 64 | fastlane/report.xml 65 | fastlane/Preview.html 66 | fastlane/screenshots 67 | fastlane/test_output 68 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Late Night Swift 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Night Mode.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 48; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 4C240D7320716814006E2A03 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C240D7220716814006E2A03 /* AppDelegate.swift */; }; 11 | 4C240D7520716814006E2A03 /* FirstViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C240D7420716814006E2A03 /* FirstViewController.swift */; }; 12 | 4C240D7720716814006E2A03 /* SecondViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C240D7620716814006E2A03 /* SecondViewController.swift */; }; 13 | 4C240D7A20716814006E2A03 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4C240D7820716814006E2A03 /* Main.storyboard */; }; 14 | 4C240D7C20716814006E2A03 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4C240D7B20716814006E2A03 /* Assets.xcassets */; }; 15 | 4C240D7F20716814006E2A03 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4C240D7D20716814006E2A03 /* LaunchScreen.storyboard */; }; 16 | 4C240D88207168BA006E2A03 /* Theming.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C240D87207168BA006E2A03 /* Theming.swift */; }; 17 | 4C240D8B20716A08006E2A03 /* SubscribableValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C240D8A20716A08006E2A03 /* SubscribableValue.swift */; }; 18 | 4C240D8D20716A45006E2A03 /* AppTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C240D8C20716A45006E2A03 /* AppTheme.swift */; }; 19 | 4C240D8F20716A63006E2A03 /* AppThemeProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C240D8E20716A63006E2A03 /* AppThemeProvider.swift */; }; 20 | 4C240D912071773B006E2A03 /* AppNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C240D902071773B006E2A03 /* AppNavigationController.swift */; }; 21 | 4C240D93207177C0006E2A03 /* AppTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C240D92207177C0006E2A03 /* AppTabBarController.swift */; }; 22 | 4C240D9520717944006E2A03 /* ArrayExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C240D9420717944006E2A03 /* ArrayExtensions.swift */; }; 23 | 4C240D9B20717FE4006E2A03 /* Weak.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C240D9A20717FE4006E2A03 /* Weak.swift */; }; 24 | /* End PBXBuildFile section */ 25 | 26 | /* Begin PBXFileReference section */ 27 | 4C240D6F20716814006E2A03 /* Night Mode.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Night Mode.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 28 | 4C240D7220716814006E2A03 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 29 | 4C240D7420716814006E2A03 /* FirstViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirstViewController.swift; sourceTree = ""; }; 30 | 4C240D7620716814006E2A03 /* SecondViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecondViewController.swift; sourceTree = ""; }; 31 | 4C240D7920716814006E2A03 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 32 | 4C240D7B20716814006E2A03 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 33 | 4C240D7E20716814006E2A03 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 34 | 4C240D8020716814006E2A03 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 35 | 4C240D87207168BA006E2A03 /* Theming.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theming.swift; sourceTree = ""; }; 36 | 4C240D8A20716A08006E2A03 /* SubscribableValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscribableValue.swift; sourceTree = ""; }; 37 | 4C240D8C20716A45006E2A03 /* AppTheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppTheme.swift; sourceTree = ""; }; 38 | 4C240D8E20716A63006E2A03 /* AppThemeProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppThemeProvider.swift; sourceTree = ""; }; 39 | 4C240D902071773B006E2A03 /* AppNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppNavigationController.swift; sourceTree = ""; }; 40 | 4C240D92207177C0006E2A03 /* AppTabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppTabBarController.swift; sourceTree = ""; }; 41 | 4C240D9420717944006E2A03 /* ArrayExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrayExtensions.swift; sourceTree = ""; }; 42 | 4C240D9A20717FE4006E2A03 /* Weak.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Weak.swift; sourceTree = ""; }; 43 | /* End PBXFileReference section */ 44 | 45 | /* Begin PBXFrameworksBuildPhase section */ 46 | 4C240D6C20716814006E2A03 /* Frameworks */ = { 47 | isa = PBXFrameworksBuildPhase; 48 | buildActionMask = 2147483647; 49 | files = ( 50 | ); 51 | runOnlyForDeploymentPostprocessing = 0; 52 | }; 53 | /* End PBXFrameworksBuildPhase section */ 54 | 55 | /* Begin PBXGroup section */ 56 | 4C240D6620716814006E2A03 = { 57 | isa = PBXGroup; 58 | children = ( 59 | 4C240D7120716814006E2A03 /* Night Mode */, 60 | 4C240D7020716814006E2A03 /* Products */, 61 | ); 62 | sourceTree = ""; 63 | }; 64 | 4C240D7020716814006E2A03 /* Products */ = { 65 | isa = PBXGroup; 66 | children = ( 67 | 4C240D6F20716814006E2A03 /* Night Mode.app */, 68 | ); 69 | name = Products; 70 | sourceTree = ""; 71 | }; 72 | 4C240D7120716814006E2A03 /* Night Mode */ = { 73 | isa = PBXGroup; 74 | children = ( 75 | 4C240D7220716814006E2A03 /* AppDelegate.swift */, 76 | 4C240D7820716814006E2A03 /* Main.storyboard */, 77 | 4C240D9620717BE6006E2A03 /* View Controllers */, 78 | 4C240D9920717C0C006E2A03 /* Subclasses */, 79 | 4C240D9820717C00006E2A03 /* Theming */, 80 | 4C240D9720717BF5006E2A03 /* Helpers */, 81 | 4C240D8020716814006E2A03 /* Info.plist */, 82 | 4C240D7B20716814006E2A03 /* Assets.xcassets */, 83 | 4C240D7D20716814006E2A03 /* LaunchScreen.storyboard */, 84 | ); 85 | path = "Night Mode"; 86 | sourceTree = ""; 87 | }; 88 | 4C240D9620717BE6006E2A03 /* View Controllers */ = { 89 | isa = PBXGroup; 90 | children = ( 91 | 4C240D7420716814006E2A03 /* FirstViewController.swift */, 92 | 4C240D7620716814006E2A03 /* SecondViewController.swift */, 93 | ); 94 | path = "View Controllers"; 95 | sourceTree = ""; 96 | }; 97 | 4C240D9720717BF5006E2A03 /* Helpers */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | 4C240D9420717944006E2A03 /* ArrayExtensions.swift */, 101 | 4C240D9A20717FE4006E2A03 /* Weak.swift */, 102 | 4C240D8A20716A08006E2A03 /* SubscribableValue.swift */, 103 | ); 104 | path = Helpers; 105 | sourceTree = ""; 106 | }; 107 | 4C240D9820717C00006E2A03 /* Theming */ = { 108 | isa = PBXGroup; 109 | children = ( 110 | 4C240D87207168BA006E2A03 /* Theming.swift */, 111 | 4C240D8C20716A45006E2A03 /* AppTheme.swift */, 112 | 4C240D8E20716A63006E2A03 /* AppThemeProvider.swift */, 113 | ); 114 | path = Theming; 115 | sourceTree = ""; 116 | }; 117 | 4C240D9920717C0C006E2A03 /* Subclasses */ = { 118 | isa = PBXGroup; 119 | children = ( 120 | 4C240D92207177C0006E2A03 /* AppTabBarController.swift */, 121 | 4C240D902071773B006E2A03 /* AppNavigationController.swift */, 122 | ); 123 | path = Subclasses; 124 | sourceTree = ""; 125 | }; 126 | /* End PBXGroup section */ 127 | 128 | /* Begin PBXNativeTarget section */ 129 | 4C240D6E20716814006E2A03 /* Night Mode */ = { 130 | isa = PBXNativeTarget; 131 | buildConfigurationList = 4C240D8320716814006E2A03 /* Build configuration list for PBXNativeTarget "Night Mode" */; 132 | buildPhases = ( 133 | 4C240D6B20716814006E2A03 /* Sources */, 134 | 4C240D6C20716814006E2A03 /* Frameworks */, 135 | 4C240D6D20716814006E2A03 /* Resources */, 136 | ); 137 | buildRules = ( 138 | ); 139 | dependencies = ( 140 | ); 141 | name = "Night Mode"; 142 | productName = "Night Mode"; 143 | productReference = 4C240D6F20716814006E2A03 /* Night Mode.app */; 144 | productType = "com.apple.product-type.application"; 145 | }; 146 | /* End PBXNativeTarget section */ 147 | 148 | /* Begin PBXProject section */ 149 | 4C240D6720716814006E2A03 /* Project object */ = { 150 | isa = PBXProject; 151 | attributes = { 152 | LastSwiftUpdateCheck = 0920; 153 | LastUpgradeCheck = 0920; 154 | ORGANIZATIONNAME = "Late Night Swift"; 155 | TargetAttributes = { 156 | 4C240D6E20716814006E2A03 = { 157 | CreatedOnToolsVersion = 9.2; 158 | ProvisioningStyle = Automatic; 159 | }; 160 | }; 161 | }; 162 | buildConfigurationList = 4C240D6A20716814006E2A03 /* Build configuration list for PBXProject "Night Mode" */; 163 | compatibilityVersion = "Xcode 8.0"; 164 | developmentRegion = en; 165 | hasScannedForEncodings = 0; 166 | knownRegions = ( 167 | en, 168 | Base, 169 | ); 170 | mainGroup = 4C240D6620716814006E2A03; 171 | productRefGroup = 4C240D7020716814006E2A03 /* Products */; 172 | projectDirPath = ""; 173 | projectRoot = ""; 174 | targets = ( 175 | 4C240D6E20716814006E2A03 /* Night Mode */, 176 | ); 177 | }; 178 | /* End PBXProject section */ 179 | 180 | /* Begin PBXResourcesBuildPhase section */ 181 | 4C240D6D20716814006E2A03 /* Resources */ = { 182 | isa = PBXResourcesBuildPhase; 183 | buildActionMask = 2147483647; 184 | files = ( 185 | 4C240D7F20716814006E2A03 /* LaunchScreen.storyboard in Resources */, 186 | 4C240D7C20716814006E2A03 /* Assets.xcassets in Resources */, 187 | 4C240D7A20716814006E2A03 /* Main.storyboard in Resources */, 188 | ); 189 | runOnlyForDeploymentPostprocessing = 0; 190 | }; 191 | /* End PBXResourcesBuildPhase section */ 192 | 193 | /* Begin PBXSourcesBuildPhase section */ 194 | 4C240D6B20716814006E2A03 /* Sources */ = { 195 | isa = PBXSourcesBuildPhase; 196 | buildActionMask = 2147483647; 197 | files = ( 198 | 4C240D8F20716A63006E2A03 /* AppThemeProvider.swift in Sources */, 199 | 4C240D9520717944006E2A03 /* ArrayExtensions.swift in Sources */, 200 | 4C240D7720716814006E2A03 /* SecondViewController.swift in Sources */, 201 | 4C240D88207168BA006E2A03 /* Theming.swift in Sources */, 202 | 4C240D7320716814006E2A03 /* AppDelegate.swift in Sources */, 203 | 4C240D9B20717FE4006E2A03 /* Weak.swift in Sources */, 204 | 4C240D8D20716A45006E2A03 /* AppTheme.swift in Sources */, 205 | 4C240D912071773B006E2A03 /* AppNavigationController.swift in Sources */, 206 | 4C240D93207177C0006E2A03 /* AppTabBarController.swift in Sources */, 207 | 4C240D8B20716A08006E2A03 /* SubscribableValue.swift in Sources */, 208 | 4C240D7520716814006E2A03 /* FirstViewController.swift in Sources */, 209 | ); 210 | runOnlyForDeploymentPostprocessing = 0; 211 | }; 212 | /* End PBXSourcesBuildPhase section */ 213 | 214 | /* Begin PBXVariantGroup section */ 215 | 4C240D7820716814006E2A03 /* Main.storyboard */ = { 216 | isa = PBXVariantGroup; 217 | children = ( 218 | 4C240D7920716814006E2A03 /* Base */, 219 | ); 220 | name = Main.storyboard; 221 | sourceTree = ""; 222 | }; 223 | 4C240D7D20716814006E2A03 /* LaunchScreen.storyboard */ = { 224 | isa = PBXVariantGroup; 225 | children = ( 226 | 4C240D7E20716814006E2A03 /* Base */, 227 | ); 228 | name = LaunchScreen.storyboard; 229 | sourceTree = ""; 230 | }; 231 | /* End PBXVariantGroup section */ 232 | 233 | /* Begin XCBuildConfiguration section */ 234 | 4C240D8120716814006E2A03 /* Debug */ = { 235 | isa = XCBuildConfiguration; 236 | buildSettings = { 237 | ALWAYS_SEARCH_USER_PATHS = NO; 238 | CLANG_ANALYZER_NONNULL = YES; 239 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 240 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 241 | CLANG_CXX_LIBRARY = "libc++"; 242 | CLANG_ENABLE_MODULES = YES; 243 | CLANG_ENABLE_OBJC_ARC = YES; 244 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 245 | CLANG_WARN_BOOL_CONVERSION = YES; 246 | CLANG_WARN_COMMA = YES; 247 | CLANG_WARN_CONSTANT_CONVERSION = YES; 248 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 249 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 250 | CLANG_WARN_EMPTY_BODY = YES; 251 | CLANG_WARN_ENUM_CONVERSION = YES; 252 | CLANG_WARN_INFINITE_RECURSION = YES; 253 | CLANG_WARN_INT_CONVERSION = YES; 254 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 255 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 256 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 257 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 258 | CLANG_WARN_STRICT_PROTOTYPES = YES; 259 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 260 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 261 | CLANG_WARN_UNREACHABLE_CODE = YES; 262 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 263 | CODE_SIGN_IDENTITY = "iPhone Developer"; 264 | COPY_PHASE_STRIP = NO; 265 | DEBUG_INFORMATION_FORMAT = dwarf; 266 | ENABLE_STRICT_OBJC_MSGSEND = YES; 267 | ENABLE_TESTABILITY = YES; 268 | GCC_C_LANGUAGE_STANDARD = gnu11; 269 | GCC_DYNAMIC_NO_PIC = NO; 270 | GCC_NO_COMMON_BLOCKS = YES; 271 | GCC_OPTIMIZATION_LEVEL = 0; 272 | GCC_PREPROCESSOR_DEFINITIONS = ( 273 | "DEBUG=1", 274 | "$(inherited)", 275 | ); 276 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 277 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 278 | GCC_WARN_UNDECLARED_SELECTOR = YES; 279 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 280 | GCC_WARN_UNUSED_FUNCTION = YES; 281 | GCC_WARN_UNUSED_VARIABLE = YES; 282 | IPHONEOS_DEPLOYMENT_TARGET = 11.2; 283 | MTL_ENABLE_DEBUG_INFO = YES; 284 | ONLY_ACTIVE_ARCH = YES; 285 | SDKROOT = iphoneos; 286 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 287 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 288 | }; 289 | name = Debug; 290 | }; 291 | 4C240D8220716814006E2A03 /* Release */ = { 292 | isa = XCBuildConfiguration; 293 | buildSettings = { 294 | ALWAYS_SEARCH_USER_PATHS = NO; 295 | CLANG_ANALYZER_NONNULL = YES; 296 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 297 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 298 | CLANG_CXX_LIBRARY = "libc++"; 299 | CLANG_ENABLE_MODULES = YES; 300 | CLANG_ENABLE_OBJC_ARC = YES; 301 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 302 | CLANG_WARN_BOOL_CONVERSION = YES; 303 | CLANG_WARN_COMMA = YES; 304 | CLANG_WARN_CONSTANT_CONVERSION = YES; 305 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 306 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 307 | CLANG_WARN_EMPTY_BODY = YES; 308 | CLANG_WARN_ENUM_CONVERSION = YES; 309 | CLANG_WARN_INFINITE_RECURSION = YES; 310 | CLANG_WARN_INT_CONVERSION = YES; 311 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 312 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 313 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 314 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 315 | CLANG_WARN_STRICT_PROTOTYPES = YES; 316 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 317 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 318 | CLANG_WARN_UNREACHABLE_CODE = YES; 319 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 320 | CODE_SIGN_IDENTITY = "iPhone Developer"; 321 | COPY_PHASE_STRIP = NO; 322 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 323 | ENABLE_NS_ASSERTIONS = NO; 324 | ENABLE_STRICT_OBJC_MSGSEND = YES; 325 | GCC_C_LANGUAGE_STANDARD = gnu11; 326 | GCC_NO_COMMON_BLOCKS = YES; 327 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 328 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 329 | GCC_WARN_UNDECLARED_SELECTOR = YES; 330 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 331 | GCC_WARN_UNUSED_FUNCTION = YES; 332 | GCC_WARN_UNUSED_VARIABLE = YES; 333 | IPHONEOS_DEPLOYMENT_TARGET = 11.2; 334 | MTL_ENABLE_DEBUG_INFO = NO; 335 | SDKROOT = iphoneos; 336 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 337 | VALIDATE_PRODUCT = YES; 338 | }; 339 | name = Release; 340 | }; 341 | 4C240D8420716814006E2A03 /* Debug */ = { 342 | isa = XCBuildConfiguration; 343 | buildSettings = { 344 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 345 | CODE_SIGN_STYLE = Automatic; 346 | INFOPLIST_FILE = "Night Mode/Info.plist"; 347 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 348 | PRODUCT_BUNDLE_IDENTIFIER = "com.latenightswift.Night-Mode"; 349 | PRODUCT_NAME = "$(TARGET_NAME)"; 350 | SWIFT_VERSION = 4.0; 351 | TARGETED_DEVICE_FAMILY = "1,2"; 352 | }; 353 | name = Debug; 354 | }; 355 | 4C240D8520716814006E2A03 /* Release */ = { 356 | isa = XCBuildConfiguration; 357 | buildSettings = { 358 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 359 | CODE_SIGN_STYLE = Automatic; 360 | INFOPLIST_FILE = "Night Mode/Info.plist"; 361 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 362 | PRODUCT_BUNDLE_IDENTIFIER = "com.latenightswift.Night-Mode"; 363 | PRODUCT_NAME = "$(TARGET_NAME)"; 364 | SWIFT_VERSION = 4.0; 365 | TARGETED_DEVICE_FAMILY = "1,2"; 366 | }; 367 | name = Release; 368 | }; 369 | /* End XCBuildConfiguration section */ 370 | 371 | /* Begin XCConfigurationList section */ 372 | 4C240D6A20716814006E2A03 /* Build configuration list for PBXProject "Night Mode" */ = { 373 | isa = XCConfigurationList; 374 | buildConfigurations = ( 375 | 4C240D8120716814006E2A03 /* Debug */, 376 | 4C240D8220716814006E2A03 /* Release */, 377 | ); 378 | defaultConfigurationIsVisible = 0; 379 | defaultConfigurationName = Release; 380 | }; 381 | 4C240D8320716814006E2A03 /* Build configuration list for PBXNativeTarget "Night Mode" */ = { 382 | isa = XCConfigurationList; 383 | buildConfigurations = ( 384 | 4C240D8420716814006E2A03 /* Debug */, 385 | 4C240D8520716814006E2A03 /* Release */, 386 | ); 387 | defaultConfigurationIsVisible = 0; 388 | defaultConfigurationName = Release; 389 | }; 390 | /* End XCConfigurationList section */ 391 | }; 392 | rootObject = 4C240D6720716814006E2A03 /* Project object */; 393 | } 394 | -------------------------------------------------------------------------------- /Night Mode.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Night Mode.xcodeproj/xcuserdata/michael.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Night Mode.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Night Mode/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Night Mode 4 | // 5 | // Created by Michael on 01/04/2018. 6 | // Copyright © 2018 Late Night Swift. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | var window: UIWindow? 14 | 15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 16 | return true 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Night Mode/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 | } -------------------------------------------------------------------------------- /Night Mode/Assets.xcassets/first.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "first.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Night Mode/Assets.xcassets/first.imageset/first.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/latenightswift/night-mode/a258032896725b1e3efe58f9b6945e2b746f9f74/Night Mode/Assets.xcassets/first.imageset/first.pdf -------------------------------------------------------------------------------- /Night Mode/Assets.xcassets/second.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "second.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Night Mode/Assets.xcassets/second.imageset/second.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/latenightswift/night-mode/a258032896725b1e3efe58f9b6945e2b746f9f74/Night Mode/Assets.xcassets/second.imageset/second.pdf -------------------------------------------------------------------------------- /Night Mode/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 | -------------------------------------------------------------------------------- /Night Mode/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 27 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /Night Mode/Helpers/ArrayExtensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ArrayExtensions.swift 3 | // Night Mode 4 | // 5 | // Created by Michael on 01/04/2018. 6 | // Copyright © 2018 Late Night Swift. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension Array { 12 | /// Move the last element of the array to the beginning 13 | /// - Returns: The element that was moved 14 | mutating func rotate() -> Element? { 15 | guard let lastElement = popLast() else { 16 | return nil 17 | } 18 | insert(lastElement, at: 0) 19 | return lastElement 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Night Mode/Helpers/SubscribableValue.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SubscribableValue.swift 3 | // Night Mode 4 | // 5 | // Created by Michael on 01/04/2018. 6 | // Copyright © 2018 Late Night Swift. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Stores a value of type T, and allows objects to subscribe to 12 | /// be notified with this value is changed. 13 | struct SubscribableValue { 14 | private typealias Subscription = (object: Weak, handler: (T) -> Void) 15 | 16 | private var subscriptions: [Subscription] = [] 17 | 18 | var value: T { 19 | didSet { 20 | for (object, handler) in subscriptions where object.value != nil { 21 | handler(value) 22 | } 23 | } 24 | } 25 | 26 | init(value: T) { 27 | self.value = value 28 | } 29 | 30 | mutating func subscribe(_ object: AnyObject, using handler: @escaping (T) -> Void) { 31 | subscriptions.append((Weak(value: object), handler)) 32 | cleanupSubscriptions() 33 | } 34 | 35 | /// Removes any subscriptions where the object has been deallocated 36 | /// and no longer exists 37 | private mutating func cleanupSubscriptions() { 38 | subscriptions = subscriptions.filter({ entry in 39 | return entry.object.value != nil 40 | }) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Night Mode/Helpers/Weak.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Weak.swift 3 | // Night Mode 4 | // 5 | // Created by Michael on 01/04/2018. 6 | // Copyright © 2018 Late Night Swift. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// A box that allows us to weakly hold on to an object 12 | struct Weak { 13 | weak var value: Object? 14 | } 15 | -------------------------------------------------------------------------------- /Night Mode/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 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UIStatusBarTintParameters 32 | 33 | UINavigationBar 34 | 35 | Style 36 | UIBarStyleDefault 37 | Translucent 38 | 39 | 40 | 41 | UISupportedInterfaceOrientations 42 | 43 | UIInterfaceOrientationPortrait 44 | UIInterfaceOrientationLandscapeLeft 45 | UIInterfaceOrientationLandscapeRight 46 | 47 | UISupportedInterfaceOrientations~ipad 48 | 49 | UIInterfaceOrientationPortrait 50 | UIInterfaceOrientationPortraitUpsideDown 51 | UIInterfaceOrientationLandscapeLeft 52 | UIInterfaceOrientationLandscapeRight 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /Night Mode/Subclasses/AppNavigationController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppNavigationController.swift 3 | // Night Mode 4 | // 5 | // Created by Michael on 01/04/2018. 6 | // Copyright © 2018 Late Night Swift. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AppNavigationController: UINavigationController { 12 | private var themedStatusBarStyle: UIStatusBarStyle? 13 | 14 | override var preferredStatusBarStyle: UIStatusBarStyle { 15 | return themedStatusBarStyle ?? super.preferredStatusBarStyle 16 | } 17 | 18 | override func viewDidLoad() { 19 | super.viewDidLoad() 20 | setUpTheming() 21 | } 22 | } 23 | 24 | extension AppNavigationController: Themed { 25 | func applyTheme(_ theme: AppTheme) { 26 | themedStatusBarStyle = theme.statusBarStyle 27 | setNeedsStatusBarAppearanceUpdate() 28 | 29 | navigationBar.barTintColor = theme.barBackgroundColor 30 | navigationBar.tintColor = theme.barForegroundColor 31 | navigationBar.titleTextAttributes = [ 32 | NSAttributedStringKey.foregroundColor: theme.barForegroundColor 33 | ] 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Night Mode/Subclasses/AppTabBarController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppTabBarController.swift 3 | // Night Mode 4 | // 5 | // Created by Michael on 01/04/2018. 6 | // Copyright © 2018 Late Night Swift. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AppTabBarController: UITabBarController { 12 | override func viewDidLoad() { 13 | super.viewDidLoad() 14 | setUpTheming() 15 | } 16 | } 17 | 18 | extension AppTabBarController: Themed { 19 | func applyTheme(_ theme: AppTheme) { 20 | tabBar.barTintColor = theme.barBackgroundColor 21 | tabBar.tintColor = theme.barForegroundColor 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Night Mode/Theming/AppTheme.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppTheme.swift 3 | // Night Mode 4 | // 5 | // Created by Michael on 01/04/2018. 6 | // Copyright © 2018 Late Night Swift. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | struct AppTheme { 12 | var statusBarStyle: UIStatusBarStyle 13 | var barBackgroundColor: UIColor 14 | var barForegroundColor: UIColor 15 | var backgroundColor: UIColor 16 | var textColor: UIColor 17 | } 18 | 19 | extension AppTheme { 20 | static let light = AppTheme( 21 | statusBarStyle: .`default`, 22 | barBackgroundColor: .white, 23 | barForegroundColor: .black, 24 | backgroundColor: UIColor(white: 0.9, alpha: 1), 25 | textColor: .darkText 26 | ) 27 | 28 | static let dark = AppTheme( 29 | statusBarStyle: .lightContent, 30 | barBackgroundColor: UIColor(white: 0, alpha: 1), 31 | barForegroundColor: .white, 32 | backgroundColor: UIColor(white: 0.2, alpha: 1), 33 | textColor: .lightText 34 | ) 35 | } 36 | -------------------------------------------------------------------------------- /Night Mode/Theming/AppThemeProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppThemeProvider.swift 3 | // Night Mode 4 | // 5 | // Created by Michael on 01/04/2018. 6 | // Copyright © 2018 Late Night Swift. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | final class AppThemeProvider: ThemeProvider { 12 | static let shared: AppThemeProvider = .init() 13 | 14 | private var theme: SubscribableValue 15 | private var availableThemes: [AppTheme] = [.light, .dark] 16 | 17 | var currentTheme: AppTheme { 18 | get { 19 | return theme.value 20 | } 21 | set { 22 | setNewTheme(newValue) 23 | } 24 | } 25 | 26 | init() { 27 | theme = SubscribableValue(value: .light) 28 | } 29 | 30 | private func setNewTheme(_ newTheme: AppTheme) { 31 | let window = UIApplication.shared.delegate!.window!! 32 | UIView.transition( 33 | with: window, 34 | duration: 0.3, 35 | options: [.transitionCrossDissolve], 36 | animations: { 37 | self.theme.value = newTheme 38 | }, 39 | completion: nil 40 | ) 41 | } 42 | 43 | func subscribeToChanges(_ object: AnyObject, handler: @escaping (AppTheme) -> Void) { 44 | theme.subscribe(object, using: handler) 45 | } 46 | 47 | func nextTheme() { 48 | guard let nextTheme = availableThemes.rotate() else { 49 | return 50 | } 51 | currentTheme = nextTheme 52 | } 53 | } 54 | 55 | extension Themed where Self: AnyObject { 56 | var themeProvider: AppThemeProvider { 57 | return AppThemeProvider.shared 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Night Mode/Theming/Theming.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NightMode.swift 3 | // Night Mode 4 | // 5 | // Created by Michael on 01/04/2018. 6 | // Copyright © 2018 Late Night Swift. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Describes a type that holds a current `Theme` and allows 12 | /// an object to be notified when the theme is changed. 13 | protocol ThemeProvider { 14 | /// Placeholder for the theme type that the app will use 15 | associatedtype Theme 16 | 17 | /// The current theme that is active 18 | var currentTheme: Theme { get } 19 | 20 | /// Subscribe to be notified when the theme changes. Handler will be 21 | /// remove from subscription when `object` is deallocated. 22 | func subscribeToChanges(_ object: AnyObject, handler: @escaping (Theme) -> Void) 23 | } 24 | 25 | /// Describes a type that can have a theme applied to it 26 | protocol Themed { 27 | /// A Themed type needs to know about what concrete type the 28 | /// ThemeProvider is. So we don't clash with the protocol, 29 | /// let's call this associated type _ThemeProvider 30 | associatedtype _ThemeProvider: ThemeProvider 31 | 32 | /// Return the current app-wide theme provider 33 | var themeProvider: _ThemeProvider { get } 34 | 35 | /// This will be called whenever the current theme changes 36 | func applyTheme(_ theme: _ThemeProvider.Theme) 37 | } 38 | 39 | extension Themed where Self: AnyObject { 40 | /// This is to be called once when Self wants to start listening for 41 | /// theme changes. This immediately triggers `applyTheme()` with the 42 | /// current theme. 43 | func setUpTheming() { 44 | applyTheme(themeProvider.currentTheme) 45 | themeProvider.subscribeToChanges(self) { [weak self] newTheme in 46 | self?.applyTheme(newTheme) 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Night Mode/View Controllers/FirstViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FirstViewController.swift 3 | // Night Mode 4 | // 5 | // Created by Michael on 01/04/2018. 6 | // Copyright © 2018 Late Night Swift. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class FirstViewController: UIViewController { 12 | @IBOutlet private var titleLabel: UILabel! 13 | @IBOutlet private var subtitleLabel: UILabel! 14 | 15 | override func viewDidLoad() { 16 | super.viewDidLoad() 17 | setUpTheming() 18 | } 19 | 20 | @IBAction func toggleThemeActionTriggered() { 21 | themeProvider.nextTheme() 22 | } 23 | } 24 | 25 | extension FirstViewController: Themed { 26 | func applyTheme(_ theme: AppTheme) { 27 | view.backgroundColor = theme.backgroundColor 28 | titleLabel.textColor = theme.textColor 29 | subtitleLabel.textColor = theme.textColor 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Night Mode/View Controllers/SecondViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SecondViewController.swift 3 | // Night Mode 4 | // 5 | // Created by Michael on 01/04/2018. 6 | // Copyright © 2018 Late Night Swift. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SecondViewController: UIViewController { 12 | @IBOutlet private var titleLabel: UILabel! 13 | @IBOutlet private var subtitleLabel: UILabel! 14 | 15 | override func viewDidLoad() { 16 | super.viewDidLoad() 17 | setUpTheming() 18 | } 19 | 20 | @IBAction func toggleThemeActionTriggered() { 21 | themeProvider.nextTheme() 22 | } 23 | } 24 | 25 | extension SecondViewController: Themed { 26 | func applyTheme(_ theme: AppTheme) { 27 | view.backgroundColor = theme.backgroundColor 28 | titleLabel.textColor = theme.textColor 29 | subtitleLabel.textColor = theme.textColor 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Preview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/latenightswift/night-mode/a258032896725b1e3efe58f9b6945e2b746f9f74/Preview.gif --------------------------------------------------------------------------------